<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://www.pragmaticcoding.ca/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.pragmaticcoding.ca/" rel="alternate" type="text/html" /><updated>2026-03-11T07:40:35+00:00</updated><id>https://www.pragmaticcoding.ca/feed.xml</id><title type="html">PragmaticCoding</title><subtitle>PragmaticCoding is all about ... pragmatic coding!  Writing code that gets the job done and is easy to read, understand, maintain and enhance.</subtitle><author><name>Dave Barrett</name><email>pragmaticcoding8@gmail.com</email></author><entry><title type="html">VLAN’s and Subnets For Home Networks</title><link href="https://www.pragmaticcoding.ca/homelab/vlans" rel="alternate" type="text/html" title="VLAN’s and Subnets For Home Networks" /><published>2026-02-06T17:00:00+00:00</published><updated>2026-02-06T17:00:00+00:00</updated><id>https://www.pragmaticcoding.ca/homelab/VLANs</id><content type="html" xml:base="https://www.pragmaticcoding.ca/homelab/vlans"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>Back in 2003 I was doing some work in the company’s accounting department when all of the PC’s in the area flicked off and rebooted again.  It was so fast and simultaneous that I thought that we’d had some kind of electrical fault that hit that part of the office, and I didn’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.</p>

<p>Sometime later, our Internet connection was down.  Or so I thought.</p>

<p>I 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.</p>

<p>Back 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.</p>

<p>The 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’d expect that light to flicker once or twice a minute.  Any more, and you likely had a problem brewing.</p>

<p>Well, I went into the computer room and that collision light was solid.</p>

<p>Not flickering, just on.</p>

<p>There 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, <strong>we hosted our own web sites on premises, and this meant that we had essentially been kicked off the web</strong>.</p>

<p>Our 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.</p>

<p>It turns out that we had been hit with a worm.  Probably the “Welchia”, or the “Blaster” 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.</p>

<p>It 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.</p>

<p>We lost of few days of our lives to investigating this, working around it, and cleaning and patching the workstations.</p>

<p>At 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 - <strong>behind our firewall</strong>.</p>

<p>It was like the babysitter late at night in the horror movie.  The phone call is coming from the basement - inside the house!</p>

<p>We had no defence against this.</p>

<h2 id="smart-home-devices">Smart Home Devices</h2>

<p>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 “smart” device.  A fridge.  A robot vaccuum. Even a smart plug.</p>

<p>And when we get it home, we take it out of the box, connect to it from our phones and give it our wifi password.</p>

<p>Now it’s in our networks.  Behind the firewall…inside the house!</p>

<p>Yeah.  But what can a smart plug do?</p>

<p>Indeed.  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’s done, the hackers half-way around the world can start poking around inside your entire home network.  And you would probably never know.</p>

<p>I’m seeing articles on-line that some refrigerators with display screens on them are starting to show ads.  What if you don’t want ads on your fridge?  What if I don’t want my printer reporting back to HP about how many pages I print each month? And let’s not talk about “Smart” TVs.</p>

<p>I 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.</p>

<p>What do I know about any of them?</p>

<p>Honestly, not much.  So why trust them?</p>

<h2 id="zero-trust">Zero Trust</h2>

<p>When it comes to this stuff, “trust” is a very misguided concept.</p>

<p>Do you need to trust these things?  The answer is, “No!”</p>

<p>What you need is a “Zero Trust” network design behind your firewall that lets you sequester these devices such that they can do no harm.  Then you don’t care if they are malicious, because they’ve been neutralized.</p>

<p>Let’s look at how to do that…</p>

<h1 id="tcpip-subnets-and-routing">TCP/IP Subnets and Routing</h1>

<p>Before we can go any further, we’ll need to have a basic understanding about how devices find each other and communicate over TCP/IP.</p>

<p>If you’ve ever manually set up a device on a network without DHCP, you’ll know that you need to provide four pieces of information:</p>

<dl>
  <dt>The IP Address</dt>
  <dd>
    <p>This is the literally the address of the computer on the network.  It’s going to be in the format of <code class="language-plaintext highlighter-rouge">###.###.###.###</code> where each <code class="language-plaintext highlighter-rouge">###</code> is a number between 1-255 (ie. one byte). An IP address is always 4 bytes long.</p>
  </dd>
  <dt>The NetMask</dt>
  <dd>
    <p>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’re going to use a netmask of <code class="language-plaintext highlighter-rouge">255.255.255.0</code> or <code class="language-plaintext highlighter-rouge">/24</code> 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 bytes represents the device’s unique place in that subnet.</p>
  </dd>
  <dt>The Default Gateway</dt>
  <dd>
    <p>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.</p>
  </dd>
  <dt>The DNS Server Addresses</dt>
  <dd>
    <p>This is where to send requests for DNS lookups.  It’s not relevant to this discussion.</p>
  </dd>
</dl>

<h2 id="how-devices-determine-routing">How Devices Determine Routing</h2>

<p>For purposes of this discussion, it’s probably best if you consider TCP/IP to be a “broadcast” 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’t see that.</p>

<p>Each 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.</p>

<p>If 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’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.</p>

<p>Here’s an example of what that would look like:</p>

<p><img src="/assets/homelab/SubnetAndGateway.png" alt="Subnet Diagram" /></p>

<p>In this diagram, the top two PC’s belong to the <code class="language-plaintext highlighter-rouge">192.168.1.0</code> network, which is addresses <code class="language-plaintext highlighter-rouge">192.168.1.0</code> to <code class="language-plaintext highlighter-rouge">192.168.1.255</code>. This is because they have the netmask <code class="language-plaintext highlighter-rouge">255.255.255.0</code> which you can see from the CIDR notation <code class="language-plaintext highlighter-rouge">/24</code> in the addresses.  These two computers can talk to each other directly.</p>

<p>If they want to talk to <code class="language-plaintext highlighter-rouge">Server1</code>, with address <code class="language-plaintext highlighter-rouge">10.196.212.116</code>, 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 <code class="language-plaintext highlighter-rouge">2811 Router</code> with the address <code class="language-plaintext highlighter-rouge">192.168.1.1</code>.  That router will then send it on to <code class="language-plaintext highlighter-rouge">10.196.212.116</code> through its second network interface, which has the address <code class="language-plaintext highlighter-rouge">10.196.212.1</code>.</p>

<p>What’s very important to understand here is that all three PC’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.</p>

<p>This is because the physical topology of the network is irrelevant to the TCP/IP routing.</p>

<h2 id="routing-with-firewalls">Routing With Firewalls</h2>

<p>For this discussion, your really big takeaway should be this…</p>

<p>The router in the diagram with the name <code class="language-plaintext highlighter-rouge">2811 Router</code> <em>could</em> be a firewall.  As a firewall, it would have rules that control if and how devices communicate between subnets.</p>

<p>As a matter of fact, firewalls can <strong>only</strong> 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 <code class="language-plaintext highlighter-rouge">2811 Router</code> could control traffic between <code class="language-plaintext highlighter-rouge">PC1</code> and <code class="language-plaintext highlighter-rouge">PC2</code> because they can communicate directly without involving the router/firewall.  On the other hand, rules in the firewall at <code class="language-plaintext highlighter-rouge">2811 Router</code> will control communication between <code class="language-plaintext highlighter-rouge">Server 1</code> and either of <code class="language-plaintext highlighter-rouge">PC1</code> or <code class="language-plaintext highlighter-rouge">PC2</code>.</p>

<p>From this, you can deduce that the IP subnet is the basic building block of home network security.</p>

<h1 id="introducing-vlans">Introducing VLAN’s</h1>

<p>If you do some web searches about VLAN’s, you’ll find something like this introduction from Wikipedia:</p>

<blockquote>
  <p>A 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.</p>

  <p>VLANs 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.</p>
</blockquote>

<p>Essentially this is describing something that looks like this:</p>

<p><img src="/assets/homelab/VLAN1.png" alt="VLAN Diagram" /></p>

<p>You 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.</p>

<p>You can also split a VLAN across switches, like this:</p>

<p><img src="/assets/homelab/VLAN2.webp" alt="VLAN With 2 Switches" /></p>

<p>This 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.</p>

<p>And that’s about all you’ll get before you’re really deep, deep into the weeds.  You’ll quickly find out about trunks and IEEE 802.1Q and lots of other complicated stuff.</p>

<p>This seems like a cool idea…until you start wondering about how something on one VLAN can communicate with something on different VLAN.</p>

<p>After all, if you can’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.</p>

<h2 id="managed-switches">Managed Switches</h2>

<p>Before we get into the details, we need to talk about one more thing…</p>

<p>Your standard, consumer grade network switch that you can buy for about $10 on any street corner probably won’t be able to handle VLAN’s. You’ll need something called a “Managed Switch”, which is generally marketted at corporate customers.  They’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’s not worth the hassle.</p>

<p>A managed switch will generally allow you to do a whole bunch of things in addition to VLAN’s, things like “spanning trees” and trunking.  We’re not going to talk about that stuff here, though.  Just VLAN’s.</p>

<h2 id="kinds-of-port-membership-in-vlans">Kinds of Port Membership in VLAN’s</h2>

<p>If you read the Wikipedia snippet from above, you’ll have noticed that VLAN’s work by attaching “tags” (whatever they are) to network packets before they are broadcast around the network.  The “tag” is just an numeric ID - generally between 2 and 1000 - and switch ports that understand VLAN’s will be programmed to only transmit and receive packets with particular tags.</p>

<p>Tag <code class="language-plaintext highlighter-rouge">1</code> is special.  It’s the default “no VLAN” tag, and get’s slapped on any untagged packet that travels around VLAN aware network equipment that hasn’t been configured to do anything different.  You cannot use tag <code class="language-plaintext highlighter-rouge">1</code> for anything else.</p>

<p>From my experience, when you configure a managed switch to use VLAN’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’s look at how this works…</p>

<p>Ports on managed switches can handle VLAN’s in two ways:</p>

<dl>
  <dt>Untagged Packets</dt>
  <dd>
    <p>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’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 <code class="language-plaintext highlighter-rouge">1</code>.</p>
  </dd>
  <dt>Tagged Packets</dt>
  <dd>
    <p>This is a mode where the switch port is assigned membership of one <strong>or more</strong> VLAN’s and it will allow incoming packets tagged with those VLAN id’s to pass through.  Packets tagged with any other VLAN’s will be blocked.  This means that if you plug a VLAN aware device into the port, that device can decide which VLAN’s it wants to communicate on, assuming the port is a member of those VLAN’s.</p>
  </dd>
</dl>

<h3 id="configuring-the-switch">Configuring the Switch</h3>

<p>Obviously, the interface for configuring each brand of switch is going to be different.  Generally, though, you’ll be looking for a section with a name like “802.1Q VLAN”.</p>

<p>In my D-Link switch the interface looks like this:</p>

<p><img src="/assets/homelab/VLAN5.png" alt="D-Link VLAN" /></p>

<p>If you click on one of the VLAN number links, you get a screen like this:</p>

<p><img src="/assets/homelab/VLAN6.png" alt="D-Link VLAN" /></p>

<p>The method with this switch is that you define each VLAN, give it a name and then define how each port interacts with the VLAN.</p>

<p>In this switch, the default VLAN <code class="language-plaintext highlighter-rouge">1</code> 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’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.</p>

<p>I should point out that this is <strong>not</strong> the final configuration that I intend for this switch.  I’m still in the process of sorting out all of my network security and I’m leaving this wide open while I configure everything else and test it.  I don’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 “VLAN unaware” wired devices on my network.  Things like my XBox and my IPTV box.  Eventually, I’ll be reconfiguring this switch such that those devices go into the IOT VLAN (I think).</p>

<h2 id="understanding-tagged-vlan-membership">Understanding Tagged VLAN Membership</h2>

<p>You might find it a little hard to picture how this second case is used at first.</p>

<p>However, if you are using Proxmox for virtualization, you’ll have seen this screen for configuring the networking on a Proxmox host:</p>

<p><img src="/assets/homelab/VLAN3.png" alt="Screen Snap" /></p>

<p>Here the Linux Bridge <code class="language-plaintext highlighter-rouge">vmbr0</code>, which defines how the VM’s on the node will connect to the ethernet adapter, is defined.  Note the checkbox beside “VLAN aware:”, and the range of “2-4094” that is assigned to “VLAN IDs:”.</p>

<p>Then, when you define the networking for a VM on that Proxmox host, you’ll see this screen:</p>

<p><img src="/assets/homelab/VLAN4.png" alt="Screen Snap" /></p>

<p>In this screen <code class="language-plaintext highlighter-rouge">eth0</code> is being defined as connecting to <code class="language-plaintext highlighter-rouge">vmbr0</code> and having VLAN tag <code class="language-plaintext highlighter-rouge">20</code> attached.</p>

<p>What’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 <code class="language-plaintext highlighter-rouge">30</code>, then it won’t be able to communicate directly with the first VM.  Even though they are sharing the exact same hardware.</p>

<p>However, these VM’s will <strong>not</strong> be able to communicate with anything unless the port on the switch that the host is plugged into is a member of both VLAN’s <code class="language-plaintext highlighter-rouge">20</code> and <code class="language-plaintext highlighter-rouge">30</code> as “tagged”.</p>

<h2 id="firewalls-and-routers">Firewalls and Routers</h2>

<p>The second, and probably most important place you’ll encounter a “VLAN aware” device is a firewall or a router.  For sure, both OPNsense and pfSense are very VLAN aware.</p>

<p>Understand that a firewall is always, by definition, a router, and that a router is designed to transfer traffic between ports.  Essentially, firewalls use VLAN’s to define multiple network interfaces on the same hardware port.  Each VLAN becomes a “device” which can have its own ethernet address.</p>

<p>In 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.</p>

<p>This is what enables you to use VLAN’s to control security inside your network.  Once you’ve done this, you’ve created the authoritative connection between a VLAN and a TCP/IP subnet.</p>

<p>Let’s say that you have that VLAN <code class="language-plaintext highlighter-rouge">20</code> from the screenshot above.  On your firewall, you define a network interface called <code class="language-plaintext highlighter-rouge">VLAN_DMZ</code> and assign it to your inside ethernet port using VLAN <code class="language-plaintext highlighter-rouge">20</code>.  You’re going to use <code class="language-plaintext highlighter-rouge">VLAN_DMZ</code> as the gateway for the network <code class="language-plaintext highlighter-rouge">10.10.10.0/24</code>, so you give it the address <code class="language-plaintext highlighter-rouge">10.10.10.1</code>.</p>

<p>Now, if any other device inside your network is on VLAN <code class="language-plaintext highlighter-rouge">20</code>, and if it wants to talk to anything outside VLAN <code class="language-plaintext highlighter-rouge">20</code> it is going to have to be on <code class="language-plaintext highlighter-rouge">10.10.10.0/24</code>.  Otherwise it cannot communicate with the gateway at <code class="language-plaintext highlighter-rouge">10.10.10.1</code>.</p>

<p>You can see from this that the firewall configuration “locks in” VLAN <code class="language-plaintext highlighter-rouge">20</code> to the <code class="language-plaintext highlighter-rouge">10.10.10.0/24</code> subnet, and the firewall rules will define how it can behave as a “DMZ”.</p>

<p>So far, none of this particularly uses the isolation qualities of VLAN’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.</p>

<p>In fact, you could just ignore the security aspects of the separation qualities of VLAN’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’t sacrifice too much actual security.  I’m not suggesting that you do this, but it wouldn’t be the end of the world if you did.</p>

<h2 id="dhcp">DHCP</h2>

<p>The one place where it really is useful to have separation between the VLAN’s is the one commonly used TCP/IP service which is truly treated as a broadcast - DHCP.</p>

<p>When a device is attached to the network that has been configured to use DHCP to get its network information it doesn’t initially have an IP address or even any knowledge about the network that it’s plugging into.  How does it communicate with the DHCP server on the network without an IP address?</p>

<p>The answer is that it uses pure local broadcast on the IP network.  Essentially, all of the communication is performed by using the address <code class="language-plaintext highlighter-rouge">255.255.255.255</code> which will be seen by every host on the local network.  Most importantly, packets addressed to <code class="language-plaintext highlighter-rouge">255.255.255.255</code> will never pass through a router - they are strictly local.</p>

<p>First, the device broadcasts a “discover” message to the entire network that essentially says, “I’m here! Can anybody give me an address?”  Every DHCP server that sees that broadcast will respond by broadcasting an “offer” of an IP address.  The new device then picks one of the offers, and “requests” that address be assigned to it (once again by broadcast).  The DHCP server that made the offer then broadcasts an “acknowledgement” of the assignment.</p>

<p>This process is known as “DORA”, which means Discover-Offer-Request-Acknowledge.</p>

<p>The key point for us is the part that says, “Every DHCP server that sees that broadcast…”.  Now go back and look at the first VLAN picture in this article.  See how it says, “Broadcast message in”, and “Broadcast message out”? And see how the VLAN’s prevent the broadcasts from crossing between them?</p>

<p>What 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 <code class="language-plaintext highlighter-rouge">VLAN_DMZ</code>, which listens and broadcasts on VLAN 20.</p>

<p>Once again OPNSense works great here.  Using “Dnsmasq DNS and DHCP” - which is the DHCP service that you should be using now - you don’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’ve configured, and provide an address, mask, gateway and DNS server tailored for that VLAN.</p>

<p>This is the piece that puts it all together.</p>

<p>Once you’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’s plugged into - and it will be attached to a particular subnet that you have defined for that VLAN.</p>

<h2 id="wifi">WiFi</h2>

<p>But what about all those devices that connect via WiFi?  They’re generally not VLAN aware, and they don’t plug into a switch port that you can configure for untagged packets.</p>

<p>The 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 “Guest” SSID and associate it with a VLAN.</p>

<p>I’ve been using D-Link DBR-X3000-AP access points, and they have the ability to set up 6 “Guest” zones, each with an SSID and VLAN tag:</p>

<p><img src="/assets/homelab/GuestZone.png" alt="WiFi VLAN" /></p>

<p>Now, when I’m setting up a new smart plug or other device, I just configure it to connect to my “IOT” SSID, and to use DHCP.  Everything else just works.  You’ll also have to make sure that whatever switch port you plug the AP into is configured to recognize the VLAN’s that you use for all of the SSID’s that you create.</p>

<h1 id="the-default-setup-with-isp-equipment">The Default Setup with ISP Equipment</h1>

<p>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’re better off if it is separate.</p>

<p>Let’s look at what your network looks like with this equipment:</p>

<p><img src="/assets/homelab/ISPRouter.png" alt="Home Network" /></p>

<p>The problem is twofold:</p>

<ol>
  <li>The WiFi router is integrated with the internet router.</li>
  <li>The consumer grade, plug and play, equipment cannot be configured adequately.</li>
</ol>

<p>These two items mean that the equipment is great for people who don’t have the technical knowledge to do much more that plug the bits together, and who can’t figure out how to even change the WiFi password.  And that’s probably the vast, vast majority of customers that the ISP’s are dealing with.</p>

<p>But if you are building your own, self-hosted, services, you’ll need something different.  For instance, with a standard, consumer, WiFi router, you’ll probably have a single “guest” SSID avaliable, and it might even allow you to associate it with a VLAN.  But if you really want to use VLAN’s and subnets properly, you’ll need a few more SSID’s.  Also, if you are implementing your own firewall, you’ll probably need to turn off all of the routing in your WiFi router, along with things like NAT and DHCP.  You’ll probably need to put it into “Bridge” or “AP” mode if that’s possible.</p>

<p>The WiFi router that my latest IP sent me is so simple it doesn’t even have a “Bridge” mode.  I didn’t even bother with it so it’s just stayed in the box in a closet.</p>

<p>Generally speaking, you’ll want to replace your WiFi router with a WiFi “Access Point” or “AP”.  As soon as you go looking for these, you’ll discover that you’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.</p>

<h1 id="setting-up-opnsense-to-use-vlans">Setting up OPNSense to Use VLAN’s</h1>

<p>Let’s take a quick survey of how you would set up OPNSense to enable VLAN’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’t talk about that.  This isn’t a step-by-step guide, but really just an overview of the things that you need to achieve in order to implement VLAN’s and subnets.  If you want step-by-step instructions, you can find tons of them on YouTube.</p>

<h2 id="creating-vlan-interfaces">Creating VLAN Interfaces</h2>

<p>When you start out, you’ll have an “Interface” configured for the physical internal ethernet port.  This is generally called “LAN”.  You’ll neet to create an Interface for each VLAN that you are going to set up.</p>

<h3 id="creating-the-vlan-device">Creating the VLAN Device</h3>

<p>Before you can create an Interface, you’ll need to set up a VLAN device.  In the “Interfaces” menu there is a submenu called “Devices” and an item in that called “VLAN”.  Click on that option and you’ll get a list of all of the VLAN devices that have already been defined, and then click on the “+” button to add a new one.</p>

<p>You’ll get a screen that looks like this:</p>

<p><img src="/assets/homelab/OPNS_VLAN1.png" alt="VLAN Device" /></p>

<p>The device name is a bit tricky, it needs to start with “vlan0” and then have some more numeric stuff after that.  The parent should be the device associated with your <code class="language-plaintext highlighter-rouge">LAN</code> 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.</p>

<h3 id="assigning-the-vlan-device-to-an-interface">Assigning the VLAN Device to an Interface</h3>

<p>The next step seems like something that should probably happen automatically, but doesn’t.  In the “Interfaces” menu there is an item called “Assignments”.  Click on that and you’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:</p>

<p><img src="/assets/homelab/OPNS_VLAN2.png" alt="VLAN Assignment Creation" /></p>

<p>There’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.</p>

<p>At this point, you will have created an Interface for your VLAN!  But it still needs to be configured.</p>

<h3 id="connecting-your-vlan-to-a-subnet">Connecting Your VLAN to a Subnet</h3>

<p>Once you’ve hit “Apply”, you’ll get a new item in the “Interfaces” menu with the name of your Interface.  Go ahead and click on it:</p>

<p><img src="/assets/homelab/OPNS_VLAN3.png" alt="Interface Configuration" /></p>

<p>Not shown here is the checkbox to enable the Interface.  Be sure to click on that to enable the Interface.</p>

<p>I’m just using IPv4 here, so you I’m not going to do any IPv6.  Select “Static” for the IPv4 configuration type and then you’ll see the box at the bottom with address textbox.</p>

<p>That address textbox <em>is</em> 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’s what the dropdown box to the right is for.  I picked “24” because I want <code class="language-plaintext highlighter-rouge">10.122.73.0</code> to be the subnet, with a mask of <code class="language-plaintext highlighter-rouge">255.255.255.0</code>.</p>

<p>That’s all you need to specify: enable, static IPv4, address and mask.  Hit “Save” and then “Apply”.</p>

<p>Now your Interface and VLAN is set up and ready to go.  You’ll see that new Interface name pop up all over the OPNSense UI whenever it’s an option.  Most importantly, it will be an option in the <code class="language-plaintext highlighter-rouge">Firewall --&gt; Rules</code> menu.  Don’t forget that at this point, it has no rules except the default, “Deny Everything” rule, meaning that this subnet is 100% sequestered for outgoing traffic.</p>

<h2 id="setting-up-dhcp">Setting Up DHCP</h2>

<p>The last step is to configure the OPNSense DHCP service to listen on your new Interface/VLAN.</p>

<p>At the time that I’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 <code class="language-plaintext highlighter-rouge">Dnsmasq DHCP</code>.  So that’s what I’ll show here.</p>

<p>The DHCP configuration is in the “Services” menu.  The first thing you’ll need to do is some basic configuration for the service.  Click on <code class="language-plaintext highlighter-rouge">Services --&gt; Dnsmasq DNS &amp; DHCP</code> to open up the submenu and then click on <code class="language-plaintext highlighter-rouge">General</code>.  You’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’re ready to set up your subnets.</p>

<h3 id="setting-up-dhcp-options">Setting Up DHCP Options</h3>

<p>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.</p>

<p>In OPNSense Dmasq the gateway and the DNS servers are treated as “options”.  You’ll need to configure these for each Interface.  There’s actually a fair number of options available, and you’ll have to pick the correct two.</p>

<p>In the DMasq screen in OPNSense there’s a tab (and a menu item) called “DHCP Options”.  This will show a list of the options that you have configured for each Interface.  There’s the usual “+” button at the bottom right.  Click it and you’ll see a dialogue like this:</p>

<p><img src="/assets/homelab/OPNS_VLAN4.png" alt="DHCP Options" /></p>

<p>Pick the Interface for your VLAN and select “set” as the action.  You need to do this twice.  Once for “router [3]” as default gateway address, and once for “dns-server [6]”.  It’s probably a good idea to specify a description so that you can see what they are at a glance from the main listing.</p>

<h2 id="thats-it">That’s It</h2>

<p>At this point, you have your firewall configured to support your VLAN’s.  It’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’ll have to add the rules to allow devices on these subnets to communicate outside the subnet.</p>

<h3 id="dont-forget-the-switch">Don’t Forget the Switch</h3>

<p>Whatever switch port that <code class="language-plaintext highlighter-rouge">LAN</code> 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.</p>

<h1 id="ignoring-vlans">Ignoring VLAN’s</h1>

<p>The one caveat to all of this is that once a subnet has been connected to a VLAN, nothing outside that VLAN can <em>directly</em> communicate with any address on that subnet that is on the VLAN.</p>

<p>Let’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 <code class="language-plaintext highlighter-rouge">77</code> 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 <code class="language-plaintext highlighter-rouge">10.10.10.10</code>, <code class="language-plaintext highlighter-rouge">10.10.10.11</code> and <code class="language-plaintext highlighter-rouge">10.10.10.12</code> 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.</p>

<p>Furthermore, 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 <code class="language-plaintext highlighter-rouge">10.10.10.1</code> - is going to also be on VLAN <code class="language-plaintext highlighter-rouge">77</code> 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.</p>

<p>At 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.</p>

<p>You can see that it’s important that once you start using VLAN’s to then use them everywhere, especially if you are configuring devices on subnets that aren’t associated with <code class="language-plaintext highlighter-rouge">VLAN1</code>, the default “No VLAN” VLAN.</p>

<h1 id="conclusion">Conclusion</h1>

<p>The first thing to remember is that network security <em>behind</em> your firewall is every bit as important as security between your firewall and the outside world.</p>

<p>It’s incredibly easy to hear about VLAN’s and how important they are to your network security, but it’s much harder to find out <em>how</em> they do that.  I’m hoping that this article helps to clear that up.  But to recap:</p>

<p>The 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’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 “tags”.  Each port can also be configured to assign a specific tag to incoming packets that have no tags on them.</p>

<p>Your 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.</p>

<p>You use this technology to divide your internal network up into subnets that represent different “zones”.  Those zones, in turn, hold devices that share a similar trust profile and they are isolated from other zones via your firewall rules.</p>]]></content><author><name>Dave Barrett</name><email>pragmaticcoding8@gmail.com</email></author><category term="homelab" /><summary type="html"><![CDATA[How to secure your home network from threats from inside the house.]]></summary></entry><entry><title type="html">ObservableLists: Extractors</title><link href="https://www.pragmaticcoding.ca/javafx/elements/extractors" rel="alternate" type="text/html" title="ObservableLists: Extractors" /><published>2025-08-05T17:00:00+00:00</published><updated>2025-08-05T17:00:00+00:00</updated><id>https://www.pragmaticcoding.ca/javafx/elements/extractors</id><content type="html" xml:base="https://www.pragmaticcoding.ca/javafx/elements/extractors"><![CDATA[<h1 id="introduction">Introduction</h1>

<p><code class="language-plaintext highlighter-rouge">ObservableLists</code> (and <code class="language-plaintext highlighter-rouge">ObservableSets</code>, and <code class="language-plaintext highlighter-rouge">ObservableMaps</code>, for that matter), are designed to invalidate and trigger <code class="language-plaintext highlighter-rouge">Listeners</code> when items are add or removed from them.  Screen elements like <code class="language-plaintext highlighter-rouge">ListView</code>, <code class="language-plaintext highlighter-rouge">TableView</code> and the pop-ups in <code class="language-plaintext highlighter-rouge">ComboBox</code> will automatically update when the <code class="language-plaintext highlighter-rouge">ObservableLists</code> that back them are updated.</p>

<p>But what about those cases where your <code class="language-plaintext highlighter-rouge">ObservableLists</code> are made of up composed objects?  Is it possible to detect that an <code class="language-plaintext highlighter-rouge">ObservableList</code> has changed when one of the fields inside an item has changed?</p>

<p>That’s what “extractors” are for.  They allow you to detect changes to <code class="language-plaintext highlighter-rouge">Property</code> fields inside the items in your list, and to trigger <code class="language-plaintext highlighter-rouge">Listeners</code> on the <code class="language-plaintext highlighter-rouge">ObservableList</code> as a result.</p>

<h1 id="the-basics">The Basics</h1>

<p>The JavaDocs for extractors is pretty thin.</p>

<p>The first thing that you’ll notice if you check out the entry for <a href="https://openjfx.io/javadoc/23/javafx.base/javafx/collections/ObservableList.html">ObservableList</a> is that there are no constructors for <code class="language-plaintext highlighter-rouge">ObservableList</code> because it’s an <code class="language-plaintext highlighter-rouge">Interface</code>.  None of the “All Known Implementing Classes” section look promising either.</p>

<p>Typically, you’ll get an <code class="language-plaintext highlighter-rouge">ObservableList</code> because it’s already part of a <code class="language-plaintext highlighter-rouge">Node</code> class like <code class="language-plaintext highlighter-rouge">ListView</code>, <code class="language-plaintext highlighter-rouge">TableView</code> or <code class="language-plaintext highlighter-rouge">ComboBox</code>.  But if you want to create one yourself, you’ll need to use the static library class, <code class="language-plaintext highlighter-rouge">FXCollections</code>.</p>

<p>If you go to the JavaDocs for <a href="https://openjfx.io/javadoc/23/javafx.base/javafx/collections/FXCollections.html">FXCollections</a> you’ll see that there are lots and lots of ways to create different kinds of <code class="language-plaintext highlighter-rouge">ObservableLists</code>, some of which specify extractors.  Let’s look at the details for <code class="language-plaintext highlighter-rouge">observableArrayList(Callback&lt;E,Observable[]&gt; extractor)</code>:</p>

<blockquote>
  <p><strong>public static &lt;E&gt; ObservableList&lt;E&gt; observableArrayList(Callback&lt;E,Observable[]&gt; extractor)</strong></p>

  <p>Creates a new empty ObservableList that is backed by an array list and listens to changes in observables of its items.</p>

  <p>The 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.</p>

  <p>For example, a list of Shapes can listen to changes in the shapes’ fill property.</p>
</blockquote>

<p>This seems to hint at something interesting, but it doesn’t really explain much, does it?</p>

<p>Let’s take a closer look at the constructor parameter: <code class="language-plaintext highlighter-rouge">Callback&lt;E,Observable[]&gt; extractor</code>.</p>

<p>A <code class="language-plaintext highlighter-rouge">Callback</code> is just a <code class="language-plaintext highlighter-rouge">Function</code>, meaning that it accepts one value and then returns another.  The name “Callback” implies that it is going to be used somewhere deep down in the internals of something.  Somewhere that we can’t see, and probably don’t want to see.  The <code class="language-plaintext highlighter-rouge">Callback</code> is a “hook” to allow that hidden code to get at something that we, the application programmers, define.</p>

<p>In this case, the <code class="language-plaintext highlighter-rouge">Callback</code> accepts something of type <code class="language-plaintext highlighter-rouge">E</code>.  What is <code class="language-plaintext highlighter-rouge">E</code>? Well, <code class="language-plaintext highlighter-rouge">ObservableList</code> itself is generic, and <code class="language-plaintext highlighter-rouge">E</code> in this case refers to the type of elements that comprise our <code class="language-plaintext highlighter-rouge">ObservableList</code>.  This means that the <code class="language-plaintext highlighter-rouge">Callback</code> is just going to accept a single element of our <code class="language-plaintext highlighter-rouge">ObservableList</code>, whatever that happens to be.</p>

<p>The output from our <code class="language-plaintext highlighter-rouge">Callback</code> is going to be an <code class="language-plaintext highlighter-rouge">array</code> of <code class="language-plaintext highlighter-rouge">Observable</code>.  <code class="language-plaintext highlighter-rouge">Observable</code> is the top level <code class="language-plaintext highlighter-rouge">Interface</code> that all of the <code class="language-plaintext highlighter-rouge">Properties</code> and observable classes implement.  It only specifies three methods, <code class="language-plaintext highlighter-rouge">addListener()</code>, <code class="language-plaintext highlighter-rouge">removeListener()</code> and <code class="language-plaintext highlighter-rouge">subscribe()</code>.  That last one, <code class="language-plaintext highlighter-rouge">subscribe()</code> is new, and <code class="language-plaintext highlighter-rouge">ObservableList</code> extractors pre-dates it.  So we don’t need to worry about it here.</p>

<p>What this tells us is that at some point, deep inside the <code class="language-plaintext highlighter-rouge">ObservableList</code> code, JavaFX is going to add a <code class="language-plaintext highlighter-rouge">Listener</code> to every <code class="language-plaintext highlighter-rouge">Observable</code> that we return for every item in our <code class="language-plaintext highlighter-rouge">ObservableList</code>.</p>

<p>The only question left is: What does that <code class="language-plaintext highlighter-rouge">Listener</code> do?</p>

<h1 id="what-does-the-extractor-do">What Does the Extractor Do?</h1>

<p>In my article about <a href="/javafx/elements/observable-classes-lists">ObservableLists</a>, part of my <a href="/javafx/elements/observables_guide">series on Observables</a>, I wrote some code to see what kind of <code class="language-plaintext highlighter-rouge">Listeners</code> are activated when changes are made to <code class="language-plaintext highlighter-rouge">ObservableLists</code>.  This involved adding, deleting, replacing and swapping <code class="language-plaintext highlighter-rouge">ObservableList</code> elements.</p>

<p>We can use some of that same code here, to see how extractors translate to the <code class="language-plaintext highlighter-rouge">Listeners</code> triggered the <code class="language-plaintext highlighter-rouge">ObservableList</code>.</p>

<div class="notice--kotlin">
 <img src="/assets/logos/Kotlin.png" alt="Kotlin" style="float:left;margin-right: 10px;margin-top: 8px;" />
 <p style="overflow:auto; float:none">
   While code is this article is written in Kotlin, all of the JavaFX concepts are exactly the same.
   Most of the Kotlin should be intuitively obvious to Java programmers,
   but if you need help understanding it, refer to this <a href="/kotlin/kotlin-examples" title="Read the article" target="_blank">page</a>.
 </p>
</div>

<p>To start, we’ll look at the code without an extractor, and see what it does…</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="kd">val</span> <span class="py">obList</span><span class="p">:</span> <span class="nc">ObservableList</span><span class="p">&lt;</span><span class="nc">ExampleData</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">FXCollections</span><span class="p">.</span><span class="nf">observableArrayList</span><span class="p">()</span>
<span class="k">private</span> <span class="kd">val</span> <span class="py">messages</span><span class="p">:</span> <span class="nc">StringProperty</span> <span class="p">=</span> <span class="nc">SimpleStringProperty</span><span class="p">(</span><span class="s">""</span><span class="p">)</span>
<span class="k">private</span> <span class="kd">val</span> <span class="py">totBinding</span> <span class="p">=</span> <span class="kd">object</span> <span class="err">: </span><span class="nc">IntegerBinding</span><span class="p">()</span> <span class="p">{</span>
    <span class="nf">init</span> <span class="p">{</span>
        <span class="k">super</span><span class="p">.</span><span class="nf">bind</span><span class="p">(</span><span class="n">obList</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">computeValue</span><span class="p">():</span> <span class="nc">Int</span> <span class="p">=</span> <span class="n">obList</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value2</span><span class="p">.</span><span class="n">value</span> <span class="p">}.</span><span class="nf">sum</span><span class="p">()</span>
<span class="p">}</span>

<span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">obList</span><span class="p">.</span><span class="nf">subscribe</span> <span class="p">{</span> <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"Invalidated \n"</span> <span class="p">}</span>
    <span class="k">for</span> <span class="p">(</span><span class="n">x</span> <span class="k">in</span> <span class="mi">1</span><span class="o">..</span><span class="mi">5</span><span class="p">)</span> <span class="n">obList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="nc">ExampleData</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
    <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"Data Loaded\n"</span>
    <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="n">obList</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value2</span><span class="p">.</span><span class="n">value</span> <span class="p">}.</span><span class="nf">joinToString</span><span class="p">()</span> <span class="p">+</span> <span class="s">"\n"</span>
    <span class="n">stage</span><span class="p">.</span><span class="n">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="nf">createContent</span><span class="p">()).</span><span class="nf">apply</span> <span class="p">{</span> <span class="p">}</span>
    <span class="n">stage</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
<span class="p">}</span>

<span class="k">private</span> <span class="k">fun</span> <span class="nf">createContent</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">BorderPane</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
    <span class="n">top</span> <span class="p">=</span> <span class="nc">HBox</span><span class="p">(</span>
        <span class="mf">10.0</span><span class="p">,</span>
        <span class="nc">Label</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span> <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">totBinding</span><span class="p">.</span><span class="nf">asString</span><span class="p">())</span> <span class="p">},</span>
        <span class="nc">Label</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span> <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">obList</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">value2</span><span class="p">.</span><span class="nf">asString</span><span class="p">())</span> <span class="p">})</span>
    <span class="n">center</span> <span class="p">=</span> <span class="nc">TextArea</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">messages</span><span class="p">)</span>
    <span class="p">}</span>
    <span class="n">bottom</span> <span class="p">=</span> <span class="nc">Button</span><span class="p">(</span><span class="s">"Increment Item"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="nf">setOnAction</span> <span class="p">{</span>
            <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"Button Clicked\n"</span>
            <span class="nf">with</span><span class="p">(</span><span class="n">obList</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span> <span class="p">{</span> <span class="n">value2</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">value2</span><span class="p">.</span><span class="n">value</span> <span class="p">+</span> <span class="mi">1</span> <span class="p">}</span>
            <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="n">obList</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value2</span><span class="p">.</span><span class="n">value</span> <span class="p">}.</span><span class="nf">joinToString</span><span class="p">()</span> <span class="p">+</span> <span class="s">"\n"</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">20.0</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="nc">ExampleData</span><span class="p">(</span><span class="n">initialValue</span><span class="p">:</span> <span class="nc">Int</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">value1</span><span class="p">:</span> <span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="n">initialValue</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">value2</span><span class="p">:</span> <span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="n">initialValue</span><span class="p">)</span>
<span class="p">}</span>


<span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Application</span><span class="p">.</span><span class="nf">launch</span><span class="p">(</span><span class="nc">ExtractorExample1</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
</code></pre></div></div>
<p>We have an <code class="language-plaintext highlighter-rouge">ObservableList</code> of <code class="language-plaintext highlighter-rouge">DataExample</code>, which is just an object with two <code class="language-plaintext highlighter-rouge">IntegerProperties</code> in it.  We populate it with 5 items, where both <code class="language-plaintext highlighter-rouge">IntegerProperties</code> are populated with the same value and that value increments for each item that we add.</p>

<p>The GUI is a <code class="language-plaintext highlighter-rouge">BorderPane</code> with an <code class="language-plaintext highlighter-rouge">HBox</code> at the top with two <code class="language-plaintext highlighter-rouge">Labels</code>.  One shows the total of all of <code class="language-plaintext highlighter-rouge">DataExample.value2</code> in the <code class="language-plaintext highlighter-rouge">ObservableList</code> through a <code class="language-plaintext highlighter-rouge">Binding</code> and the other has the current value of <code class="language-plaintext highlighter-rouge">obList[2].value2</code>.</p>

<p>The centre is a <code class="language-plaintext highlighter-rouge">TextArea</code> which is bound to a <code class="language-plaintext highlighter-rouge">StringProperty</code> called <code class="language-plaintext highlighter-rouge">messages</code>.  Various bits of code add new data to <code class="language-plaintext highlighter-rouge">messages</code>, and we’ll look at this.</p>

<p>At the bottom is a <code class="language-plaintext highlighter-rouge">Button</code>.  When this <code class="language-plaintext highlighter-rouge">Button</code> is clicked three things happen:</p>

<ol>
  <li>“Button Clicked” is added to <code class="language-plaintext highlighter-rouge">messages</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">obList[2].value2</code> is incremented.</li>
  <li>A list of all of the values in <code class="language-plaintext highlighter-rouge">value2</code> is added to <code class="language-plaintext highlighter-rouge">messages</code></li>
</ol>

<p>We also have an <code class="language-plaintext highlighter-rouge">InvalidationListener</code> (through <code class="language-plaintext highlighter-rouge">subscribe()</code>) on <code class="language-plaintext highlighter-rouge">obList</code> that just adds “Invalidated” to <code class="language-plaintext highlighter-rouge">messages</code>.  And we add “Data Loaded” to <code class="language-plaintext highlighter-rouge">messages</code> when all of the setup is done.</p>

<p>And all of this looks like this:</p>

<p><img src="/assets/elements/Extractors2.png" alt="Screen Capture 2" /></p>

<p>You can see that <code class="language-plaintext highlighter-rouge">obList</code> invalidates every time a new <code class="language-plaintext highlighter-rouge">ExampleData</code> is added to it.  Then the “Data Loaded” message is appended and we see the initial list of values in <code class="language-plaintext highlighter-rouge">value2</code>.</p>

<p>Then we see that the <code class="language-plaintext highlighter-rouge">Button</code> was clicked, and that <code class="language-plaintext highlighter-rouge">obList[2].value2</code> was incremented.  And then again, and again, and again.</p>

<p>We can also see that our <code class="language-plaintext highlighter-rouge">Label</code> bound to <code class="language-plaintext highlighter-rouge">obList[2].value2</code> has been updated, and it shows “7” - just as you would expect.</p>

<p>However, the other <code class="language-plaintext highlighter-rouge">Label</code> hasn’t been updated.  This is the one bound to the <code class="language-plaintext highlighter-rouge">ObservableList</code> itself.  Also, out <code class="language-plaintext highlighter-rouge">InvalidationListener</code> never fired again.</p>

<div class="notice--primary">
 <img src="/assets/logos/brain.png" alt="primary" style="float:left;margin-right: 10px" />
 <p style="overflow:auto; float:none">
   When ObservableList items are composed objects containing Observable type fields, changes to those internal fields will not trigger Listeners on the ObservableList.
 </p>
</div>

<h2 id="adding-an-extractor">Adding an Extractor</h2>

<p>All we are going to do is change a single line of the code from above.  This:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="kd">val</span> <span class="py">obList</span><span class="p">:</span> <span class="nc">ObservableList</span><span class="p">&lt;</span><span class="nc">ExampleData</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">FXCollections</span><span class="p">.</span><span class="nf">observableArrayList</span><span class="p">()</span>
</code></pre></div></div>
<p>will change to this:</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="kd">val</span> <span class="py">obList</span><span class="p">:</span> <span class="nc">ObservableList</span><span class="p">&lt;</span><span class="nc">ExampleData</span><span class="p">&gt;</span> <span class="p">=</span>
    <span class="nc">FXCollections</span><span class="p">.</span><span class="nf">observableArrayList</span><span class="p">({</span> <span class="n">item</span> <span class="p">-&gt;</span> <span class="nf">arrayOf</span><span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="n">value2</span><span class="p">)</span> <span class="p">})</span>
</code></pre></div></div>
<p>In this code, <code class="language-plaintext highlighter-rouge">{ item -&gt; arrayOf(item.value2) }</code> is the Callback for the extractor.  It creates an array, and it includes <code class="language-plaintext highlighter-rouge">ExampleData.value2</code> in that array.</p>

<p>Now, when we run the program and click the <code class="language-plaintext highlighter-rouge">Button</code> four times, we get this:</p>

<p><img src="/assets/elements/Extractors3.png" alt="Screen Capture 3" /></p>

<p>The first thing that you notice is that we get the “Invalidated” message right after every “Button Clicked” message.  This means that the <code class="language-plaintext highlighter-rouge">InvalidationListener</code> on <code class="language-plaintext highlighter-rouge">obList</code> has fired!</p>

<p>The other thing that you should notice is that the first <code class="language-plaintext highlighter-rouge">Label</code> now says “19”, which is 1+2+7+4+5.  This means that the <code class="language-plaintext highlighter-rouge">Binding</code> on <code class="language-plaintext highlighter-rouge">obList</code> that sums up the values is now working!</p>

<div class="notice--primary">
 <img src="/assets/logos/brain.png" alt="primary" style="float:left;margin-right: 10px" />
 <p style="overflow:auto; float:none">
   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.
 </p>
</div>

<p>This tells us that, at a minumum, the <code class="language-plaintext highlighter-rouge">InvalidationListener</code> that the <code class="language-plaintext highlighter-rouge">ObservableList</code> puts on every <code class="language-plaintext highlighter-rouge">value2</code> of every <code class="language-plaintext highlighter-rouge">ExampleData</code> in the list is used to trigger an <code class="language-plaintext highlighter-rouge">InvalidationListener</code> on the <code class="language-plaintext highlighter-rouge">ObservableList</code> itself.  That’s enough to trigger the <code class="language-plaintext highlighter-rouge">Binding</code> on the <code class="language-plaintext highlighter-rouge">ObservableList</code>, too.</p>

<p>But does it do more?</p>

<h1 id="will-a-listchangelistener-work">Will a ListChangeListener Work?</h1>

<p>Let’s change the code a little bit to add a <code class="language-plaintext highlighter-rouge">ListChangeListener</code> to <code class="language-plaintext highlighter-rouge">obList</code> to see if the extractor will cause it to fire:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">obList</span><span class="p">.</span><span class="nf">subscribe</span> <span class="p">{</span> <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"Invalidated \n"</span> <span class="p">}</span>
    <span class="n">obList</span><span class="p">.</span><span class="nf">addListener</span><span class="p">(</span><span class="nc">ListChangeListener</span> <span class="p">{</span> <span class="n">change</span> <span class="p">-&gt;</span>
        <span class="k">while</span> <span class="p">(</span><span class="n">change</span><span class="p">.</span><span class="nf">next</span><span class="p">())</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">change</span><span class="p">.</span><span class="nf">wasPermutated</span><span class="p">())</span> <span class="p">{</span>
                <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"List was permutated\n"</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">change</span><span class="p">.</span><span class="nf">wasRemoved</span><span class="p">())</span> <span class="p">{</span>
                    <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"List item was removed\n"</span>
                <span class="p">}</span>

                <span class="k">if</span> <span class="p">(</span><span class="n">change</span><span class="p">.</span><span class="nf">wasAdded</span><span class="p">())</span> <span class="p">{</span>
                    <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"List item was added\n"</span>
                <span class="p">}</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">change</span><span class="p">.</span><span class="nf">wasUpdated</span><span class="p">())</span> <span class="p">{</span>
                   <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"List item ${change.from} was updated\n"</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">})</span>
    <span class="k">for</span> <span class="p">(</span><span class="n">x</span> <span class="k">in</span> <span class="mi">1</span><span class="o">..</span><span class="mi">5</span><span class="p">)</span> <span class="n">obList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="nc">ExampleData</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
    <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"Data Loaded\n"</span>
    <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="n">obList</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value2</span><span class="p">.</span><span class="n">value</span> <span class="p">}.</span><span class="nf">joinToString</span><span class="p">()</span> <span class="p">+</span> <span class="s">"\n"</span>
    <span class="n">stage</span><span class="p">.</span><span class="n">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="nf">createContent</span><span class="p">()).</span><span class="nf">apply</span> <span class="p">{</span> <span class="p">}</span>
    <span class="n">stage</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">ListChangeListener</code> accepts a value of <code class="language-plaintext highlighter-rouge">ListChangeListener.Change</code>, which itself is an iterable construct and may contain several different changes to the <code class="language-plaintext highlighter-rouge">ObservableList</code> 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.</p>

<p>Again, we’ll run the code and click on the <code class="language-plaintext highlighter-rouge">Button</code> a few times:</p>

<p><img src="/assets/elements/Extractors4.png" alt="Screen Capture 4" /></p>

<p>Here, we can see that the <code class="language-plaintext highlighter-rouge">ListChangeListener</code> fires as each item is added to the <code class="language-plaintext highlighter-rouge">ObservableList</code>, but then it fires with an “update” <code class="language-plaintext highlighter-rouge">Change</code> when the <code class="language-plaintext highlighter-rouge">Button</code> is clicked.  It also reports which element was updated in <code class="language-plaintext highlighter-rouge">Change.from</code>.</p>

<p>From this, we can safely assume that a <code class="language-plaintext highlighter-rouge">ListChangeListener</code> is going to detect a change made to a field specified in an extractor.</p>

<h1 id="will-a-listproperty-work">Will a ListProperty Work?</h1>

<p>There is another class, called <code class="language-plaintext highlighter-rouge">ListProperty</code> that acts as a wrapper around an <code class="language-plaintext highlighter-rouge">ObservableList</code>.  What if we put our <code class="language-plaintext highlighter-rouge">obList</code> into a <code class="language-plaintext highlighter-rouge">ListProperty</code> and then add a <code class="language-plaintext highlighter-rouge">ChangeListener</code> to that <code class="language-plaintext highlighter-rouge">ListProperty</code>?</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">listProperty</span> <span class="p">=</span> <span class="nc">SimpleListProperty</span><span class="p">(</span><span class="n">obList</span><span class="p">)</span>
    <span class="n">listProperty</span><span class="p">.</span><span class="nf">addListener</span><span class="p">(</span><span class="nc">InvalidationListener</span> <span class="p">{</span> <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"Invalidated \n"</span> <span class="p">})</span>
    <span class="n">listProperty</span><span class="p">.</span><span class="nf">subscribe</span> <span class="p">{</span> <span class="n">oldVal</span><span class="p">,</span> <span class="n">newVal</span> <span class="p">-&gt;</span>
        <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"Change -&gt; Old: $oldVal  New: $newVal\n"</span>
    <span class="p">}</span>
    <span class="k">for</span> <span class="p">(</span><span class="n">x</span> <span class="k">in</span> <span class="mi">1</span><span class="o">..</span><span class="mi">5</span><span class="p">)</span> <span class="n">obList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="nc">ExampleData</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
    <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"Data Loaded\n"</span>
    <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="n">obList</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value2</span><span class="p">.</span><span class="n">value</span> <span class="p">}.</span><span class="nf">joinToString</span><span class="p">()</span> <span class="p">+</span> <span class="s">"\n"</span>
    <span class="n">stage</span><span class="p">.</span><span class="n">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="nf">createContent</span><span class="p">()).</span><span class="nf">apply</span> <span class="p">{</span> <span class="p">}</span>
    <span class="n">stage</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Notice that I’ve also moved the <code class="language-plaintext highlighter-rouge">InvalidationListener</code> to the <code class="language-plaintext highlighter-rouge">ListProperty</code>, so that we can see if it fires on there as well.</p>

<p>When we run this, clicking the <code class="language-plaintext highlighter-rouge">Button</code> four times, we get:</p>

<p><img src="/assets/elements/Extractors5.png" alt="Screen Capture 5" /></p>

<p>We see the <code class="language-plaintext highlighter-rouge">ChangeListener</code> firing as the elements are added, and this not suprising.  You will notice, however, thet the <code class="language-plaintext highlighter-rouge">oldVal</code> and <code class="language-plaintext highlighter-rouge">newVal</code> are both actually the <code class="language-plaintext highlighter-rouge">newVal</code>.  This is a quirk of <code class="language-plaintext highlighter-rouge">ChangeListeners</code> on <code class="language-plaintext highlighter-rouge">ListProperty</code>, and has nothing to do with extractors.</p>

<p>Then we see the <code class="language-plaintext highlighter-rouge">ChangeListener</code> also fires on the changes to the enclosed field through the extractor.  This is interesting, although we still don’t get to see the correct value for <code class="language-plaintext highlighter-rouge">oldVal</code>.</p>

<p>You should also note that the <code class="language-plaintext highlighter-rouge">InvalidationListener</code> also fires every time for the <code class="language-plaintext highlighter-rouge">ListProperty</code>.</p>

<p>And just to make sure this is the extractor that is doing this, I took it out and re-ran the program:</p>

<p><img src="/assets/elements/Extractors6.png" alt="Screen Capture 6" /></p>

<p>Here, we just get the <code class="language-plaintext highlighter-rouge">Listeners</code> firing for the additions to the <code class="language-plaintext highlighter-rouge">ObservableList</code>.  The changes to the enclosed <code class="language-plaintext highlighter-rouge">Property</code> do not trigger any <code class="language-plaintext highlighter-rouge">Listeners</code>.</p>

<h1 id="advanced-extractors">Advanced Extractors</h1>

<p>If you look up extractor examples on-line, you’ll never see anything more than:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Callback</span><span class="o">&lt;</span><span class="nc">TestClass</span><span class="o">,</span> <span class="nc">Observable</span><span class="o">[]&gt;</span> <span class="n">extractor</span> <span class="o">=</span> <span class="n">obj</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nc">Observable</span><span class="o">[]{</span> <span class="n">obj</span><span class="o">.</span><span class="na">nameProperty</span><span class="o">()</span> <span class="o">};</span>
<span class="nc">ObservableList</span><span class="o">&lt;</span><span class="nc">TestClass</span><span class="o">&gt;</span> <span class="n">list</span> <span class="o">=</span> <span class="nc">FXCollections</span><span class="o">.</span><span class="na">observableArrayList</span><span class="o">(</span><span class="n">extractor</span><span class="o">);</span>
</code></pre></div></div>
<p>I just pulled this example from a StackOverflow question, but it’s pretty much the standard.</p>

<p>But the extractor is a <code class="language-plaintext highlighter-rouge">Callback</code> and can do much more, if you want.  Let’s take a look at that:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">ExtractorExample4</span> <span class="p">:</span> <span class="nc">Application</span><span class="p">()</span> <span class="p">{</span>

    <span class="k">private</span> <span class="kd">val</span> <span class="py">obList</span><span class="p">:</span> <span class="nc">ObservableList</span><span class="p">&lt;</span><span class="nc">ExampleData1</span><span class="p">&gt;</span> <span class="p">=</span>
        <span class="nc">FXCollections</span><span class="p">.</span><span class="nf">observableArrayList</span><span class="p">({</span> <span class="n">item</span> <span class="p">-&gt;</span> <span class="n">item</span><span class="p">.</span><span class="nf">extractableValues</span><span class="p">()</span> <span class="p">})</span>

    <span class="k">private</span> <span class="kd">val</span> <span class="py">messages</span><span class="p">:</span> <span class="nc">StringProperty</span> <span class="p">=</span> <span class="nc">SimpleStringProperty</span><span class="p">(</span><span class="s">""</span><span class="p">)</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">totBinding</span> <span class="p">=</span> <span class="kd">object</span> <span class="err">: </span><span class="nc">IntegerBinding</span><span class="p">()</span> <span class="p">{</span>
        <span class="nf">init</span> <span class="p">{</span>
            <span class="k">super</span><span class="p">.</span><span class="nf">bind</span><span class="p">(</span><span class="n">obList</span><span class="p">)</span>
        <span class="p">}</span>

        <span class="k">override</span> <span class="k">fun</span> <span class="nf">computeValue</span><span class="p">():</span> <span class="nc">Int</span> <span class="p">=</span> <span class="n">obList</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value2</span><span class="p">.</span><span class="n">value</span> <span class="p">}.</span><span class="nf">sum</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">obList</span><span class="p">.</span><span class="nf">addListener</span><span class="p">(</span><span class="nc">InvalidationListener</span> <span class="p">{</span> <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"Invalidated \n"</span> <span class="p">})</span>
        <span class="k">for</span> <span class="p">(</span><span class="n">x</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="mi">5</span><span class="p">)</span> <span class="n">obList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="nc">ExampleData1</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
        <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"Data Loaded\n"</span>
        <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="n">obList</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value2</span><span class="p">.</span><span class="n">value</span> <span class="p">}.</span><span class="nf">joinToString</span><span class="p">()</span> <span class="p">+</span> <span class="s">"\n"</span>
        <span class="n">stage</span><span class="p">.</span><span class="n">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="nf">createContent</span><span class="p">()).</span><span class="nf">apply</span> <span class="p">{</span> <span class="p">}</span>
        <span class="n">stage</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">createContent</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">BorderPane</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">top</span> <span class="p">=</span> <span class="nc">HBox</span><span class="p">(</span>
            <span class="mf">10.0</span><span class="p">,</span>
            <span class="nc">Label</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span> <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">totBinding</span><span class="p">.</span><span class="nf">asString</span><span class="p">())</span> <span class="p">},</span>
            <span class="nc">Label</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span> <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">obList</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">value2</span><span class="p">.</span><span class="nf">asString</span><span class="p">())</span> <span class="p">})</span>
        <span class="n">center</span> <span class="p">=</span> <span class="nc">TextArea</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">messages</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">bottom</span> <span class="p">=</span> <span class="nc">HBox</span><span class="p">(</span>
            <span class="mf">20.0</span><span class="p">,</span> <span class="nc">Button</span><span class="p">(</span><span class="s">"Increment Item 2"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="nf">setOnAction</span> <span class="p">{</span>
                    <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"Button 1 Clicked\n"</span>
                    <span class="nf">with</span><span class="p">(</span><span class="n">obList</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span> <span class="p">{</span> <span class="n">value2</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">value2</span><span class="p">.</span><span class="n">value</span> <span class="p">+</span> <span class="mi">1</span> <span class="p">}</span>
                    <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="n">obList</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value2</span><span class="p">.</span><span class="n">value</span> <span class="p">}.</span><span class="nf">joinToString</span><span class="p">()</span> <span class="p">+</span> <span class="s">"\n"</span>
                <span class="p">}</span>
            <span class="p">},</span>
            <span class="nc">Button</span><span class="p">(</span><span class="s">"Increment Item 5"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="nf">setOnAction</span> <span class="p">{</span>
                    <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"Button 2 Clicked\n"</span>
                    <span class="nf">with</span><span class="p">(</span><span class="n">obList</span><span class="p">[</span><span class="mi">5</span><span class="p">])</span> <span class="p">{</span> <span class="n">value2</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">value2</span><span class="p">.</span><span class="n">value</span> <span class="p">+</span> <span class="mi">1</span> <span class="p">}</span>
                    <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="n">obList</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value2</span><span class="p">.</span><span class="n">value</span> <span class="p">}.</span><span class="nf">joinToString</span><span class="p">()</span> <span class="p">+</span> <span class="s">"\n"</span>
                <span class="p">}</span>
            <span class="p">})</span>
        <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">20.0</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="nc">ExampleData1</span><span class="p">(</span><span class="k">private</span> <span class="kd">val</span> <span class="py">initialValue</span><span class="p">:</span> <span class="nc">Int</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">value1</span><span class="p">:</span> <span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="n">initialValue</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">value2</span><span class="p">:</span> <span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="n">initialValue</span><span class="p">)</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">toString</span><span class="p">()</span> <span class="p">=</span> <span class="s">"[${value1.value}, ${value2.value}]"</span>

    <span class="k">fun</span> <span class="nf">extractableValues</span><span class="p">():</span> <span class="nc">Array</span><span class="p">&lt;</span><span class="nc">Observable</span><span class="p">&gt;</span> <span class="p">{</span>
        <span class="k">return</span> <span class="k">if</span> <span class="p">(</span><span class="n">initialValue</span> <span class="p">&lt;</span> <span class="mi">4</span><span class="p">)</span> <span class="nf">arrayOf</span><span class="p">(</span><span class="n">value1</span><span class="p">)</span> <span class="k">else</span> <span class="nf">arrayOf</span><span class="p">(</span><span class="n">value2</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>


<span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Application</span><span class="p">.</span><span class="nf">launch</span><span class="p">(</span><span class="nc">ExtractorExample4</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
</code></pre></div></div>
<p>You’ll notice that we’ve added <code class="language-plaintext highlighter-rouge">ExampleData1</code>, which differs from <code class="language-plaintext highlighter-rouge">ExampleData</code> in two ways:</p>

<ol>
  <li>The constructor parameter <code class="language-plaintext highlighter-rouge">initialValue</code> has now been changed into a private immutable field.</li>
  <li>We’ve have a new method, <code class="language-plaintext highlighter-rouge">extractableValues</code>.</li>
</ol>

<p>This new method will return an <code class="language-plaintext highlighter-rouge">Array</code> containing either <code class="language-plaintext highlighter-rouge">value1</code> or <code class="language-plaintext highlighter-rouge">value2</code> depending on whether <code class="language-plaintext highlighter-rouge">initialValue</code> is less than 4 or not.</p>

<p>Additionally, we’ve added a second <code class="language-plaintext highlighter-rouge">Button</code> that works very much the same way as the original <code class="language-plaintext highlighter-rouge">Button</code> but acts on the 5th item in the <code class="language-plaintext highlighter-rouge">ObservableList</code>, which should have an <code class="language-plaintext highlighter-rouge">initialValue</code> greater than 4.</p>

<p>The <code class="language-plaintext highlighter-rouge">Listeners</code> have been return to very much the way that they were before we introduced the <code class="language-plaintext highlighter-rouge">ListProperty</code>, which has now been removed.</p>

<p>Here’s what the screen looks like when we click the first <code class="language-plaintext highlighter-rouge">Button</code> a few times:</p>

<p><img src="/assets/elements/Extractors7.png" alt="Screen Capture 7" /></p>

<p>You can see the messages from the <code class="language-plaintext highlighter-rouge">Button</code> click, and you can see the updated values in <code class="language-plaintext highlighter-rouge">obList</code>.  You can also see that the second <code class="language-plaintext highlighter-rouge">Label</code> is still correct.  However, the first <code class="language-plaintext highlighter-rouge">Label</code> does NOT have the correct value, and we do not have any “Invalidated” messages for those clicks.</p>

<p>This is expected, because the <code class="language-plaintext highlighter-rouge">initialValue</code> of the item we clicked was “2” which is less than “4” and the extractor pulls <code class="language-plaintext highlighter-rouge">value1</code> for that item, while the <code class="language-plaintext highlighter-rouge">Button</code> is updating <code class="language-plaintext highlighter-rouge">value2</code>.</p>

<p>Here’s what it looks like after we click the new <code class="language-plaintext highlighter-rouge">Button</code> a few times:</p>

<p><img src="/assets/elements/Extractors8.png" alt="Screen Capture 8" /></p>

<p>Now we see the “Invalidated” messages, and the first <code class="language-plaintext highlighter-rouge">Label</code> now contains the correct total.</p>

<h2 id="uses-of-this-technique">Uses of This Technique</h2>

<p>I’ve never seen anyone demonstrate anything like this before, and I’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:</p>

<h3 id="the-extraction-details-are-encapsulated">The Extraction Details are Encapsulated</h3>

<p>When you think about it, you see that this approach puts the logic for extraction <em>inside</em> the <code class="language-plaintext highlighter-rouge">ObservableList</code> items.  This means that the nature of the extraction is hidden from the <code class="language-plaintext highlighter-rouge">ObservableList</code> itself.</p>

<p>If you are using a framework with a formal Presentation Model, like MVCI, then you are most likely going to instantiate your <code class="language-plaintext highlighter-rouge">ObservableLists</code> inside the Model.  Defining the extractors as part of that call to <code class="language-plaintext highlighter-rouge">FXCollections.observableArrayList()</code> requires that the Model knows about the structure of the objects that are put inside the <code class="language-plaintext highlighter-rouge">ObservableList</code>.</p>

<p>This might not be appropriate, even though those <code class="language-plaintext highlighter-rouge">ObservableList</code> items are part of the Presentation Model.</p>

<p>Still, it’s worth considering, and I can see some value in putting the logic that dictates how the items will behave in an <code class="language-plaintext highlighter-rouge">ObservableList</code> right beside the actual data that comprises the items.</p>

<h3 id="the-extraction-logic-needs-to-be-immutable">The Extraction Logic Needs to Be Immutable</h3>

<p>You’ll notice that I very deliberately based the extraction logic on a field defined as <code class="language-plaintext highlighter-rouge">val</code> instead of <code class="language-plaintext highlighter-rouge">var</code>.  This is the same as using <code class="language-plaintext highlighter-rouge">final</code> in Java.</p>

<p>This is because the extractor logic is only run once, when the item is added to the <code class="language-plaintext highlighter-rouge">ObservableList</code>.</p>

<p>I checked the source code and found that the extractor code is run to get an <code class="language-plaintext highlighter-rouge">Array</code> of <code class="language-plaintext highlighter-rouge">Observable</code> and then the <code class="language-plaintext highlighter-rouge">ObservableList</code> loops through all of these <code class="language-plaintext highlighter-rouge">Observables</code> and adds an <code class="language-plaintext highlighter-rouge">InvalidationListener</code> to each one.  This code is run when the <code class="language-plaintext highlighter-rouge">ObservableList</code> is created as a wrapper around an existing <code class="language-plaintext highlighter-rouge">List</code>, like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">obList</span> <span class="p">=</span> <span class="nc">FXCollections</span><span class="p">.</span><span class="nf">observableArrayList</span><span class="p">(</span><span class="n">existingList</span><span class="p">,</span> <span class="n">extractor</span><span class="p">)</span>
</code></pre></div></div>
<p>In this case, the interal code will loop through every item in <code class="language-plaintext highlighter-rouge">existingList</code>, run the <code class="language-plaintext highlighter-rouge">extractor</code> against them and the add the <code class="language-plaintext highlighter-rouge">InvalidationListeners</code> to every <code class="language-plaintext highlighter-rouge">Observable</code> returned by the <code class="language-plaintext highlighter-rouge">extractor</code> for each item.</p>

<p>The extractor code is also run when a new item is added to an existing <code class="language-plaintext highlighter-rouge">ObservableList</code> that has an extractor.</p>

<p>There extractor code is also run when an item is removed from an <code class="language-plaintext highlighter-rouge">ObservableList</code>, in order to remove the <code class="language-plaintext highlighter-rouge">InvalidationListeners</code> that were added by the <code class="language-plaintext highlighter-rouge">ObservableList</code>.</p>

<p>Other than that, the extract isn’t ever run against an item.</p>

<p>If, in our example, we had made <code class="language-plaintext highlighter-rouge">initialValue</code> a var, and provided some way to change it after the items were in the list, there would be no change to <code class="language-plaintext highlighter-rouge">InvalidationListeners</code> added by the <code class="language-plaintext highlighter-rouge">ObservableList</code>, because they were already established.  So, you could end up with a system where the apparent behaviour of the extractor doesn’t match the current state of the data.</p>

<p>The worse case scenario is that if the item is removed from the <code class="language-plaintext highlighter-rouge">ObservableList</code>, then some of the <code class="language-plaintext highlighter-rouge">InvalidationListeners</code> might not be removed because the extractor behaviour wasn’t the same as when the item was added to the <code class="language-plaintext highlighter-rouge">ObservableList</code>.  That could give some weird results.</p>

<h1 id="extractors-in-tableview-items">Extractors in TableView Items</h1>

<p>Probably the most common use of composed <code class="language-plaintext highlighter-rouge">Property</code> objects as items in <code class="language-plaintext highlighter-rouge">ObservableLists</code> is in <code class="language-plaintext highlighter-rouge">TableView</code>.  So let’s take a look at how <code class="language-plaintext highlighter-rouge">TableView</code> columns are usually set up, and how they interact with enclosed <code class="language-plaintext highlighter-rouge">Property</code> fields.</p>

<p>Here’s the same example code, with just a <code class="language-plaintext highlighter-rouge">TableView</code> added:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">ExtractorExample5</span> <span class="p">:</span> <span class="nc">Application</span><span class="p">()</span> <span class="p">{</span>

    <span class="k">private</span> <span class="kd">val</span> <span class="py">obList</span><span class="p">:</span> <span class="nc">ObservableList</span><span class="p">&lt;</span><span class="nc">ExampleData</span><span class="p">&gt;</span> <span class="p">=</span>
        <span class="nc">FXCollections</span><span class="p">.</span><span class="nf">observableArrayList</span><span class="p">()</span>

    <span class="k">private</span> <span class="kd">val</span> <span class="py">messages</span><span class="p">:</span> <span class="nc">StringProperty</span> <span class="p">=</span> <span class="nc">SimpleStringProperty</span><span class="p">(</span><span class="s">""</span><span class="p">)</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">totBinding</span> <span class="p">=</span> <span class="kd">object</span> <span class="err">: </span><span class="nc">IntegerBinding</span><span class="p">()</span> <span class="p">{</span>
        <span class="nf">init</span> <span class="p">{</span>
            <span class="k">super</span><span class="p">.</span><span class="nf">bind</span><span class="p">(</span><span class="n">obList</span><span class="p">)</span>
        <span class="p">}</span>

        <span class="k">override</span> <span class="k">fun</span> <span class="nf">computeValue</span><span class="p">():</span> <span class="nc">Int</span> <span class="p">=</span> <span class="n">obList</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value2</span><span class="p">.</span><span class="n">value</span> <span class="p">}.</span><span class="nf">sum</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">obList</span><span class="p">.</span><span class="nf">addListener</span><span class="p">(</span><span class="nc">InvalidationListener</span> <span class="p">{</span> <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"Invalidated \n"</span> <span class="p">})</span>
        <span class="k">for</span> <span class="p">(</span><span class="n">x</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="mi">5</span><span class="p">)</span> <span class="n">obList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="nc">ExampleData</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
        <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"Data Loaded\n"</span>
        <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="n">obList</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value2</span><span class="p">.</span><span class="n">value</span> <span class="p">}.</span><span class="nf">joinToString</span><span class="p">()</span> <span class="p">+</span> <span class="s">"\n"</span>
        <span class="n">stage</span><span class="p">.</span><span class="n">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="nf">createContent</span><span class="p">()).</span><span class="nf">apply</span> <span class="p">{</span> <span class="p">}</span>
        <span class="n">stage</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">createContent</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">BorderPane</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">top</span> <span class="p">=</span> <span class="nc">HBox</span><span class="p">(</span>
            <span class="mf">10.0</span><span class="p">,</span>
            <span class="nc">Label</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span> <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">totBinding</span><span class="p">.</span><span class="nf">asString</span><span class="p">())</span> <span class="p">},</span>
            <span class="nc">Label</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span> <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">obList</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">value2</span><span class="p">.</span><span class="nf">asString</span><span class="p">())</span> <span class="p">})</span>
        <span class="n">center</span> <span class="p">=</span> <span class="nc">TextArea</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">messages</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">left</span> <span class="p">=</span> <span class="nc">TableView</span><span class="p">&lt;</span><span class="nc">ExampleData</span><span class="p">&gt;().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">items</span> <span class="p">=</span> <span class="n">obList</span>
            <span class="n">columns</span> <span class="p">+=</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">ExampleData</span><span class="p">,</span> <span class="nc">Int</span><span class="p">&gt;(</span><span class="s">"Value 1"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="nf">setCellValueFactory</span> <span class="p">{</span> <span class="n">p</span> <span class="p">-&gt;</span> <span class="n">p</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value1</span><span class="p">.</span><span class="nf">asObject</span><span class="p">()</span> <span class="p">}</span>
            <span class="p">}</span>
            <span class="n">columns</span> <span class="p">+=</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">ExampleData</span><span class="p">,</span> <span class="nc">Int</span><span class="p">&gt;(</span><span class="s">"Value 2"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="n">maxHeight</span> <span class="p">=</span> <span class="mf">100.0</span>
                <span class="nf">setCellValueFactory</span> <span class="p">{</span> <span class="n">p</span> <span class="p">-&gt;</span> <span class="n">p</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value2</span><span class="p">.</span><span class="nf">asObject</span><span class="p">()</span> <span class="p">}</span>
                <span class="nf">setCellFactory</span> <span class="p">{</span> <span class="n">column</span> <span class="p">-&gt;</span>
                    <span class="kd">object</span> <span class="err">: </span><span class="nc">TableCell</span><span class="p">&lt;</span><span class="nc">ExampleData</span><span class="p">,</span> <span class="nc">Int</span><span class="p">&gt;()</span> <span class="p">{</span>
                        <span class="k">override</span> <span class="k">fun</span> <span class="nf">updateItem</span><span class="p">(</span><span class="n">newItem</span><span class="p">:</span> <span class="nc">Int</span><span class="p">?,</span> <span class="n">empty</span><span class="p">:</span> <span class="nc">Boolean</span><span class="p">)</span> <span class="p">{</span>
                            <span class="k">super</span><span class="p">.</span><span class="nf">updateItem</span><span class="p">(</span><span class="n">newItem</span><span class="p">,</span> <span class="n">empty</span><span class="p">)</span>
                            <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"Table Cell Update: $newItem\n"</span>
                            <span class="n">text</span> <span class="p">=</span> <span class="k">null</span>
                            <span class="n">graphic</span> <span class="p">=</span> <span class="k">null</span>
                            <span class="n">newItem</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span>
                                <span class="k">if</span> <span class="p">(!</span><span class="n">empty</span><span class="p">)</span> <span class="p">{</span>
                                    <span class="n">text</span> <span class="p">=</span> <span class="n">newItem</span><span class="p">.</span><span class="nf">toString</span><span class="p">()</span>
                                <span class="p">}</span>
                            <span class="p">}</span>
                        <span class="p">}</span>
                    <span class="p">}</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="n">bottom</span> <span class="p">=</span> <span class="nc">Button</span><span class="p">(</span><span class="s">"Increment Item 2"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nf">setOnAction</span> <span class="p">{</span>
                <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"Button 1 Clicked\n"</span>
                <span class="nf">with</span><span class="p">(</span><span class="n">obList</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span> <span class="p">{</span> <span class="n">value2</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">value2</span><span class="p">.</span><span class="n">value</span> <span class="p">+</span> <span class="mi">1</span> <span class="p">}</span>
                <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="n">obList</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value2</span><span class="p">.</span><span class="n">value</span> <span class="p">}.</span><span class="nf">joinToString</span><span class="p">()</span> <span class="p">+</span> <span class="s">"\n"</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">20.0</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>


<span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Application</span><span class="p">.</span><span class="nf">launch</span><span class="p">(</span><span class="nc">ExtractorExample5</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
</code></pre></div></div>
<p>This is very close to the first example, but with the <code class="language-plaintext highlighter-rouge">TableView</code> added.  I’ve also removed the extractor from the <code class="language-plaintext highlighter-rouge">ObservableList</code>, so what we will see is pure <code class="language-plaintext highlighter-rouge">TableView</code> behaviour.</p>

<p>I added a custom <code class="language-plaintext highlighter-rouge">TableCell</code> to the second column so that we could see when the <code class="language-plaintext highlighter-rouge">updateItem()</code> method gets called, and what value it receives.  I’ve also limited the height of the <code class="language-plaintext highlighter-rouge">TableView</code> to restrict the number of <code class="language-plaintext highlighter-rouge">TableCells</code> instantiated because they generate a lot of messages, even if empty:</p>

<p><img src="/assets/elements/Extractors9.png" alt="Screen Snap 9" /></p>

<p>The <code class="language-plaintext highlighter-rouge">Button</code> has been clicked twice, and you can see from the <code class="language-plaintext highlighter-rouge">TableView</code> that it has updated correctly.  You can see from the messages, that the <code class="language-plaintext highlighter-rouge">TableCells</code> get updated a lot just setting things up.  If you look at the bottom of the messages you can see the two <code class="language-plaintext highlighter-rouge">Button</code> clicks, and you can see the <code class="language-plaintext highlighter-rouge">TableCell</code> updating right away.  You do not see any invalidation messages from the <code class="language-plaintext highlighter-rouge">ObservableList</code>, however.</p>

<p>This tells us that the <code class="language-plaintext highlighter-rouge">TableView</code> puts a <code class="language-plaintext highlighter-rouge">Listener</code> of some sort on the <code class="language-plaintext highlighter-rouge">Property</code> when it identifies it by calling the <code class="language-plaintext highlighter-rouge">setCellValueFactory</code>.  That’s the only way that this will work.</p>

<p>What happens if we put the extractor back into the <code class="language-plaintext highlighter-rouge">ObservableList</code>?  You get this:</p>

<p><img src="/assets/elements/Extractors10.png" alt="Screen Snap 10" /></p>

<p>Not much, really.</p>

<p>The first <code class="language-plaintext highlighter-rouge">Label</code> now has the correct total, as expected, and we see that the <code class="language-plaintext highlighter-rouge">ObservableList</code> invalidates after each <code class="language-plaintext highlighter-rouge">Button</code> click.  But it doesn’t cause the <code class="language-plaintext highlighter-rouge">TableCell</code> to update twice, or anything like that.</p>

<p>We can determine from this that adding an extractor to an <code class="language-plaintext highlighter-rouge">ObservableList</code> used in a <code class="language-plaintext highlighter-rouge">TableView</code> doesn’t hurt the performance of the <code class="language-plaintext highlighter-rouge">TableView</code>, but it doesn’t help it either.</p>

<p>Of course this is only true <strong>if</strong> you compose your <code class="language-plaintext highlighter-rouge">TableView</code> items from <code class="language-plaintext highlighter-rouge">Properties</code> and other <code class="language-plaintext highlighter-rouge">ObservableValues</code>.</p>

<h1 id="extractors-in-listview-items">Extractors in ListView Items</h1>

<p>So now we know that <code class="language-plaintext highlighter-rouge">TableView</code> works fine without extractors, but what about <code class="language-plaintext highlighter-rouge">ListView</code>?</p>

<p>We’ll go back to our example, and change the layout so that it shows a <code class="language-plaintext highlighter-rouge">ListView</code> instead of a <code class="language-plaintext highlighter-rouge">TableView</code>:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="k">fun</span> <span class="nf">createContent</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">BorderPane</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
    <span class="n">top</span> <span class="p">=</span> <span class="nc">HBox</span><span class="p">(</span>
        <span class="mf">10.0</span><span class="p">,</span>
        <span class="nc">Label</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span> <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">totBinding</span><span class="p">.</span><span class="nf">asString</span><span class="p">())</span> <span class="p">},</span>
        <span class="nc">Label</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span> <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">obList</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">value2</span><span class="p">.</span><span class="nf">asString</span><span class="p">())</span> <span class="p">})</span>
    <span class="n">center</span> <span class="p">=</span> <span class="nc">TextArea</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">messages</span><span class="p">)</span>
    <span class="p">}</span>
    <span class="n">left</span> <span class="p">=</span> <span class="nc">ListView</span><span class="p">&lt;</span><span class="nc">ExampleData</span><span class="p">&gt;().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">items</span> <span class="p">=</span> <span class="n">obList</span>
        <span class="n">cellFactory</span> <span class="p">=</span> <span class="nc">Callback</span> <span class="p">{</span> <span class="n">_</span> <span class="p">-&gt;</span>
            <span class="kd">object</span> <span class="err">: </span><span class="nc">ListCell</span><span class="p">&lt;</span><span class="nc">ExampleData</span><span class="p">&gt;()</span> <span class="p">{</span>
                <span class="kd">val</span> <span class="py">label1</span> <span class="p">=</span> <span class="nc">Label</span><span class="p">()</span>
                <span class="kd">val</span> <span class="py">label2</span> <span class="p">=</span> <span class="nc">Label</span><span class="p">()</span>
                <span class="kd">val</span> <span class="py">layout</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">2.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
                    <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">,</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Value 1:"</span><span class="p">),</span> <span class="n">label1</span><span class="p">)</span>
                    <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">,</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Value 2:"</span><span class="p">),</span> <span class="n">label2</span><span class="p">)</span>
                    <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">8.0</span><span class="p">)</span>
                <span class="p">}</span>

                <span class="k">override</span> <span class="k">fun</span> <span class="nf">updateItem</span><span class="p">(</span><span class="n">newItem</span><span class="p">:</span> <span class="nc">ExampleData</span><span class="p">?,</span> <span class="n">isEmpty</span><span class="p">:</span> <span class="nc">Boolean</span><span class="p">)</span> <span class="p">{</span>
                    <span class="k">super</span><span class="p">.</span><span class="nf">updateItem</span><span class="p">(</span><span class="n">newItem</span><span class="p">,</span> <span class="n">isEmpty</span><span class="p">)</span>
                    <span class="n">graphic</span> <span class="p">=</span> <span class="k">null</span>
                    <span class="n">text</span> <span class="p">=</span> <span class="k">null</span>
                    <span class="n">newItem</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span>
                        <span class="k">if</span> <span class="p">(!</span><span class="n">isEmpty</span><span class="p">)</span> <span class="p">{</span>
                            <span class="n">label1</span><span class="p">.</span><span class="n">text</span> <span class="p">=</span> <span class="n">newItem</span><span class="p">.</span><span class="n">value1</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="nf">toString</span><span class="p">()</span>
                            <span class="n">label2</span><span class="p">.</span><span class="n">text</span> <span class="p">=</span> <span class="n">newItem</span><span class="p">.</span><span class="n">value2</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="nf">toString</span><span class="p">()</span>
                            <span class="n">graphic</span> <span class="p">=</span> <span class="n">layout</span>
                        <span class="p">}</span>
                    <span class="p">}</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="n">bottom</span> <span class="p">=</span> <span class="nc">Button</span><span class="p">(</span><span class="s">"Increment Item 2"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="nf">setOnAction</span> <span class="p">{</span>
            <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="s">"Button 1 Clicked\n"</span>
            <span class="nf">with</span><span class="p">(</span><span class="n">obList</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span> <span class="p">{</span> <span class="n">value1</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">value1</span><span class="p">.</span><span class="n">value</span> <span class="p">+</span> <span class="mi">1</span> <span class="p">}</span>
            <span class="n">messages</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="n">obList</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value1</span><span class="p">.</span><span class="n">value</span> <span class="p">}.</span><span class="nf">joinToString</span><span class="p">()</span> <span class="p">+</span> <span class="s">"\n"</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">20.0</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Here we have a custom <code class="language-plaintext highlighter-rouge">ListCell</code> layout that contains a <code class="language-plaintext highlighter-rouge">VBox</code> holding two <code class="language-plaintext highlighter-rouge">HBoxes</code>.  Each <code class="language-plaintext highlighter-rouge">HBox</code> has two <code class="language-plaintext highlighter-rouge">Labels</code>, one is just a title, and the other is loaded with either <code class="language-plaintext highlighter-rouge">ExampleData.value1.value</code> or <code class="language-plaintext highlighter-rouge">ExampleData.value2.value</code>.  No <code class="language-plaintext highlighter-rouge">Bindings</code> are used, so there’s no reliance inside the <code class="language-plaintext highlighter-rouge">ListCell</code> on the <code class="language-plaintext highlighter-rouge">Property</code> nature of <code class="language-plaintext highlighter-rouge">value1</code> or <code class="language-plaintext highlighter-rouge">value2</code>.</p>

<p>The <code class="language-plaintext highlighter-rouge">updateItem()</code> method does the usual stuff, and loads <code class="language-plaintext highlighter-rouge">value1</code> and <code class="language-plaintext highlighter-rouge">value2</code> into their respective <code class="language-plaintext highlighter-rouge">Labels</code>.</p>

<p>The only other change to this code from the <code class="language-plaintext highlighter-rouge">TableView</code> version was to change the <code class="language-plaintext highlighter-rouge">Button</code> action to update <code class="language-plaintext highlighter-rouge">ExampleData.value1</code> instead of <code class="language-plaintext highlighter-rouge">ExampleData.value2</code>.  The <code class="language-plaintext highlighter-rouge">ObservableList</code> still has an extractor that returns <code class="language-plaintext highlighter-rouge">value2</code>, so this change essentially disables the extractor for this example.</p>

<p>This is what it looks like when it is run and the <code class="language-plaintext highlighter-rouge">Button</code> is clicked a few times:</p>

<p><img src="/assets/elements/Extractors11.png" alt="Screen Snap" /></p>

<p>You can see that the values displayed in the <code class="language-plaintext highlighter-rouge">ListView</code> do not change at all.  You can see in the <code class="language-plaintext highlighter-rouge">TextArea</code> that the <code class="language-plaintext highlighter-rouge">Button</code> has been clicked several times, yet the <code class="language-plaintext highlighter-rouge">ObservableList</code> never invalidated, but that <code class="language-plaintext highlighter-rouge">value1</code> did increment each time.</p>

<p>If we change the extractor to return <code class="language-plaintext highlighter-rouge">value1</code> instead of <code class="language-plaintext highlighter-rouge">value2</code>, this is what it looks like:</p>

<p><img src="/assets/elements/Extractors12.png" alt="Screen Snap" /></p>

<p>Now you can see that the <code class="language-plaintext highlighter-rouge">ListView</code> values are correct, and that the <code class="language-plaintext highlighter-rouge">Button</code> click causes the <code class="language-plaintext highlighter-rouge">ObservableList</code> to invalidate.</p>

<h2 id="is-this-a-good-approach">Is This a Good Approach?</h2>

<p>This approach has the advantage that you can implement a very naive design for the <code class="language-plaintext highlighter-rouge">ListCell</code>, without having to worry about binding and unbinding from a changing <code class="language-plaintext highlighter-rouge">itemProperty()</code>.  This is pretty much exactly the same approach to <code class="language-plaintext highlighter-rouge">ListCell</code> design that you’ll see as the copypasta example in virtually every online tutorial about <code class="language-plaintext highlighter-rouge">ListView</code>.  It’s simple, and it’s easy, and it’s what everybody knows.</p>

<p>However, it effectively splits the logic involved in keeping the <code class="language-plaintext highlighter-rouge">ListCell</code> up to date between two places, the <code class="language-plaintext highlighter-rouge">ListCell</code> itself, and the extractor.</p>

<div class="notice--primary">
 <img src="/assets/logos/brain.png" alt="primary" style="float:left;margin-right: 10px" />
 <p style="overflow:auto; float:none">
   Always ask yourself this question: "How much code will a maintenance programmer need to look at to fix a bug with this feature?"
 </p>
</div>

<p>In this case, you cannot really understand how the <code class="language-plaintext highlighter-rouge">ListCell</code> works without looking at the instantiation of <code class="language-plaintext highlighter-rouge">ObservableList</code> to see that it has an extractor.  That’s very likely to be in another class altogether.</p>

<p>Some time back, before JavaFX 19, achieving this entirely within the <code class="language-plaintext highlighter-rouge">ListCell</code> would have been a bit tedious.  The <code class="language-plaintext highlighter-rouge">Labels</code> would need to be bound to fields inside a value that itself was changing.  This would have required unbinding and the rebinding the <code class="language-plaintext highlighter-rouge">Labels</code> each time the value in the <code class="language-plaintext highlighter-rouge">ListCell</code> was changed.</p>

<p>But JavaFX 19 introduced <code class="language-plaintext highlighter-rouge">ObservableValue.flatmap()</code>, and now we can skip all the unbinding and rebinding.</p>

<p>Here’s the <code class="language-plaintext highlighter-rouge">ListCell</code> implemented that way:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">cellFactory</span> <span class="p">=</span> <span class="nc">Callback</span> <span class="p">{</span> <span class="n">_</span> <span class="p">-&gt;</span>
    <span class="kd">object</span> <span class="err">: </span><span class="nc">ListCell</span><span class="p">&lt;</span><span class="nc">ExampleData</span><span class="p">&gt;()</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">label1</span> <span class="p">=</span> <span class="nc">Label</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="nf">itemProperty</span><span class="p">().</span><span class="nf">flatMap</span> <span class="p">{</span> <span class="n">item</span> <span class="p">-&gt;</span> <span class="n">item</span><span class="p">.</span><span class="n">value1</span><span class="p">.</span><span class="nf">asString</span><span class="p">()</span> <span class="p">})</span>
        <span class="p">}</span>
        <span class="kd">val</span> <span class="py">label2</span> <span class="p">=</span> <span class="nc">Label</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="nf">itemProperty</span><span class="p">().</span><span class="nf">flatMap</span> <span class="p">{</span> <span class="n">item</span> <span class="p">-&gt;</span> <span class="n">item</span><span class="p">.</span><span class="n">value2</span><span class="p">.</span><span class="nf">asString</span><span class="p">()</span> <span class="p">})</span>
        <span class="p">}</span>
        <span class="kd">val</span> <span class="py">layout</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">2.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">,</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Value 1:"</span><span class="p">),</span> <span class="n">label1</span><span class="p">)</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">,</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Value 2:"</span><span class="p">),</span> <span class="n">label2</span><span class="p">)</span>
            <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">8.0</span><span class="p">)</span>
        <span class="p">}</span>

        <span class="nf">init</span> <span class="p">{</span>
            <span class="nf">graphicProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span>
                <span class="nc">Bindings</span><span class="p">.</span><span class="n">createObjectBinding</span><span class="p">&lt;</span><span class="nc">Region</span><span class="p">&gt;(</span>
                    <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="n">item</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span> <span class="n">layout</span> <span class="k">else</span> <span class="k">null</span> <span class="p">},</span>
                    <span class="nf">itemProperty</span><span class="p">()</span>
                <span class="p">)</span>
            <span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">Labels</code> are bound to fields in <code class="language-plaintext highlighter-rouge">itemProperty()</code> through <code class="language-plaintext highlighter-rouge">flatMap()</code>.  Now, if the value in the field changes, or the <code class="language-plaintext highlighter-rouge">itemProperty()</code> itself changes, the binding will handle it automatically.  By also creating a <code class="language-plaintext highlighter-rouge">Binding</code> to control whether or not the <code class="language-plaintext highlighter-rouge">graphic</code> displays in the <code class="language-plaintext highlighter-rouge">init{}</code> block, we can now do away with <code class="language-plaintext highlighter-rouge">updateItem()</code> entirely.  Everything is simply dependent on <code class="language-plaintext highlighter-rouge">itemProperty()</code>.</p>

<p>In order to test this, I removed the extractor from the <code class="language-plaintext highlighter-rouge">ObservableList</code>.  The result looks like this:</p>

<p><img src="/assets/elements/Extractors13.png" alt="Screen Snap" /></p>

<p>You can see that the items in the <code class="language-plaintext highlighter-rouge">ListView</code> are still correct, and the messages show the <code class="language-plaintext highlighter-rouge">Button</code> clicks that do <strong>not</strong> trigger an “Invalidated” message from the <code class="language-plaintext highlighter-rouge">Listener</code>.</p>

<p>Obviously, this requires a somewhat deeper understanding of the <code class="language-plaintext highlighter-rouge">ListCell</code> mechanics.  Most beginners aren’t even aware that <code class="language-plaintext highlighter-rouge">item</code> exists in the <code class="language-plaintext highlighter-rouge">ListCell</code> as on <code class="language-plaintext highlighter-rouge">ObjectProperty</code>, and they assume that the only way to deal with a changing <code class="language-plaintext highlighter-rouge">item</code> is through <code class="language-plaintext highlighter-rouge">updateItem()</code>.</p>

<h1 id="what-about-performance">What About Performance?</h1>

<p>You may be thinking, “What if I have an <code class="language-plaintext highlighter-rouge">ObservableList</code> with thousands, and thousands of items?  Won’t that mean thousands and thousands of <code class="language-plaintext highlighter-rouge">Listeners</code> running all the time?”</p>

<p>For sure, there is some overhead to <code class="language-plaintext highlighter-rouge">Listeners</code>, but it’s not as much as you might think.  Let’s look at what happens when you add an <code class="language-plaintext highlighter-rouge">InvalidationListener</code> to an <code class="language-plaintext highlighter-rouge">Observable</code>…</p>

<p>To find this you need to look at <code class="language-plaintext highlighter-rouge">ObjectPropertyBase</code> which is the highest abstract class that implements <code class="language-plaintext highlighter-rouge">addListener()</code> for <code class="language-plaintext highlighter-rouge">Properties</code>.  In it, you’ll find that it delegates to something called <code class="language-plaintext highlighter-rouge">ExpressionHelper</code> which is an instance variable of <code class="language-plaintext highlighter-rouge">ObjectPropertyBase</code>, and then that ends up in a subclass of <code class="language-plaintext highlighter-rouge">ExpressionHelper</code> called <code class="language-plaintext highlighter-rouge">Generic</code>.  In there, you will find this:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protected</span> <span class="nc">Generic</span><span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;</span> <span class="nf">addListener</span><span class="o">(</span><span class="nc">InvalidationListener</span> <span class="n">listener</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">invalidationListeners</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">invalidationListeners</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">InvalidationListener</span><span class="o">[]</span> <span class="o">{</span><span class="n">listener</span><span class="o">};</span>
        <span class="n">invalidationSize</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span>
    <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
        <span class="kd">final</span> <span class="kt">int</span> <span class="n">oldCapacity</span> <span class="o">=</span> <span class="n">invalidationListeners</span><span class="o">.</span><span class="na">length</span><span class="o">;</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">locked</span><span class="o">)</span> <span class="o">{</span>
            <span class="kd">final</span> <span class="kt">int</span> <span class="n">newCapacity</span> <span class="o">=</span> <span class="o">(</span><span class="n">invalidationSize</span> <span class="o">&lt;</span> <span class="n">oldCapacity</span><span class="o">)?</span> <span class="n">oldCapacity</span> <span class="o">:</span> <span class="o">(</span><span class="n">oldCapacity</span> <span class="o">*</span> <span class="mi">3</span><span class="o">)/</span><span class="mi">2</span> <span class="o">+</span> <span class="mi">1</span><span class="o">;</span>
            <span class="n">invalidationListeners</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">copyOf</span><span class="o">(</span><span class="n">invalidationListeners</span><span class="o">,</span> <span class="n">newCapacity</span><span class="o">);</span>
        <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">invalidationSize</span> <span class="o">==</span> <span class="n">oldCapacity</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">invalidationSize</span> <span class="o">=</span> <span class="n">trim</span><span class="o">(</span><span class="n">invalidationSize</span><span class="o">,</span> <span class="n">invalidationListeners</span><span class="o">);</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">invalidationSize</span> <span class="o">==</span> <span class="n">oldCapacity</span><span class="o">)</span> <span class="o">{</span>
                <span class="kd">final</span> <span class="kt">int</span> <span class="n">newCapacity</span> <span class="o">=</span> <span class="o">(</span><span class="n">oldCapacity</span> <span class="o">*</span> <span class="mi">3</span><span class="o">)/</span><span class="mi">2</span> <span class="o">+</span> <span class="mi">1</span><span class="o">;</span>
                <span class="n">invalidationListeners</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">copyOf</span><span class="o">(</span><span class="n">invalidationListeners</span><span class="o">,</span> <span class="n">newCapacity</span><span class="o">);</span>
            <span class="o">}</span>
        <span class="o">}</span>
        <span class="n">invalidationListeners</span><span class="o">[</span><span class="n">invalidationSize</span><span class="o">++]</span> <span class="o">=</span> <span class="n">listener</span><span class="o">;</span>
    <span class="o">}</span>
    <span class="k">return</span> <span class="k">this</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Yikes!</p>

<p>In summary, it has an array of <code class="language-plaintext highlighter-rouge">InvalidationListeners</code> and it adds your new <code class="language-plaintext highlighter-rouge">InvalidationListener</code> to it.  The remaining dozen or so lines of appear to be some kind of memory management optimization.  That’s good to see, if you are worried about performance degredation associated with extractors.</p>

<p>So, this means that every <code class="language-plaintext highlighter-rouge">Observable</code> potentially contains an <code class="language-plaintext highlighter-rouge">Array</code> that holds all of the <code class="language-plaintext highlighter-rouge">InvalidationListeners</code> that have been added to it.</p>

<p>How does it use this?</p>

<p>In <code class="language-plaintext highlighter-rouge">ObjectPropertyBase</code> we have this:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">set</span><span class="o">(</span><span class="no">T</span> <span class="n">newValue</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">isBound</span><span class="o">())</span> <span class="o">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">RuntimeException</span><span class="o">((</span><span class="n">getBean</span><span class="o">()</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">getName</span><span class="o">()</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">?</span>
                <span class="n">getBean</span><span class="o">().</span><span class="na">getClass</span><span class="o">().</span><span class="na">getSimpleName</span><span class="o">()</span> <span class="o">+</span> <span class="s">"."</span> <span class="o">+</span> <span class="n">getName</span><span class="o">()</span> <span class="o">+</span> <span class="s">" : "</span><span class="o">:</span> <span class="s">""</span><span class="o">)</span> <span class="o">+</span> <span class="s">"A bound value cannot be set."</span><span class="o">);</span>
    <span class="o">}</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">value</span> <span class="o">!=</span> <span class="n">newValue</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">value</span> <span class="o">=</span> <span class="n">newValue</span><span class="o">;</span>
        <span class="n">markInvalid</span><span class="o">();</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The most important part for us is the call to <code class="language-plaintext highlighter-rouge">markInvalid()</code>:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protected</span> <span class="kt">void</span> <span class="nf">fireValueChangedEvent</span><span class="o">()</span> <span class="o">{</span>
    <span class="nc">ExpressionHelper</span><span class="o">.</span><span class="na">fireValueChangedEvent</span><span class="o">(</span><span class="n">helper</span><span class="o">);</span>
<span class="o">}</span>

<span class="kd">private</span> <span class="kt">void</span> <span class="nf">markInvalid</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">valid</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">valid</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span>
        <span class="n">invalidated</span><span class="o">();</span>
        <span class="n">fireValueChangedEvent</span><span class="o">();</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>And we are back to <code class="language-plaintext highlighter-rouge">ExpressionHelper</code> again, and then back into <code class="language-plaintext highlighter-rouge">Generic</code> for the actual implementation:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">curInvalidationSize</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
    <span class="k">try</span> <span class="o">{</span>
        <span class="n">curInvalidationList</span><span class="o">[</span><span class="n">i</span><span class="o">].</span><span class="na">invalidated</span><span class="o">(</span><span class="n">observable</span><span class="o">);</span>
    <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">Thread</span><span class="o">.</span><span class="na">currentThread</span><span class="o">().</span><span class="na">getUncaughtExceptionHandler</span><span class="o">().</span><span class="na">uncaughtException</span><span class="o">(</span><span class="nc">Thread</span><span class="o">.</span><span class="na">currentThread</span><span class="o">(),</span> <span class="n">e</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>This is just a snippet of the code for this method, but you can see how it works.  It loops through the <code class="language-plaintext highlighter-rouge">Array</code> of <code class="language-plaintext highlighter-rouge">InvalidationListeners</code> and invokes <code class="language-plaintext highlighter-rouge">invalidated()</code> on each one.  Remember that <code class="language-plaintext highlighter-rouge">InvalidationListener</code> is a Functional Interface with one method, <code class="language-plaintext highlighter-rouge">invalidated()</code>.</p>

<p>If you were picturing an implementation where JavaFX had some master list of <code class="language-plaintext highlighter-rouge">Listeners</code> and it had to check them all every time something changed, then you can see that this is backwards to that.  Each <code class="language-plaintext highlighter-rouge">Observable</code> maintains its own list of <code class="language-plaintext highlighter-rouge">Listeners</code> that have been registered with it.  Then when its value is updated, it triggers each of those <code class="language-plaintext highlighter-rouge">InvalidationListeners</code>.</p>

<p>If there is a potential performance hit, it would be from having many <code class="language-plaintext highlighter-rouge">Listeners</code> on each <code class="language-plaintext highlighter-rouge">Observable</code> item.  Not from having many <code class="language-plaintext highlighter-rouge">Observable</code> items with one or two <code class="language-plaintext highlighter-rouge">Listeners</code> each.</p>

<h1 id="conclusion">Conclusion</h1>

<p>Creating an <code class="language-plaintext highlighter-rouge">ObservableList</code> with an extractor causes just two things to happen:</p>

<ol>
  <li>The <code class="language-plaintext highlighter-rouge">ObservableList</code> will invalidate, and fire any <code class="language-plaintext highlighter-rouge">InvalidationListeners</code> on it when any <code class="language-plaintext highlighter-rouge">Observable</code> returned from the extractor invalidates.</li>
  <li>If you wrap the <code class="language-plaintext highlighter-rouge">ObservableList</code> in a <code class="language-plaintext highlighter-rouge">ListProperty</code> that <code class="language-plaintext highlighter-rouge">ListProperty</code> will both invalidate and fire and <code class="language-plaintext highlighter-rouge">ChangeListeners</code> on it when any <code class="language-plaintext highlighter-rouge">Observable</code> return from the extractor invalidates.</li>
</ol>

<p>Invalidation is <strong>the</strong> key concept on <code class="language-plaintext highlighter-rouge">Bindings</code>.  This is because <code class="language-plaintext highlighter-rouge">Bindings</code> always recalculate by calling their <code class="language-plaintext highlighter-rouge">computeValue()</code> method when any of their dependencies invalidates.</p>

<p>This means that you can create <code class="language-plaintext highlighter-rouge">Bindings</code> on <code class="language-plaintext highlighter-rouge">ObservableLists</code> that will re-evaluate not just when items in the <code class="language-plaintext highlighter-rouge">ObservableList</code> are added, removed or replaced, but when internal values in those <code class="language-plaintext highlighter-rouge">ObservableList</code> items change…if you add the appropriate extractor.</p>]]></content><author><name>Dave Barrett</name><email>pragmaticcoding8@gmail.com</email></author><category term="javafx" /><summary type="html"><![CDATA[ObservableList extractors allow you to track changes to elements inside your ObservableLists.]]></summary></entry><entry><title type="html">Converting FXML to Code</title><link href="https://www.pragmaticcoding.ca/javafx/elements/fxml-to-code" rel="alternate" type="text/html" title="Converting FXML to Code" /><published>2025-07-22T17:00:00+00:00</published><updated>2025-07-22T17:00:00+00:00</updated><id>https://www.pragmaticcoding.ca/javafx/elements/fxml-to-code</id><content type="html" xml:base="https://www.pragmaticcoding.ca/javafx/elements/fxml-to-code"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>Recently, I posted an article titled, <a href="/javafx/fxml-or-not">Should You Use FXML?</a>.  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 “is this better than this?”, exploration.</p>

<p>In 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 “bad”, 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.</p>

<p>I wasn’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’t matter to me if the FXML example that I picked was good, bad or average.</p>

<p>It didn’t matter because I understand that the way that I write layout code is astronomically better than FXML can ever be.</p>

<p>But I couldn’t say that.  Who would believe me?  What makes me think my coding is so much better?</p>

<p>This article is my attempt to explain my position.  I’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.</p>

<p>My 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.</p>

<p>And this is not about <em>me</em> or <em>my</em> coding skill.  There’s nothing in my version that anyone reading this article couldn’t do themselves, or learn to do themselves.</p>

<h1 id="approach">Approach</h1>

<p>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:</p>

<dl>
  <dt>Improving the Layout</dt>
  <dd>
    <p>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’t arise coding the layout by hand.</p>
  </dd>
</dl>

<div class="notice--primary">
 <img src="/assets/logos/brain.png" alt="primary" style="float:left;margin-right: 10px" />
 <p style="overflow:auto; float:none">
   This shows that the impact of hand-coding goes beyond being simply a matter of clarity and maintainability, but also affects the application design.
 </p>
</div>

<dl>
  <dt>Implementing a Framework</dt>
  <dd>
    <p>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.</p>
  </dd>
  <dt>Changing to a Reactive Design</dt>
  <dd>
    <p>It’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.</p>
  </dd>
  <dt>Threading and the FXAT</dt>
  <dd>
    <p>The FXML Controller doesn’t have any code that attempts to perform potentially blocking (like file access) operations off the FXAT.  There’s no point in creating bad code for conversion, so I’ve organized the code such that thread handling is performed as it should be.</p>
  </dd>
  <dt>WidgetsFX</dt>
  <dd>
    <p>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’m not including any of that code here, but you can easily tell what it would do.</p>
  </dd>
</dl>

<p class="notice--primary">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’s also how the WidgetsFX library grows organically over time.</p>

<dl>
  <dt>Styling</dt>
  <dd>
    <p>I’ve chosen to move any styling in the FXML or the FXML Controller into an external style sheet.  Then I’m not going to create that style sheet because…why bother.  The result is that the screens in my project, while having an identical effective layout, don’t look like the originals…but that’s not the point.</p>
  </dd>
</dl>

<h2 id="kotlin">Kotlin</h2>

<p>I’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.</p>

<p>I think that, even if you don’t fully understand the syntax, the Kotlin code is easy enough to understand for most Java programmers.  Take a look at this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">VBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
  <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">25.0</span><span class="p">)</span>
    <span class="p">.</span>
    <span class="p">.</span>
    <span class="p">.</span>
  <span class="n">children</span> <span class="p">+=</span> <span class="nf">intSpinnerOf</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="s">"standard-spinner"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">connectorThickness</span><span class="p">.</span><span class="nf">asObject</span><span class="p">())</span>
        <span class="p">.</span><span class="nf">setStep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">bindDisable</span><span class="p">(</span><span class="n">model</span><span class="p">.</span><span class="n">selectedDistance</span><span class="p">.</span><span class="n">isNull</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">withInitialDelay</span><span class="p">(</span><span class="nc">Duration</span><span class="p">.</span><span class="nf">millis</span><span class="p">(</span><span class="mf">500.0</span><span class="p">))</span>
        <span class="p">.</span><span class="nf">withRepeatDelay</span><span class="p">(</span><span class="nc">Duration</span><span class="p">.</span><span class="nf">millis</span><span class="p">(</span><span class="mf">500.0</span><span class="p">))</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>You don’t really need to know the details of how the extension function <code class="language-plaintext highlighter-rouge">.apply{}</code> works to understand that the code inside the <code class="language-plaintext highlighter-rouge">{}</code> configures the <code class="language-plaintext highlighter-rouge">VBox</code>.  You can see that the padding is set to 25px, even though you don’t understand that Kotlin allows you to refer directly to values of fields that have <code class="language-plaintext highlighter-rouge">getters</code> and <code class="language-plaintext highlighter-rouge">setters</code> and that it will still call those <code class="language-plaintext highlighter-rouge">getters</code> and <code class="language-plaintext highlighter-rouge">setters</code>.  Then, <code class="language-plaintext highlighter-rouge">children += </code> is clearly using the <code class="language-plaintext highlighter-rouge">+=</code> operator on a <code class="language-plaintext highlighter-rouge">List</code> to do the equivalent of <code class="language-plaintext highlighter-rouge">getChildren().add()</code>.</p>

<p>It’s also clear that <code class="language-plaintext highlighter-rouge">setStep()</code>, <code class="language-plaintext highlighter-rouge">bindDisable()</code>, <code class="language-plaintext highlighter-rouge">withInitialDelay()</code> and <code class="language-plaintext highlighter-rouge">withRepeatDelay()</code> are all configuration methods for the <code class="language-plaintext highlighter-rouge">Spinner</code>.  Since they are chained, all of these methods are designed as decorators.</p>

<p>It’s obviously not clear what the parameters of <code class="language-plaintext highlighter-rouge">intSpinnerOf()</code> 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.</p>

<p>For purposes of this article, pretty much any Java programmer can look at this snippet of code and understand that it defines a <code class="language-plaintext highlighter-rouge">VBox</code> with a particular padding and that it contains a <code class="language-plaintext highlighter-rouge">Spinner&lt;Integer&gt;</code> that has been configured in a particular way and bound to some <code class="language-plaintext highlighter-rouge">Property</code> in the Model.  That’s probably enough to get the point.</p>

<h1 id="the-original-version">The Original Version</h1>

<p>You can find this project on GitHub <a href="https://github.com/trinity-xai/Trinity">here</a>.</p>

<p>I 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 <a href="/pages/trinity-source">article</a>.  I’m not going to include all the code in this article itself, because it’s just going to be too big.</p>

<p>When 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’t quite match what the code does:</p>

<p><img src="/assets/images/TrinityScreenShot.png" alt="Trinity Screenshot" /></p>

<p>The code we are working on is the control panel in the upper left corner, so here it is close-up:</p>

<p><img src="/assets/images/TrinityScreenShot1.png" alt="Trinty Control Panel" /></p>

<p>Unfortunately, we don’t have screen shots of all of the <code class="language-plaintext highlighter-rouge">Tabs</code> in the control panel, but you can get a sense of the design from this.</p>

<p>There’s obviously some pretty strong styling going on here, it looks like maybe they used AtlantaFX?  I didn’t go looking, and I’m not going to make any attempt to duplicate it.  There is zero code or FXML that applies any of this styling, so I’m deeming it “out of scope”.</p>

<h1 id="notes-while-performing-the-conversion">Notes While Performing the Conversion</h1>

<p>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…</p>

<h2 id="this-was-probably-generated-from-scenebuilder">This Was Probably Generated From SceneBuilder</h2>

<p>I came across this in the FXML file:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;HBox</span> <span class="na">alignment=</span><span class="s">"CENTER"</span> <span class="na">spacing=</span><span class="s">"10.0"</span> <span class="na">GridPane.columnSpan=</span><span class="s">"2147483647"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"8"</span><span class="nt">&gt;</span>
</code></pre></div></div>
<p>I cannot see any human entering “2147483647” as a <code class="language-plaintext highlighter-rouge">columnSpan</code> value.  So this is either generated from SceneBuilder, or copypasta from some section that <em>was</em> generated by SceneBuilder.</p>

<p>Why 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’s an issue.</p>

<h2 id="the-design-is-difficult-to-understand">The Design is Difficult to Understand</h2>

<p>It’s monolithic.  462 lines of FXML, and you need to scan all of it to understand that it’s basically just 4 <code class="language-plaintext highlighter-rouge">Tabs</code> in a <code class="language-plaintext highlighter-rouge">TabPane</code>.  The <code class="language-plaintext highlighter-rouge">GridPanes</code> 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.</p>

<div class="notice--primary">
 <img src="/assets/logos/brain.png" alt="primary" style="float:left;margin-right: 10px" />
 <p style="overflow:auto; float:none">
   All in all, I spent more time trying to understand the FXML and FXML Controller than I did writing my own version.
 </p>
</div>

<h2 id="bad-fxml-doesnt-appear-be-more-verbose">“Bad” FXML Doesn’t Appear Be More Verbose</h2>

<p>It has become clear that you wouldn’t write FXML like this by hand.  It’s just too jumbled up for that, and probably crosses into the realm of “bad” FXML.  However, there really isn’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 “row” or “column” specifications for <code class="language-plaintext highlighter-rouge">GridPane</code> elements in row or column 0 - that make it a bit less verbose.</p>

<p>The 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.</p>

<h2 id="dealing-with-filechooser">Dealing With FileChooser</h2>

<p>In most of my programming, I don’t use <code class="language-plaintext highlighter-rouge">FileChooser</code> much at all, but this project and this screen do.  So I had to grapple with the question of “Where does it go in the MVCI structure?”.  It can be argued that <code class="language-plaintext highlighter-rouge">FileChooser</code> is a GUI element, and therefore belongs to the View, and that’s were I initially placed it.  But then I ended up with references to <code class="language-plaintext highlighter-rouge">File</code> objects, and that caused me to look closer at this approach.</p>

<p>First off, <code class="language-plaintext highlighter-rouge">File</code> is most distinctly <strong>not</strong> a front end class.  It seemed worrying to have this data type handled by the GUI code.</p>

<p>Secondly, the <code class="language-plaintext highlighter-rouge">File</code> had to be passed back to the Interactor somehow, because it’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 <em>anything</em> about the back-end structure.  Furthermore, the View doesn’t actually <em>use</em> the <code class="language-plaintext highlighter-rouge">File</code> object, it just passes it back to the Controller.</p>

<p>Now, 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?</p>

<p>For sure, that <code class="language-plaintext highlighter-rouge">FileChooser</code> would need to change.  Maybe it becomes a <code class="language-plaintext highlighter-rouge">Dialog</code> that allows for load and save to a database, or to select an email with attachments (or to send an email).  But there’s no reason that you should have to change the View for that.</p>

<p>In the end, I moved the <code class="language-plaintext highlighter-rouge">FileChooser</code> calls into the Controller, which is where I think they really belong.  It’s now integrated with the thread handling and invocation of Interactor methods.  The <code class="language-plaintext highlighter-rouge">File</code> object is no longer tramp data passing from the View through the Controller to the Interactor.</p>

<h2 id="unused-elements">Unused? Elements</h2>

<p>In the <code class="language-plaintext highlighter-rouge">Tab</code> labeled “PCA” there are two <code class="language-plaintext highlighter-rouge">RadioButtons</code>, “Use Hyperspace” and “Use Hypersurface” that do <strong>NOT</strong> have their <code class="language-plaintext highlighter-rouge">isSelected</code> values used in any code in the FXML Controller.</p>

<p>However, the <code class="language-plaintext highlighter-rouge">ToggleGroup</code> to which they belong, <code class="language-plaintext highlighter-rouge">pcahyperSourceGroup</code> is <strong>NOT</strong> private to the FXML Controller.  So it is possible that some other class with a reference to this FXML Controller is accessing this <code class="language-plaintext highlighter-rouge">ToggleGroup</code>, getting a reference to the <code class="language-plaintext highlighter-rouge">RadioButton</code> that is selected, and (Gasp!!!) checking it’s <code class="language-plaintext highlighter-rouge">text</code> value to find out which <code class="language-plaintext highlighter-rouge">RadioButton</code> it is.</p>

<p>Don’t try this at home.</p>

<p>Needless to say, I’m not sure if these <code class="language-plaintext highlighter-rouge">RadioButtons</code> do or do not do anything - and that should be immediately apparent from reading the code.  I’ve connected these <code class="language-plaintext highlighter-rouge">RadioButtons</code> to some elements in the Model that are clearly named as some kind of “dummy” value.</p>

<p>It may be that I grabbed the code in the middle of development and it just wasn’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.</p>

<h2 id="connecting-to-the-rest-of-the-application">Connecting to the Rest of the Application</h2>

<p>When you look at the screen snaps on the project Read.me page, it’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.</p>

<p>We see that the original code implements this communication through <code class="language-plaintext highlighter-rouge">EventHandlers</code>, like this:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">scene</span><span class="o">.</span><span class="na">addEventHandler</span><span class="o">(</span><span class="nc">ManifoldEvent</span><span class="o">.</span><span class="na">DISTANCE_CONNECTOR_SELECTED</span><span class="o">,</span> <span class="n">e</span> <span class="o">-&gt;</span> <span class="o">{</span>   <span class="o">}</span>
</code></pre></div></div>
<p>for incoming <code class="language-plaintext highlighter-rouge">Events</code>, and this:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">scene</span><span class="o">.</span><span class="na">getRoot</span><span class="o">().</span><span class="na">fireEvent</span><span class="o">(</span><span class="k">new</span> <span class="nc">ManifoldEvent</span><span class="o">(</span><span class="nc">ManifoldEvent</span><span class="o">.</span><span class="na">DISTANCE_CONNECTOR_WIDTH</span><span class="o">,</span> <span class="n">item</span><span class="o">.</span><span class="na">getDistance</span><span class="o">()));</span>
</code></pre></div></div>
<p>for outgoing <code class="language-plaintext highlighter-rouge">Events</code>.</p>

<p>This is probably one of the most insidious issues that can occur when you forego implementing a proper framework.  The code you are writing is <code class="language-plaintext highlighter-rouge">Event</code> centric, so then <em>everything</em> is now an <code class="language-plaintext highlighter-rouge">Event</code>.  Even when it really isn’t.  I’ll discuss this more when we look at the new design.</p>

<h1 id="changes-to-the-design">Changes to the Design</h1>

<p>It was clear when doing the conversion that there were “issues” with the original design.  Furthermore, it was clear that it would not make sense to leave these issues unaddressed when performing the conversion.</p>

<h2 id="issues-with-the-original-design">Issues With the Original Design</h2>

<p>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’s take a look at them:</p>

<h3 id="there-is-no-framework">There is no Framework</h3>

<p>The original implementation assumed that the FXML file was the “View” and the FXML Controller was the “Controller” and the domain objects collectively comprised the “Model”.  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.</p>

<p>This is, in my opinion, a big problem with the design of this application, and this clearly shows how the automatic “separation of concerns” 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 <code class="language-plaintext highlighter-rouge">Nodes</code>.  Within the layout itself, you have extensive coupling of <code class="language-plaintext highlighter-rouge">Nodes</code> all over the layout.</p>

<h3 id="there-is-no-presentation-model">There is no Presentation Model</h3>

<p>All of the data is stored in the screen <code class="language-plaintext highlighter-rouge">Nodes</code>, and there is no external data storage outside of the value <code class="language-plaintext highlighter-rouge">Properties</code> of the <code class="language-plaintext highlighter-rouge">Nodes</code> themselves.  This means that all of the data must to be scraped out of the <code class="language-plaintext highlighter-rouge">Nodes</code> when it’s needed, which, in turn,  means that all of the <code class="language-plaintext highlighter-rouge">Node</code> 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.</p>

<p>Of course, the design of FXML requires that all of the <code class="language-plaintext highlighter-rouge">Nodes</code> 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.</p>

<p>Finally, without a Presentation Model, it’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.</p>

<h3 id="it-is-an-action-based-design">It is an “Action” Based Design</h3>

<p>Without a Presentation Model to act as a data representation of the “State” 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.</p>

<p>The effect of this is that the application becames <code class="language-plaintext highlighter-rouge">Node</code>-centric, instead of data centric, and this shift, in turn, leads to an “Action” based design.</p>

<h3 id="using-events-to-communicate-between-screens">Using <code class="language-plaintext highlighter-rouge">Events</code> to Communicate Between Screens</h3>

<p>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 <code class="language-plaintext highlighter-rouge">Events</code> to do this.  Consider this snippet of code:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">connectorThicknessSpinner</span><span class="o">.</span><span class="na">valueProperty</span><span class="o">().</span><span class="na">addListener</span><span class="o">(</span><span class="n">e</span> <span class="o">-&gt;</span> <span class="o">{</span>
    <span class="nc">DistanceListItem</span> <span class="n">item</span> <span class="o">=</span> <span class="n">distancesListView</span><span class="o">.</span><span class="na">getSelectionModel</span><span class="o">().</span><span class="na">getSelectedItem</span><span class="o">();</span>
    <span class="k">if</span> <span class="o">(</span><span class="kc">null</span> <span class="o">!=</span> <span class="n">item</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">Integer</span> <span class="n">width</span> <span class="o">=</span> <span class="o">(</span><span class="nc">Integer</span><span class="o">)</span> <span class="n">connectorThicknessSpinner</span><span class="o">.</span><span class="na">getValue</span><span class="o">();</span>
        <span class="n">item</span><span class="o">.</span><span class="na">getDistance</span><span class="o">().</span><span class="na">setWidth</span><span class="o">(</span><span class="n">width</span><span class="o">);</span>
        <span class="n">scene</span><span class="o">.</span><span class="na">getRoot</span><span class="o">().</span><span class="na">fireEvent</span><span class="o">(</span>
            <span class="k">new</span> <span class="nf">ManifoldEvent</span><span class="o">(</span><span class="nc">ManifoldEvent</span><span class="o">.</span><span class="na">DISTANCE_CONNECTOR_WIDTH</span><span class="o">,</span> <span class="n">item</span><span class="o">.</span><span class="na">getDistance</span><span class="o">()));</span>
    <span class="o">}</span>
<span class="o">});</span>
</code></pre></div></div>
<p>Here we have a <code class="language-plaintext highlighter-rouge">Spinner</code> configured such that every time its value changes a <code class="language-plaintext highlighter-rouge">Listener</code> is triggered and that <code class="language-plaintext highlighter-rouge">Listener</code> will eventually fire a <code class="language-plaintext highlighter-rouge">ManifoldEvent</code> of a certain type, in this case <code class="language-plaintext highlighter-rouge">ManifoldEvent.DISTANCE_CONNECTOR_WIDTH</code> type.</p>

<p>Presumably, some external <code class="language-plaintext highlighter-rouge">EventHandler</code> is configured to respond to this <code class="language-plaintext highlighter-rouge">ManifoldEvent</code> and do something.</p>

<p>On the surface, this seems like a reasonable approach:  The programmers needed a reliable messaging facility to communicate between components, and the JavaFX <code class="language-plaintext highlighter-rouge">Event</code> system seems to fit the bill.  But there are some problems with this:</p>

<ol>
  <li>
    <p>Sequencing can be a problem.  <code class="language-plaintext highlighter-rouge">Events</code> invoke <code class="language-plaintext highlighter-rouge">EventHandlers</code> which are submitted to the FXAT.  If a system is very active, it’s possible that there’s a lot of items in the FXAT queue, and they may have changed the environment in which the <code class="language-plaintext highlighter-rouge">EventHandler</code> will run.  You cannot control this.</p>
  </li>
  <li>
    <p>It deals in domain objects.  That call to <code class="language-plaintext highlighter-rouge">item.getDistance()</code> returns a <code class="language-plaintext highlighter-rouge">Distance</code> which is a domain object.</p>
  </li>
  <li>
    <p>Related to the previous item, the domain objects are stored in singleton <code class="language-plaintext highlighter-rouge">Lists</code> in <code class="language-plaintext highlighter-rouge">Distance</code> and <code class="language-plaintext highlighter-rouge">Manifold</code>.  This leads to timing issues where domain objects might exist in the singleton lists but not in the <code class="language-plaintext highlighter-rouge">ListViews</code> yet.  There is code that defends against this and generates errors when this happens.</p>
  </li>
  <li>
    <p>Delivery is <strong>not</strong> guaranteed.  There’s nothing to say that some screen element couldn’t filter an <code class="language-plaintext highlighter-rouge">Event</code> before it gets to its intended target.  This might be hard to debug. Essentially, the <code class="language-plaintext highlighter-rouge">Event</code> system is global and couples everything that uses it.</p>
  </li>
  <li>
    <p>It’s not clear who the recipient is.  <code class="language-plaintext highlighter-rouge">Events</code> are, by definition, broadcast entities.  To find out how this <code class="language-plaintext highlighter-rouge">Event</code> is handled, you’ll have to search through the entire application to see which classes  have an <code class="language-plaintext highlighter-rouge">EventHandler</code> for the <code class="language-plaintext highlighter-rouge">ManifoldEvent.DISTANCE_CONNECTOR_WIDTH</code> subtype.</p>
  </li>
</ol>

<p>Personally, I think that <code class="language-plaintext highlighter-rouge">Events</code> and <code class="language-plaintext highlighter-rouge">EventHandlers</code> are best used for very localized things.  This would mean adding an <code class="language-plaintext highlighter-rouge">EventHandler</code> onto the <code class="language-plaintext highlighter-rouge">Button</code> that generates the <code class="language-plaintext highlighter-rouge">Event</code> (like a click <code class="language-plaintext highlighter-rouge">ActionEvent</code>).  Using the JavaFX <code class="language-plaintext highlighter-rouge">Event</code> system as a general communication bus feels like a “code smell” to me.</p>

<h3 id="the-listviews">The ListViews</h3>

<p>This is the only part of the design which is objectively “wrong” from a technical JavaFX respect.</p>

<p>There are two classes for each <code class="language-plaintext highlighter-rouge">ListView</code>, let’s look at the one for manifolds.  We have the class <code class="language-plaintext highlighter-rouge">ManifoldListItem</code> which extends <code class="language-plaintext highlighter-rouge">VBox</code>, and the class <code class="language-plaintext highlighter-rouge">Manifold</code> which we should probably consider to be a “Domain Object”.  Every <code class="language-plaintext highlighter-rouge">ManifoldListItem</code> contains a reference to a <code class="language-plaintext highlighter-rouge">Manifold</code>.</p>

<p>The biggest problem is that the <code class="language-plaintext highlighter-rouge">ListView</code> is defined as <code class="language-plaintext highlighter-rouge">ListView&lt;ManifoldListItem&gt;</code> and then has no <code class="language-plaintext highlighter-rouge">ListCell</code> defined anywhere.  This violates one of the principal rules of <code class="language-plaintext highlighter-rouge">ListView</code> and <code class="language-plaintext highlighter-rouge">TableView</code>:  Do <strong>NOT</strong> put <code class="language-plaintext highlighter-rouge">Nodes</code> as the items.</p>

<p>I don’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.</p>

<p>The <code class="language-plaintext highlighter-rouge">Manifold</code> class has a static <code class="language-plaintext highlighter-rouge">HashMap</code> field that stores all of the <code class="language-plaintext highlighter-rouge">Manifolds</code> that have been created - essentially a singleton that can be accessed through the whole application.  Whenever a new <code class="language-plaintext highlighter-rouge">Manifold</code> is created, there should also be an <code class="language-plaintext highlighter-rouge">ManifoldEvent</code> fired that should trigger an <code class="language-plaintext highlighter-rouge">EventHandler</code> added to the <code class="language-plaintext highlighter-rouge">Scene</code>.  That <code class="language-plaintext highlighter-rouge">EventHandler</code>creates a new <code class="language-plaintext highlighter-rouge">ManifoldListItem</code> and adds it to the <code class="language-plaintext highlighter-rouge">ListView</code>.</p>

<p>If the manifold <code class="language-plaintext highlighter-rouge">HashMap</code> is cleared, there should be an accompanying <code class="language-plaintext highlighter-rouge">ManifoldEvent</code> fired which trigger a different <code class="language-plaintext highlighter-rouge">EventHandler</code> set on the <code class="language-plaintext highlighter-rouge">Scene</code>.</p>

<p>There are three <code class="language-plaintext highlighter-rouge">ColorPickers</code> outside the <code class="language-plaintext highlighter-rouge">ListView</code> that display colours associated with the item selected in the <code class="language-plaintext highlighter-rouge">ListView</code>.  There is code that updates the value in these <code class="language-plaintext highlighter-rouge">ColorPickers</code> when an <code class="language-plaintext highlighter-rouge">ManifoldEvent</code> indicating that a new <code class="language-plaintext highlighter-rouge">Manifold</code> has been selected.  Also, when the value in the <code class="language-plaintext highlighter-rouge">Spinners</code> is changed, it fires a <code class="language-plaintext highlighter-rouge">ManifoldEvent</code> that presumably gets handled externally and updates the <code class="language-plaintext highlighter-rouge">Manifold</code>.</p>

<p>If all of this seems very confusing and roundabout, that’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’ll see how this works in the next section.</p>

<h2 id="the-new-design">The New Design</h2>

<p>The new version uses <a href="/javafx/mvci/">MVCI</a> as a framework to implement a Reactive design.  There is a Presentation Model composed of <code class="language-plaintext highlighter-rouge">Observable</code> data classes which are then bound (mostly bi-directionally) to the value <code class="language-plaintext highlighter-rouge">Properties</code> of the screen <code class="language-plaintext highlighter-rouge">Nodes</code>.  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 <code class="language-plaintext highlighter-rouge">Properties</code> in the Presentation Model.</p>

<p>There is no need to “scrape” data out of the GUI <code class="language-plaintext highlighter-rouge">Nodes</code>.</p>

<p>All 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.</p>

<p>There is a <code class="language-plaintext highlighter-rouge">SharedModel</code> class which contains JavaFX <code class="language-plaintext highlighter-rouge">Observable</code> objects that are supplied by whatever element of the overall application contains this screen.</p>

<p>The Controller, as usual, handles instantiation, threading and connectivity to the rest of the application’s GUI.</p>

<h3 id="the-listviews-1">The ListViews</h3>

<p>This was the biggest architectural change to the design.  The <code class="language-plaintext highlighter-rouge">Manifold</code> and <code class="language-plaintext highlighter-rouge">Distance</code> domain objects were transformed into Presentation Objects and all of the singleton related stuff was removed.  Now they are simple JavaFX Observable POJO`s.</p>

<p>The <code class="language-plaintext highlighter-rouge">ListViews</code> were changed to have custom <code class="language-plaintext highlighter-rouge">ListCells</code> that mirrored the structure in the <code class="language-plaintext highlighter-rouge">ManifoldListItem</code> and <code class="language-plaintext highlighter-rouge">DistanceListItem</code> classes.  These <code class="language-plaintext highlighter-rouge">ListCells</code> contain interactive <code class="language-plaintext highlighter-rouge">Nodes</code> like <code class="language-plaintext highlighter-rouge">TextField</code> and <code class="language-plaintext highlighter-rouge">CheckBox</code> who’s values are bi-directionally bound to the corresponding <code class="language-plaintext highlighter-rouge">Properties</code> in the item currently loaded into the <code class="language-plaintext highlighter-rouge">ListCell</code>.</p>

<p>To handle the values inside <code class="language-plaintext highlighter-rouge">Manifold</code> and <code class="language-plaintext highlighter-rouge">Distance</code> that are updated from outside the <code class="language-plaintext highlighter-rouge">ListView</code>, the Model has two <code class="language-plaintext highlighter-rouge">Properties</code>:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">selectedDistance</span><span class="p">:</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">Distance</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">SimpleObjectProperty</span><span class="p">()</span>
<span class="kd">val</span> <span class="py">selectedManifold</span><span class="p">:</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">Manifold</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">SimpleObjectProperty</span><span class="p">()</span>
</code></pre></div></div>
<p>These need to be synchronized with the current selection in the <code class="language-plaintext highlighter-rouge">ListViews</code>.  Ordinarily you could do this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">infix</span> <span class="k">fun</span> <span class="p">&lt;</span><span class="nc">T</span> <span class="p">:</span> <span class="nc">Any</span><span class="p">&gt;</span> <span class="nf">ListView</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;.</span><span class="nf">bindSelection</span><span class="p">(</span><span class="n">boundProperty</span><span class="p">:</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;):</span> <span class="nc">ListView</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nf">apply</span> <span class="p">{</span>
    <span class="n">boundProperty</span><span class="p">.</span><span class="nf">bind</span><span class="p">(</span><span class="n">selectionModel</span><span class="p">.</span><span class="nf">selectedItemProperty</span><span class="p">())</span>
<span class="p">}</span>
</code></pre></div></div>
<p>But there’s a twist here…</p>

<p>The 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 <code class="language-plaintext highlighter-rouge">ListViews</code>.  Ordinarily, you’d just make the bind bi-directional, but there’s an issue there:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="nc">ReadOnlyObjectProperty</span><span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;</span> <span class="n">selectedItem</span>
</code></pre></div></div>
<p>Oh no!  The <code class="language-plaintext highlighter-rouge">selectedItem</code> property in <code class="language-plaintext highlighter-rouge">SelectionModel</code> is read-only!  The only way to change the value programmatically is to call <code class="language-plaintext highlighter-rouge">SelectionModel.selectItem()</code>.  This means we’ll have to use subscriptions to handle the changes:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">infix</span> <span class="k">fun</span> <span class="p">&lt;</span><span class="nc">T</span> <span class="p">:</span> <span class="nc">Any</span><span class="p">&gt;</span> <span class="nf">ListView</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;.</span><span class="nf">connectSelection</span><span class="p">(</span><span class="n">connectedProperty</span><span class="p">:</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;):</span> <span class="nc">ListView</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nf">apply</span> <span class="p">{</span>
    <span class="n">selectionModel</span><span class="p">.</span><span class="nf">selectedItemProperty</span><span class="p">().</span><span class="nf">subscribe</span> <span class="p">{</span> <span class="n">newItem</span> <span class="p">-&gt;</span> <span class="n">connectedProperty</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">newItem</span> <span class="p">}</span>
    <span class="n">connectedProperty</span><span class="p">.</span><span class="nf">subscribe</span> <span class="p">{</span> <span class="n">newItem</span> <span class="p">-&gt;</span> <span class="n">selectionModel</span><span class="p">.</span><span class="nf">select</span><span class="p">(</span><span class="n">newItem</span><span class="p">)</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>If you think about it, you’ll realize that triggering the subscription on one <code class="language-plaintext highlighter-rouge">Property</code> due to a change in the other won’t cause an infinite loop because the result of that subscription will bring the two values in sync and won’t trigger the second subscription.</p>

<p>You can now see that this screen doesn’t even need to know about the domain objects <code class="language-plaintext highlighter-rouge">Manifold</code> and <code class="language-plaintext highlighter-rouge">Distance</code> any more.  These elements - if they exist at all - are created somewhere else and then presumably used to create <code class="language-plaintext highlighter-rouge">Manifold</code> and <code class="language-plaintext highlighter-rouge">Distance</code> presentation data objects.</p>

<h3 id="drag-n-drop-file-import">“Drag ‘n Drop” File Import</h3>

<p>Right near the top of the FXML Controller, there is some code that initialized the “root” of the Scene as the destination for a drag ‘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.</p>

<p>I wrestled with the idea of putting the <code class="language-plaintext highlighter-rouge">EventHandlers</code> for drag and drop into the layout itself, but then realized that I’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 <code class="language-plaintext highlighter-rouge">File</code> object floating around in my layout code, even if indirectly.  This part bothered me.</p>

<p>I came to the conclusion that the drag and drop didn’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 “Drop Files Here” and only that box would respond to the drag and drop operation.  In this situation, we don’t have anything like this, and the entire layout can just be considered as a <code class="language-plaintext highlighter-rouge">Node</code> (in this case a <code class="language-plaintext highlighter-rouge">Region</code>, but we only care about it as a subclass of <code class="language-plaintext highlighter-rouge">Node</code>) and dealt with from the outside.</p>

<p>This 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’t have to pass any handlers over to the ViewBuilder, since it isn’t involved.  In fact, neither the ViewBuilder or the layout itself has any knowledge that it is a drag and drop destination.</p>

<p>In the Controller, the <code class="language-plaintext highlighter-rouge">getView()</code> method now looks like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fun</span> <span class="nf">getView</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="n">viewBuilder</span><span class="p">.</span><span class="nf">build</span><span class="p">()</span> <span class="nf">asFileDrop</span> <span class="p">{</span> <span class="nf">loadUmap</span><span class="p">(</span><span class="n">it</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="p">}</span>
</code></pre></div></div>
<p>It’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:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">infix</span> <span class="k">fun</span> <span class="p">&lt;</span><span class="nc">T</span> <span class="p">:</span> <span class="nc">Node</span><span class="p">&gt;</span> <span class="nc">T</span><span class="p">.</span><span class="nf">asFileDrop</span><span class="p">(</span><span class="n">handler</span><span class="p">:</span> <span class="p">(</span><span class="nc">List</span><span class="p">&lt;</span><span class="nc">File</span><span class="p">&gt;)</span> <span class="p">-&gt;</span> <span class="nc">Unit</span><span class="p">):</span> <span class="nc">T</span> <span class="p">=</span> <span class="nf">apply</span> <span class="p">{</span>
    <span class="nf">addEventHandler</span><span class="p">(</span><span class="nc">DragEvent</span><span class="p">.</span><span class="nc">DRAG_OVER</span><span class="p">)</span> <span class="p">{</span> <span class="n">event</span> <span class="p">-&gt;</span>
        <span class="n">event</span><span class="p">.</span><span class="nf">acceptTransferModes</span><span class="p">(</span><span class="nc">TransferMode</span><span class="p">.</span><span class="nc">COPY</span><span class="p">)</span>
    <span class="p">}</span>
    <span class="nf">addEventHandler</span><span class="p">(</span><span class="nc">DragEvent</span><span class="p">.</span><span class="nc">DRAG_DROPPED</span><span class="p">)</span> <span class="p">{</span> <span class="n">event</span> <span class="p">-&gt;</span>
        <span class="nf">with</span> <span class="p">(</span><span class="n">event</span><span class="p">.</span><span class="n">dragboard</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="nf">hasFiles</span><span class="p">())</span> <span class="p">{</span>
                <span class="n">handler</span><span class="p">.</span><span class="nf">invoke</span><span class="p">(</span><span class="n">files</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Everything in here is boilerplate and every time you implement this you would need those 11 lines of code.  But now it just becomes <code class="language-plaintext highlighter-rouge">asFileDrop {do something}</code>.</p>

<h3 id="the-large-number-of-action-handlers">The Large Number of Action Handlers</h3>

<p>There’s a lot of <code class="language-plaintext highlighter-rouge">Buttons</code> in this screen, and all of them trigger some kind of action within the application logic.</p>

<div class="notice_question--primary">
 <img src="/assets/logos/BRAIN_Question.png" alt="primary" style="float:left;margin-right: 10px" />
 <p style="overflow:auto; float:none">
   How do we provide all these action handlers to the View?<br /><br />
 </p>
</div>

<p>The 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.</p>

<p class="notice--primary">This means that no action handler ever has to supply data from the GUI.  The only information that is required is which <code class="language-plaintext highlighter-rouge">Button</code> was clicked.</p>

<p>The few actions that need to open <code class="language-plaintext highlighter-rouge">FileChoosers</code> need to pass the <code class="language-plaintext highlighter-rouge">Window (Stage)</code> that triggered it as <code class="language-plaintext highlighter-rouge">FileChooser</code> needs this to work.  I chose to implement these such that the <code class="language-plaintext highlighter-rouge">Button</code> would grab this element as part of its <code class="language-plaintext highlighter-rouge">EventHandler</code> that runs at click-time.  These actions do need to pass that element back to the action handler.</p>

<p>Two <code class="language-plaintext highlighter-rouge">Enums</code> were created, one for the <code class="language-plaintext highlighter-rouge">FileChooser</code> operations, called <code class="language-plaintext highlighter-rouge">FileOperation</code> and the other for all the other operations, called <code class="language-plaintext highlighter-rouge">GeneralOperation</code>.  “Operation” seems less likely to get confused with “Action”.  Naming things is hard!  The action handler is a <code class="language-plaintext highlighter-rouge">Consumer&lt;GeneralOperation&gt;</code>, or in Kotlin notation <code class="language-plaintext highlighter-rouge">(GeneralOperation) -&gt; Unit</code>, and each <code class="language-plaintext highlighter-rouge">Button</code> will call the handler’s <code class="language-plaintext highlighter-rouge">invoke(GeneralOperation)</code> method, passing the appropriate <code class="language-plaintext highlighter-rouge">GeneralOperation</code> value to it.</p>

<p>It is the Controller’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 “dispatch routine”, which invokes an appropriate Interactor method for each operation type via the Kotlin version of <code class="language-plaintext highlighter-rouge">switch</code>:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="k">fun</span> <span class="nf">generalOperation</span><span class="p">(</span><span class="n">operation</span><span class="p">:</span> <span class="nc">GeneralOperation</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">when</span> <span class="p">(</span><span class="n">operation</span><span class="p">)</span> <span class="p">{</span>
        <span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">BUILD_CLUSTER</span> <span class="p">-&gt;</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">buildCluster</span><span class="p">()</span>
        <span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">GENERATE</span> <span class="p">-&gt;</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">generate</span><span class="p">()</span>
        <span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">CLEAR_ALL</span> <span class="p">-&gt;</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">clearAll</span><span class="p">()</span>
        <span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">EXPORT_ALL</span> <span class="p">-&gt;</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">exportAll</span><span class="p">()</span>
        <span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">CLEAR_DISTANCES</span> <span class="p">-&gt;</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">clearDistances</span><span class="p">()</span>
        <span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">PROJECT</span> <span class="p">-&gt;</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">project</span><span class="p">()</span>
        <span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">EXPORT_MATRIX</span> <span class="p">-&gt;</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">exportMatrix</span><span class="p">()</span>
        <span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">SAVE_PROJECTIONS</span> <span class="p">-&gt;</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">saveProjections</span><span class="p">()</span>
        <span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">RUN_PCA</span> <span class="p">-&gt;</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">runPca</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>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’ll look at that next…</p>

<h3 id="connecting-to-the-rest-of-the-application-1">Connecting to the Rest of the Application</h3>

<p>There is a <code class="language-plaintext highlighter-rouge">SharedElements</code> class which contains JavaFX <code class="language-plaintext highlighter-rouge">Observable</code> 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’s GUI.  Sharing these data elements has the magic effect of making all the <code class="language-plaintext highlighter-rouge">Event</code> firing and <code class="language-plaintext highlighter-rouge">EventHandlers</code> in the original version totally redundant.</p>

<p>Let’s look at how this works…</p>

<p>In a situation where this screen is part of a larger application, whatever GUI element of that application that “owns” this screen would pass a <code class="language-plaintext highlighter-rouge">SharedElements</code> object to the Controller via its constructor.  This <code class="language-plaintext highlighter-rouge">SharedElements</code> object is then passed to the Model via its constructor.  At this point, the Controller’s involvement with the SharedElements is done.</p>

<p><code class="language-plaintext highlighter-rouge">SharedElements</code> is a set of <code class="language-plaintext highlighter-rouge">Properties</code> which are defined externally.  These are used to instantiate the <code class="language-plaintext highlighter-rouge">Property</code> fields in the Model.  Like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">showWireFrame</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="n">sharedElements</span><span class="p">.</span><span class="n">showWireFrame</span>
<span class="kd">val</span> <span class="py">showControlPoints</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="n">sharedElements</span><span class="p">.</span><span class="n">showControlPoints</span>
</code></pre></div></div>
<p>It’s important to note that since these elements are incorporated into the Model, just like any other elements, the fact that these are <code class="language-plaintext highlighter-rouge">Properties</code> 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 <code class="language-plaintext highlighter-rouge">Properties</code> in the Model are never going to ripple through to be changes in the View or the Interactor.</p>

<p>In a similar manner an object of the class <code class="language-plaintext highlighter-rouge">SharedFunctions</code> is passed to the constructor of the Controller, which calls it <code class="language-plaintext highlighter-rouge">externalFunctions</code>.  This is, in turn, passed to the Interactor via its constructor.  At this point, the Controller’s involvement with <code class="language-plaintext highlighter-rouge">SharedFunctions</code> is complete.</p>

<p>The Interactor then invokes these external functions as part of the application logic related to performing various actions related to <code class="language-plaintext highlighter-rouge">Button</code> clicks in the View.</p>

<p>On the surface this seem very round-about but it actually isolates the coupling nicely.  The View has a <code class="language-plaintext highlighter-rouge">Button</code> that triggers an <code class="language-plaintext highlighter-rouge">EventHandler</code>.  That <code class="language-plaintext highlighter-rouge">EventHandler</code> invokes an action <code class="language-plaintext highlighter-rouge">Consumer</code> that is defined by the Controller.  That <code class="language-plaintext highlighter-rouge">Consumer</code> invokes an corresponding method in the Interactor which, in turn, invokes a <code class="language-plaintext highlighter-rouge">Runnable</code> provided to the Interactor from outside the MVCI construct through the Controller.</p>

<p>Information about what these pieces do is available on a strict “need to know” basis throughout the framework.  There is an <code class="language-plaintext highlighter-rouge">Enum</code> 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’t know what they are.</p>

<h1 id="counting-lines-of-codefxml">Counting Lines of Code/FXML</h1>

<p>I’m not generally a fan of counting code, but it can be an indicator of the complexity, readability and maintainability of a system.</p>

<p>Let’s look at the original:</p>

<table>
  <thead>
    <tr>
      <th>Element</th>
      <th>Lines</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>FXML</td>
      <td>462</td>
    </tr>
    <tr>
      <td>FXML Controller</td>
      <td>752</td>
    </tr>
    <tr>
      <td>ManifoldListItem</td>
      <td>76</td>
    </tr>
    <tr>
      <td>DistanceListItem</td>
      <td>55</td>
    </tr>
    <tr>
      <td>Manifold</td>
      <td>130</td>
    </tr>
    <tr>
      <td>Distance</td>
      <td>190</td>
    </tr>
    <tr>
      <td>Total</td>
      <td>1665</td>
    </tr>
  </tbody>
</table>

<p>Now, let’s look at the hand-coded version:</p>

<table>
  <thead>
    <tr>
      <th>Element</th>
      <th>Lines</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Model</td>
      <td>60</td>
    </tr>
    <tr>
      <td>Controller</td>
      <td>48</td>
    </tr>
    <tr>
      <td>Interactor</td>
      <td>80</td>
    </tr>
    <tr>
      <td>ViewBuilder</td>
      <td>230</td>
    </tr>
    <tr>
      <td>ManifoldListCell</td>
      <td>21</td>
    </tr>
    <tr>
      <td>DistanceListCell</td>
      <td>24</td>
    </tr>
    <tr>
      <td>SharedElements</td>
      <td>12</td>
    </tr>
    <tr>
      <td>SharedFunctions</td>
      <td>12</td>
    </tr>
    <tr>
      <td>Total</td>
      <td>487</td>
    </tr>
  </tbody>
</table>

<p>The hand-coded version is less than 1/3 the amount of code in the original version - although I’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.</p>

<h1 id="is-it-easier-to-understand">Is it Easier to Understand?</h1>

<p>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.</p>

<p>In this case, we have a bunch of <code class="language-plaintext highlighter-rouge">Tabs</code> and the programmer is probably going to want to drill down into the code for a specific <code class="language-plaintext highlighter-rouge">Tab</code>.</p>

<p>Let’s take a look at the top of the layout code, were the root elements are defined:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">override</span> <span class="k">fun</span> <span class="nf">build</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">TabPane</span><span class="p">(</span>
    <span class="nf">createUmapTab</span><span class="p">(),</span>
    <span class="nf">createPcaTab</span><span class="p">(),</span>
    <span class="nf">createDistancesTab</span><span class="p">(),</span>
    <span class="nf">createHullTab</span><span class="p">()</span>
<span class="p">)</span> <span class="n">withClosingPolicy</span> <span class="nc">TabPane</span><span class="p">.</span><span class="nc">TabClosingPolicy</span><span class="p">.</span><span class="nc">UNAVAILABLE</span>

<span class="k">private</span> <span class="k">fun</span> <span class="nf">createPcaTab</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Tab</span><span class="p">(</span><span class="s">"PCA"</span><span class="p">)</span> <span class="nf">withContents</span> <span class="p">(</span><span class="nc">VBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">,</span> <span class="nf">pcaGridPane</span><span class="p">(),</span> <span class="nf">pcaButtonBox</span><span class="p">())</span> <span class="n">padWith</span> <span class="mf">25.0</span><span class="p">)</span>

<span class="k">private</span> <span class="k">fun</span> <span class="nf">createUmapTab</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Tab</span><span class="p">(</span><span class="s">"UMAP"</span><span class="p">)</span> <span class="n">withContents</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">padWith</span><span class="p">(</span><span class="mf">25.0</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">addChild</span><span class="p">(</span><span class="nf">umapGridPane</span><span class="p">())</span>
    <span class="p">.</span><span class="nf">addChild</span><span class="p">(</span><span class="nf">umapDistanceThresholdBox</span><span class="p">())</span>
    <span class="p">.</span><span class="nf">addChild</span><span class="p">(</span><span class="nf">umapControlBox</span><span class="p">())</span>

<span class="k">private</span> <span class="k">fun</span> <span class="nf">createDistancesTab</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Tab</span><span class="p">(</span><span class="s">"Distances"</span><span class="p">)</span> <span class="n">withContents</span> <span class="nc">HBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">,</span> <span class="nf">distanceLeft</span><span class="p">(),</span> <span class="nf">distanceRight</span><span class="p">())</span>

<span class="k">private</span> <span class="k">fun</span> <span class="nf">createHullTab</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Tab</span><span class="p">(</span><span class="s">"Hull Geometry"</span><span class="p">)</span> <span class="n">withContents</span> <span class="nc">BorderPane</span><span class="p">()</span>
    <span class="p">.</span><span class="nf">withTop</span><span class="p">(</span><span class="nf">hullTop</span><span class="p">())</span>
    <span class="p">.</span><span class="nf">withLeft</span><span class="p">(</span><span class="nf">manifoldPropertyBox</span><span class="p">())</span>
    <span class="p">.</span><span class="nf">withCenter</span><span class="p">(</span><span class="nf">manifoldBox</span><span class="p">())</span>
</code></pre></div></div>
<p>We can see right away that the entirety of the layout is a single <code class="language-plaintext highlighter-rouge">TabPane</code> with 4 <code class="language-plaintext highlighter-rouge">Tabs</code> that cannot be closed.  We have a short builder method for each <code class="language-plaintext highlighter-rouge">Tab</code> with an name that mirrors the title of each <code class="language-plaintext highlighter-rouge">Tab</code>.  No matter which <code class="language-plaintext highlighter-rouge">Tab</code> you are interested in you can quickly find it, without even having to scroll down through the code.</p>

<p>We can quickly get an idea about the structure of the contents of these <code class="language-plaintext highlighter-rouge">Tabs</code>.  Two are <code class="language-plaintext highlighter-rouge">VBoxes</code>, one is an <code class="language-plaintext highlighter-rouge">HBox</code> and the other is a <code class="language-plaintext highlighter-rouge">BorderPane</code>.  There are builders for every element contained in these <code class="language-plaintext highlighter-rouge">Regions</code> in the <code class="language-plaintext highlighter-rouge">Tabs</code>, and we can click-through on them to get to them.</p>

<p>You can also see here that all of the configuration elements such <code class="language-plaintext highlighter-rouge">setPadding()</code> have been implemented as extension decorator functions that have also been implemented as “infix” functions.  This means that they can be used without the <code class="language-plaintext highlighter-rouge">.</code> and <code class="language-plaintext highlighter-rouge">()</code> and can, in some cases, increase readability.</p>

<p>In 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 “PCA” <code class="language-plaintext highlighter-rouge">Tab</code> to the “UMAP” <code class="language-plaintext highlighter-rouge">Tab</code>.</p>

<p>I’ve tried to avoid naming the builders with positional names whenever possible.  However, I really don’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 <code class="language-plaintext highlighter-rouge">BorderPane.top</code> in the “Hull” <code class="language-plaintext highlighter-rouge">Tab</code>, and I just called it <code class="language-plaintext highlighter-rouge">hullTop()</code>.</p>

<p>I find that <code class="language-plaintext highlighter-rouge">GridPanes</code> 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 “UMAP” <code class="language-plaintext highlighter-rouge">GridPane</code>:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="k">fun</span> <span class="nf">umapGridPane</span><span class="p">()</span> <span class="p">=</span> <span class="nc">GridPane</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
    <span class="n">columnConstraints</span> <span class="p">+=</span> <span class="nc">ColumnConstraints</span><span class="p">(</span><span class="mf">10.0</span><span class="p">,</span> <span class="mf">200.0</span><span class="p">,</span> <span class="mf">288.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span> <span class="n">hgrow</span> <span class="p">=</span> <span class="nc">Priority</span><span class="p">.</span><span class="nc">SOMETIMES</span> <span class="p">}</span>
    <span class="n">columnConstraints</span> <span class="p">+=</span> <span class="nc">ColumnConstraints</span><span class="p">(</span><span class="mf">10.0</span><span class="p">,</span> <span class="mf">200.0</span><span class="p">,</span> <span class="mf">380.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span> <span class="n">hgrow</span> <span class="p">=</span> <span class="nc">Priority</span><span class="p">.</span><span class="nc">SOMETIMES</span> <span class="p">}</span>
    <span class="nf">addRowConstraints</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="n">stdRowConst</span><span class="p">)</span>
    <span class="nf">umapSpinnerColumn</span><span class="p">()</span>
    <span class="nf">umapSliderColumn</span><span class="p">()</span>
<span class="p">}</span>

<span class="k">private</span> <span class="k">fun</span> <span class="nc">GridPane</span><span class="p">.</span><span class="nf">umapSliderColumn</span><span class="p">()</span> <span class="p">{</span>
    <span class="nf">stackedSlider</span><span class="p">(</span><span class="s">"Repulsion Strength"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">repulsionStr</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">2.0</span><span class="p">,</span> <span class="mf">0.1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="n">ticksOn</span> <span class="k">true</span> <span class="n">addStyle</span> <span class="s">"std-slider"</span>
    <span class="nf">stackedSlider</span><span class="p">(</span><span class="s">"Minimum Distance"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">minimumDistance</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">0.6</span><span class="p">,</span> <span class="mf">0.1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="n">ticksOn</span> <span class="k">true</span> <span class="n">addStyle</span> <span class="s">"std-slider"</span>
    <span class="nf">stackedSlider</span><span class="p">(</span><span class="s">"Spread"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">spread</span><span class="p">,</span> <span class="mf">0.5</span><span class="p">,</span> <span class="mf">1.5</span><span class="p">,</span> <span class="mf">0.1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span> <span class="n">ticksOn</span> <span class="k">true</span> <span class="n">addStyle</span> <span class="s">"std-slider"</span>
    <span class="nf">stackedSlider</span><span class="p">(</span><span class="s">"Op Mix Ration"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">opMixRatio</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">0.1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> <span class="n">ticksOn</span> <span class="k">true</span> <span class="n">addStyle</span> <span class="s">"std-slider"</span>
    <span class="nf">stackedSlider</span><span class="p">(</span><span class="s">"Target Weight"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">targetWeight</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">0.1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span> <span class="n">ticksOn</span> <span class="k">true</span> <span class="n">addStyle</span> <span class="s">"std-slider"</span>
<span class="p">}</span>

<span class="k">private</span> <span class="k">fun</span> <span class="nc">GridPane</span><span class="p">.</span><span class="nf">umapSpinnerColumn</span><span class="p">()</span> <span class="p">{</span>
    <span class="nf">stackedIntSpinner</span><span class="p">(</span><span class="s">"Number of Components"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">numberOfComponents</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="n">addStyle</span> <span class="s">"std-spinner"</span>
    <span class="nf">stackedIntSpinner</span><span class="p">(</span><span class="s">"Number of Epochs"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">numberOfEpochs</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">500</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="n">addStyle</span> <span class="s">"std-spinner"</span>
    <span class="nf">stackedIntSpinner</span><span class="p">(</span><span class="s">"Nearest Neighbours"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">nearestNeighbour</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">500</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span> <span class="n">addStyle</span> <span class="s">"std-spinner"</span>
    <span class="nf">stackedIntSpinner</span><span class="p">(</span><span class="s">"Negative Sample Rate"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">negativeSampleRate</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">250</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> <span class="n">addStyle</span> <span class="s">"std-spinner"</span>
    <span class="nf">stackedIntSpinner</span><span class="p">(</span><span class="s">"Local Connectivity"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">localConnectivity</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">250</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span> <span class="n">addStyle</span> <span class="s">"std-spinner"</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This <code class="language-plaintext highlighter-rouge">GridPane</code> has two columns, the one on the right has a bunch of <code class="language-plaintext highlighter-rouge">Sliders</code> with their <code class="language-plaintext highlighter-rouge">Labels</code>, and the one on the left has <code class="language-plaintext highlighter-rouge">Spinners</code> and their <code class="language-plaintext highlighter-rouge">Labels</code>.  In both columns, the <code class="language-plaintext highlighter-rouge">Label</code> sits on the row above its corresponding input <code class="language-plaintext highlighter-rouge">Control</code>.  It’s now easy to see the structure of the <code class="language-plaintext highlighter-rouge">GridPane</code> at a glance.  In contrast, translating these <code class="language-plaintext highlighter-rouge">GridPanes</code> from the FXML took more time than any other part because you couldn’t just look at it quickly and understand the structure.</p>

<p>It’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’s not seen here.  For instance, this is what I see with <code class="language-plaintext highlighter-rouge">umapSliderColumn()</code>:</p>

<p><img src="/assets/images/TrinityScreenShot2.png" alt="Intellij Snapshot" /></p>

<p>From here it is clear what all of those parameters do.</p>

<p>One further thing, which I think contributes greatly to the “easier to understand” aspect of this discussion.  You can see that <strong>ALL</strong> of the parameters related to these input <code class="language-plaintext highlighter-rouge">Controls</code> are included here in these 10 method calls.  There’s no need to go running off somewhere else see how one of the <code class="language-plaintext highlighter-rouge">Spinners</code> is configured.  Each one is also bi-directionally bound to a <code class="language-plaintext highlighter-rouge">Property</code> field in the Model, so there’s no need to mess about with initial values either - as that is handled in the Model or the Interactor.</p>

<h1 id="the-new-code">The New Code</h1>

<p>Oh, wow!  That’s a lot of discussion and preamble, and not a lot of coding.  Let’s take a look at the completed redesign.</p>

<h2 id="the-viewbuilder">The ViewBuilder</h2>

<p>This is the bulk of the code…</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">FromFxmlViewBuilder</span><span class="p">(</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">model</span><span class="p">:</span> <span class="nc">FromFxmlModel</span><span class="p">,</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">fileOp</span><span class="p">:</span> <span class="p">(</span><span class="nc">Window</span><span class="p">,</span> <span class="nc">FileOperation</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">Unit</span><span class="p">,</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">genOp</span><span class="p">:</span> <span class="p">(</span><span class="nc">GeneralOperation</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">Unit</span>
<span class="p">)</span> <span class="p">:</span>
    <span class="nc">Builder</span><span class="p">&lt;</span><span class="nc">Region</span><span class="p">&gt;</span> <span class="p">{</span>

    <span class="k">private</span> <span class="kd">val</span> <span class="py">stdRowConst</span> <span class="p">=</span> <span class="nc">RowConstraints</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">minHeight</span> <span class="p">=</span> <span class="mf">10.0</span>
        <span class="n">prefHeight</span> <span class="p">=</span> <span class="mf">30.0</span>
        <span class="n">vgrow</span> <span class="p">=</span> <span class="nc">Priority</span><span class="p">.</span><span class="nc">NEVER</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">build</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">TabPane</span><span class="p">(</span><span class="nf">createUmapTab</span><span class="p">(),</span> <span class="nf">createPcaTab</span><span class="p">(),</span> <span class="nf">createDistancesTab</span><span class="p">(),</span> <span class="nf">createHullTab</span><span class="p">())</span>
        <span class="p">.</span><span class="nf">withClosingPolicy</span><span class="p">(</span><span class="nc">TabPane</span><span class="p">.</span><span class="nc">TabClosingPolicy</span><span class="p">.</span><span class="nc">UNAVAILABLE</span><span class="p">)</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">createPcaTab</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Tab</span><span class="p">(</span><span class="s">"PCA"</span><span class="p">)</span> <span class="nf">withContents</span> <span class="p">(</span><span class="nc">VBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">,</span> <span class="nf">pcaGridPane</span><span class="p">(),</span> <span class="nf">pcaButtonBox</span><span class="p">())</span> <span class="n">padWith</span> <span class="mf">25.0</span><span class="p">)</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">createUmapTab</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Tab</span><span class="p">(</span><span class="s">"UMAP"</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">withContents</span><span class="p">(</span><span class="nc">VBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">,</span> <span class="nf">umapGridPane</span><span class="p">(),</span> <span class="nf">umapDistanceThresholdBox</span><span class="p">(),</span> <span class="nf">umapControlBox</span><span class="p">()).</span><span class="nf">padWith</span><span class="p">(</span><span class="mf">25.0</span><span class="p">))</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">createDistancesTab</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Tab</span><span class="p">(</span><span class="s">"Distances"</span><span class="p">)</span> <span class="n">withContents</span> <span class="nc">HBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">,</span> <span class="nf">distanceLeft</span><span class="p">(),</span> <span class="nf">distanceRight</span><span class="p">())</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">createHullTab</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Tab</span><span class="p">(</span><span class="s">"Hull Geometry"</span><span class="p">)</span> <span class="n">withContents</span> <span class="nc">BorderPane</span><span class="p">()</span>
        <span class="p">.</span><span class="nf">withTop</span><span class="p">(</span><span class="nf">hullTop</span><span class="p">())</span>
        <span class="p">.</span><span class="nf">withLeft</span><span class="p">(</span><span class="nf">manifoldPropertyBox</span><span class="p">())</span>
        <span class="p">.</span><span class="nf">withCenter</span><span class="p">(</span><span class="nf">manifoldBox</span><span class="p">())</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">umapGridPane</span><span class="p">()</span> <span class="p">=</span> <span class="nc">GridPane</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">columnConstraints</span> <span class="p">+=</span> <span class="nc">ColumnConstraints</span><span class="p">(</span><span class="mf">10.0</span><span class="p">,</span> <span class="mf">200.0</span><span class="p">,</span> <span class="mf">288.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span> <span class="n">hgrow</span> <span class="p">=</span> <span class="nc">Priority</span><span class="p">.</span><span class="nc">SOMETIMES</span> <span class="p">}</span>
        <span class="n">columnConstraints</span> <span class="p">+=</span> <span class="nc">ColumnConstraints</span><span class="p">(</span><span class="mf">10.0</span><span class="p">,</span> <span class="mf">200.0</span><span class="p">,</span> <span class="mf">380.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span> <span class="n">hgrow</span> <span class="p">=</span> <span class="nc">Priority</span><span class="p">.</span><span class="nc">SOMETIMES</span> <span class="p">}</span>
        <span class="nf">addRowConstraints</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="n">stdRowConst</span><span class="p">)</span>
        <span class="nf">umapSpinnerColumn</span><span class="p">()</span>
        <span class="nf">umapSliderColumn</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nc">GridPane</span><span class="p">.</span><span class="nf">umapSliderColumn</span><span class="p">()</span> <span class="p">{</span>
        <span class="nf">stackedSlider</span><span class="p">(</span><span class="s">"Repulsion Strength"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">repulsionStr</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">2.0</span><span class="p">,</span> <span class="mf">0.1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="n">ticksOn</span> <span class="k">true</span> <span class="n">addStyle</span> <span class="s">"std-slider"</span>
        <span class="nf">stackedSlider</span><span class="p">(</span><span class="s">"Minimum Distance"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">minimumDistance</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">0.6</span><span class="p">,</span> <span class="mf">0.1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="n">ticksOn</span> <span class="k">true</span> <span class="n">addStyle</span> <span class="s">"std-slider"</span>
        <span class="nf">stackedSlider</span><span class="p">(</span><span class="s">"Spread"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">spread</span><span class="p">,</span> <span class="mf">0.5</span><span class="p">,</span> <span class="mf">1.5</span><span class="p">,</span> <span class="mf">0.1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span> <span class="n">ticksOn</span> <span class="k">true</span> <span class="n">addStyle</span> <span class="s">"std-slider"</span>
        <span class="nf">stackedSlider</span><span class="p">(</span><span class="s">"Op Mix Ration"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">opMixRatio</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">0.1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> <span class="n">ticksOn</span> <span class="k">true</span> <span class="n">addStyle</span> <span class="s">"std-slider"</span>
        <span class="nf">stackedSlider</span><span class="p">(</span><span class="s">"Target Weight"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">targetWeight</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">0.1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span> <span class="n">ticksOn</span> <span class="k">true</span> <span class="n">addStyle</span> <span class="s">"std-slider"</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nc">GridPane</span><span class="p">.</span><span class="nf">umapSpinnerColumn</span><span class="p">()</span> <span class="p">{</span>
        <span class="nf">stackedIntSpinner</span><span class="p">(</span><span class="s">"Number of Components"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">numberOfComponents</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="n">addStyle</span> <span class="s">"std-spinner"</span>
        <span class="nf">stackedIntSpinner</span><span class="p">(</span><span class="s">"Number of Epochs"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">numberOfEpochs</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">500</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="n">addStyle</span> <span class="s">"std-spinner"</span>
        <span class="nf">stackedIntSpinner</span><span class="p">(</span><span class="s">"Nearest Neighbours"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">nearestNeighbour</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">500</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span> <span class="n">addStyle</span> <span class="s">"std-spinner"</span>
        <span class="nf">stackedIntSpinner</span><span class="p">(</span><span class="s">"Negative Sample Rate"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">negativeSampleRate</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">250</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> <span class="n">addStyle</span> <span class="s">"std-spinner"</span>
        <span class="nf">stackedIntSpinner</span><span class="p">(</span><span class="s">"Local Connectivity"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">localConnectivity</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">250</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span> <span class="n">addStyle</span> <span class="s">"std-spinner"</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">umapDistanceThresholdBox</span><span class="p">()</span> <span class="p">=</span> <span class="nc">HBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">alignment</span> <span class="p">=</span> <span class="nc">Pos</span><span class="p">.</span><span class="nc">TOP_CENTER</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">,</span> <span class="nf">promptOf</span><span class="p">(</span><span class="s">"Distance Metric"</span><span class="p">),</span> <span class="nc">ChoiceBox</span><span class="p">(</span><span class="nf">generateDefaultMetrics</span><span class="p">()).</span><span class="nf">firstSelected</span><span class="p">())</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">VBox</span><span class="p">(</span>
            <span class="mf">10.0</span><span class="p">,</span>
            <span class="nf">promptOf</span><span class="p">(</span><span class="s">"Threshold (if applicable)"</span><span class="p">),</span>
            <span class="nf">doubleSpinnerOf</span><span class="p">(</span><span class="mf">0.01</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="s">"standard-spinner"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">threshold</span><span class="p">.</span><span class="nf">asObject</span><span class="p">()).</span><span class="nf">setStep</span><span class="p">(</span><span class="mf">0.01</span><span class="p">)</span>
        <span class="p">)</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">umapControlBox</span><span class="p">()</span> <span class="p">=</span> <span class="nc">HBox</span><span class="p">(</span><span class="mf">15.0</span><span class="p">,</span> <span class="nf">umapConfigButtonBox</span><span class="p">(),</span> <span class="nf">umapHyperBox</span><span class="p">(),</span> <span class="nf">umapRunExportButtonBox</span><span class="p">())</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">umapConfigButtonBox</span><span class="p">()</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nf">buttonOf</span><span class="p">(</span><span class="s">"Load New Config"</span><span class="p">)</span> <span class="p">{</span> <span class="nf">println</span><span class="p">(</span><span class="s">"Hello"</span><span class="p">)</span> <span class="p">}</span> <span class="n">addStyle</span> <span class="s">"standard-button"</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nf">buttonOf</span><span class="p">(</span><span class="s">"Save Current Config"</span><span class="p">)</span> <span class="p">{</span> <span class="n">fileOp</span><span class="p">.</span><span class="nf">invoke</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="n">scene</span><span class="p">.</span><span class="n">window</span><span class="p">,</span> <span class="nc">FileOperation</span><span class="p">.</span><span class="nc">UMAP_SAVE</span><span class="p">)</span> <span class="p">}</span>
            <span class="p">.</span><span class="nf">addStyle</span><span class="p">(</span><span class="s">"standard-button"</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">umapHyperBox</span><span class="p">()</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="nf">with</span><span class="p">(</span><span class="nc">ToggleGroup</span><span class="p">())</span> <span class="p">{</span>
            <span class="nf">addChild</span><span class="p">(</span><span class="nc">RadioButton</span><span class="p">(</span><span class="s">"Use Hypersurface"</span><span class="p">)</span> <span class="n">inToggleGroup</span> <span class="k">this</span><span class="p">)</span>
            <span class="nf">addChild</span><span class="p">(</span><span class="nc">RadioButton</span><span class="p">(</span><span class="s">"Use Hyperspace"</span><span class="p">)</span> <span class="n">inToggleGroup</span> <span class="k">this</span> <span class="n">setSelected</span> <span class="k">true</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="nf">addChild</span><span class="p">(</span><span class="nc">CheckBox</span><span class="p">(</span><span class="s">"Progress Output"</span><span class="p">))</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">umapRunExportButtonBox</span><span class="p">()</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">(</span>
        <span class="mf">10.0</span><span class="p">,</span>
        <span class="nf">buttonOf</span><span class="p">(</span><span class="s">"Run UMAP"</span><span class="p">)</span> <span class="p">{</span> <span class="n">genOp</span><span class="p">.</span><span class="nf">invoke</span><span class="p">(</span><span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">PROJECT</span><span class="p">)</span> <span class="p">}</span> <span class="n">addStyle</span> <span class="s">"standard-button"</span><span class="p">,</span>
        <span class="nf">buttonOf</span><span class="p">(</span><span class="s">"Export TMatrix"</span><span class="p">)</span> <span class="p">{</span> <span class="n">genOp</span><span class="p">.</span><span class="nf">invoke</span><span class="p">(</span><span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">EXPORT_MATRIX</span><span class="p">)</span> <span class="p">}</span> <span class="n">addStyle</span> <span class="s">"standard-button"</span><span class="p">,</span>
        <span class="nf">buttonOf</span><span class="p">(</span><span class="s">"Export Projections"</span><span class="p">)</span> <span class="p">{</span> <span class="n">genOp</span><span class="p">.</span><span class="nf">invoke</span><span class="p">(</span><span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">SAVE_PROJECTIONS</span><span class="p">)</span> <span class="p">}</span> <span class="n">addStyle</span> <span class="s">"standard-button"</span>
    <span class="p">)</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">pcaButtonBox</span><span class="p">()</span> <span class="p">=</span> <span class="nc">HBox</span><span class="p">(</span>
        <span class="mf">15.0</span><span class="p">,</span>
        <span class="nf">buttonOf</span><span class="p">(</span><span class="s">"Project Data"</span><span class="p">)</span> <span class="p">{</span> <span class="n">genOp</span><span class="p">.</span><span class="nf">invoke</span><span class="p">(</span><span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">RUN_PCA</span><span class="p">)</span> <span class="p">}</span> <span class="n">addStyle</span> <span class="s">"standard-button"</span><span class="p">,</span>
        <span class="nf">buttonOf</span><span class="p">(</span><span class="s">"Export Projections"</span><span class="p">)</span> <span class="p">{</span> <span class="n">genOp</span><span class="p">.</span><span class="nf">invoke</span><span class="p">(</span><span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">SAVE_PROJECTIONS</span><span class="p">)</span> <span class="p">}</span> <span class="n">addStyle</span> <span class="s">"standard-button"</span>
    <span class="p">)</span> <span class="n">alignTo</span> <span class="nc">Pos</span><span class="p">.</span><span class="nc">CENTER</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">pcaGridPane</span><span class="p">()</span> <span class="p">=</span> <span class="nc">GridPane</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">columnConstraints</span> <span class="p">+=</span> <span class="nc">ColumnConstraints</span><span class="p">(</span><span class="mf">10.0</span><span class="p">,</span> <span class="mf">200.0</span><span class="p">,</span> <span class="mf">288.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span> <span class="n">hgrow</span> <span class="p">=</span> <span class="nc">Priority</span><span class="p">.</span><span class="nc">SOMETIMES</span> <span class="p">}</span>
        <span class="n">columnConstraints</span> <span class="p">+=</span> <span class="nc">ColumnConstraints</span><span class="p">(</span><span class="mf">10.0</span><span class="p">,</span> <span class="mf">200.0</span><span class="p">,</span> <span class="mf">380.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span> <span class="n">hgrow</span> <span class="p">=</span> <span class="nc">Priority</span><span class="p">.</span><span class="nc">SOMETIMES</span> <span class="p">}</span>
        <span class="nf">addRowConstraints</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="n">stdRowConst</span><span class="p">)</span>
        <span class="nf">pcaLeftColumn</span><span class="p">()</span>
        <span class="nf">pcaRightColumn</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nc">GridPane</span><span class="p">.</span><span class="nf">pcaRightColumn</span><span class="p">()</span> <span class="p">{</span>
        <span class="nf">add</span><span class="p">(</span><span class="nf">promptOf</span><span class="p">(</span><span class="s">"Component Analysis Type"</span><span class="p">),</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
        <span class="nf">with</span><span class="p">(</span><span class="nc">ToggleGroup</span><span class="p">())</span> <span class="p">{</span>
            <span class="nf">add</span><span class="p">(</span><span class="nf">radioButtonOf</span><span class="p">(</span><span class="s">"PCA (EigenValue)"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">analysisMethodPca</span><span class="p">,</span> <span class="k">this</span><span class="p">,</span> <span class="s">"std-radio"</span><span class="p">),</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
            <span class="nf">add</span><span class="p">(</span>
                <span class="nf">radioButtonOf</span><span class="p">(</span><span class="s">"Singular Value Decomposition"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">analysisMethodSvd</span><span class="p">,</span> <span class="k">this</span><span class="p">,</span> <span class="s">"std-radio"</span><span class="p">),</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span>
            <span class="p">)</span>
        <span class="p">}</span>
        <span class="nf">add</span><span class="p">(</span><span class="nf">promptOf</span><span class="p">(</span><span class="s">"Input Data Source"</span><span class="p">),</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
        <span class="nf">with</span><span class="p">(</span><span class="nc">ToggleGroup</span><span class="p">())</span> <span class="p">{</span>
            <span class="nf">add</span><span class="p">(</span><span class="nf">radioButtonOf</span><span class="p">(</span><span class="s">"Use Hypersurface"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">dummyBoolean1</span><span class="p">,</span> <span class="k">this</span><span class="p">,</span> <span class="s">"std-radio"</span><span class="p">),</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
            <span class="nf">add</span><span class="p">(</span><span class="nf">radioButtonOf</span><span class="p">(</span><span class="s">"Use Hyperspace"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">dummyBoolean2</span><span class="p">,</span> <span class="k">this</span><span class="p">,</span> <span class="s">"std-radio"</span><span class="p">),</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nc">GridPane</span><span class="p">.</span><span class="nf">pcaLeftColumn</span><span class="p">()</span> <span class="p">{</span>
        <span class="nf">stackedIntSpinner</span><span class="p">(</span><span class="s">"Number of Components"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">numberOfPcaComponents</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="n">addStyle</span> <span class="s">"std-spinner"</span>
        <span class="nf">add</span><span class="p">(</span><span class="nf">checkBoxOf</span><span class="p">(</span><span class="s">"Enabled Ranged Fitting (Experimental)"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">rangeFitting</span><span class="p">,</span> <span class="s">"std-checkbox"</span><span class="p">),</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
        <span class="nf">stackedIntSpinner</span><span class="p">(</span><span class="s">"Fit Start Index"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">fitStartIndex</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">500</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">bindDisable</span><span class="p">(</span><span class="n">model</span><span class="p">.</span><span class="n">rangeFitting</span><span class="p">.</span><span class="nf">not</span><span class="p">())</span> <span class="n">addStyle</span> <span class="s">"std-spinner"</span>
        <span class="nf">stackedIntSpinner</span><span class="p">(</span><span class="s">"Fit End Index"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">fitEndIndex</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">2000</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">bindDisable</span><span class="p">(</span><span class="n">model</span><span class="p">.</span><span class="n">rangeFitting</span><span class="p">.</span><span class="nf">not</span><span class="p">())</span> <span class="n">addStyle</span> <span class="s">"std-spinner"</span>
        <span class="nf">stackedIntSpinner</span><span class="p">(</span><span class="s">"Output Scaling Factor"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">pcaScalingFactor</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1000</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">7</span><span class="p">)</span> <span class="n">addStyle</span> <span class="s">"std-spinner"</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">distanceLeft</span><span class="p">()</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">25.0</span><span class="p">)</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nf">h3Of</span><span class="p">(</span><span class="s">"Distance Metric"</span><span class="p">)</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nf">dataOf</span><span class="p">(</span><span class="nc">StringExpression</span><span class="p">.</span><span class="nf">stringExpression</span><span class="p">(</span><span class="n">model</span><span class="p">.</span><span class="n">selectedDistance</span><span class="p">.</span><span class="nf">flatMap</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">metric</span> <span class="p">}</span>
            <span class="p">.</span><span class="nf">orElse</span><span class="p">(</span><span class="s">"Select Distance"</span><span class="p">)))</span>
        <span class="nf">with</span><span class="p">(</span><span class="nc">ToggleGroup</span><span class="p">())</span> <span class="p">{</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nf">radioButtonOf</span><span class="p">(</span><span class="s">"Point to Point"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">pointToPoint</span><span class="p">,</span> <span class="k">this</span><span class="p">,</span> <span class="s">"std-radio"</span><span class="p">)</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nf">radioButtonOf</span><span class="p">(</span><span class="s">"Point to Group"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">pointToGroup</span><span class="p">,</span> <span class="k">this</span><span class="p">,</span> <span class="s">"std-radio"</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nf">h3Of</span><span class="p">(</span><span class="s">"Connector Thickness"</span><span class="p">)</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nf">intSpinnerOf</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="s">"standard-spinner"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">connectorThickness</span><span class="p">.</span><span class="nf">asObject</span><span class="p">())</span>
            <span class="p">.</span><span class="nf">setStep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">bindDisable</span><span class="p">(</span><span class="n">model</span><span class="p">.</span><span class="n">selectedDistance</span><span class="p">.</span><span class="n">isNull</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">withInitialDelay</span><span class="p">(</span><span class="nc">Duration</span><span class="p">.</span><span class="nf">millis</span><span class="p">(</span><span class="mf">500.0</span><span class="p">))</span>
            <span class="p">.</span><span class="nf">withRepeatDelay</span><span class="p">(</span><span class="nc">Duration</span><span class="p">.</span><span class="nf">millis</span><span class="p">(</span><span class="mf">500.0</span><span class="p">))</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nf">promptOf</span><span class="p">(</span><span class="s">"Connector Colour"</span><span class="p">)</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">ColorPicker</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">promptText</span> <span class="p">=</span> <span class="s">"Change the colour of the 3D connector"</span>
            <span class="nf">valueProperty</span><span class="p">().</span><span class="nf">bindBidirectional</span><span class="p">(</span><span class="n">model</span><span class="p">.</span><span class="n">connectorColour</span><span class="p">)</span>
            <span class="nf">bindDisable</span><span class="p">(</span><span class="n">model</span><span class="p">.</span><span class="n">selectedDistance</span><span class="p">.</span><span class="n">isNull</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">distanceRight</span><span class="p">()</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">(</span>
            <span class="mf">10.0</span><span class="p">,</span>
            <span class="nf">h3Of</span><span class="p">(</span><span class="s">"Collected Distances"</span><span class="p">),</span>
            <span class="nf">buttonOf</span><span class="p">(</span><span class="s">"Clear All"</span><span class="p">)</span> <span class="p">{</span> <span class="n">genOp</span><span class="p">.</span><span class="nf">invoke</span><span class="p">(</span><span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">CLEAR_DISTANCES</span><span class="p">)</span> <span class="p">})</span> <span class="n">alignTo</span> <span class="nc">Pos</span><span class="p">.</span><span class="nc">CENTER</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="p">(</span><span class="nc">ListView</span><span class="p">&lt;</span><span class="nc">Distance</span><span class="p">&gt;()</span> <span class="n">withItems</span> <span class="n">model</span><span class="p">.</span><span class="n">distanceList</span> <span class="n">withCellFactory</span> <span class="nc">Callback</span> <span class="p">{</span> <span class="nc">DistanceListCell</span><span class="p">()</span> <span class="p">}</span> <span class="n">connectSelection</span> <span class="n">model</span><span class="p">.</span><span class="n">selectedDistance</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">model</span><span class="p">.</span><span class="n">externallySelectedDistance</span><span class="p">.</span><span class="nf">subscribe</span> <span class="p">{</span> <span class="n">newValue</span> <span class="p">-&gt;</span>
                <span class="k">this</span><span class="p">.</span><span class="n">selectionModel</span><span class="p">.</span><span class="nf">select</span><span class="p">(</span><span class="n">newValue</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">manifoldPropertyBox</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nf">promptOf</span><span class="p">(</span><span class="s">"Selected Manifold Properties"</span><span class="p">)</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nf">titledPaneOf</span><span class="p">(</span><span class="s">"Material"</span><span class="p">)</span> <span class="p">{</span>
            <span class="nc">TwoColumnGridPane</span><span class="p">().</span><span class="nf">addColorPickerRow</span><span class="p">(</span><span class="s">"Diffuse Colour"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">manifoldDiffuseColour</span><span class="p">,</span> <span class="s">"std-color-picker"</span><span class="p">)</span>
                <span class="p">.</span><span class="nf">addColorPickerRow</span><span class="p">(</span><span class="s">"Wire Mesh Colour"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">manifoldWireMeshColour</span><span class="p">,</span> <span class="s">"std-color-picker"</span><span class="p">)</span>
                <span class="p">.</span><span class="nf">addColorPickerRow</span><span class="p">(</span><span class="s">"Specular Colour"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">manifoldSpecularColour</span><span class="p">,</span> <span class="s">"std-color-picker"</span><span class="p">)</span>
        <span class="p">}</span> <span class="n">withCollapsable</span> <span class="k">false</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nf">meshViewPane</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">meshViewPane</span><span class="p">()</span> <span class="p">=</span> <span class="nf">titledPaneOf</span><span class="p">(</span><span class="s">"MeshView"</span><span class="p">)</span> <span class="p">{</span>
        <span class="nc">VBox</span><span class="p">(</span><span class="mf">5.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nf">radioButtonHBox</span><span class="p">(</span>
                <span class="s">"Cull Face"</span><span class="p">,</span>
                <span class="nf">listOf</span><span class="p">(</span>
                    <span class="nc">Pair</span><span class="p">(</span><span class="s">"Front"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">frontCullFace</span><span class="p">),</span>
                    <span class="nc">Pair</span><span class="p">(</span><span class="s">"Back"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">backCullFace</span><span class="p">),</span>
                    <span class="nc">Pair</span><span class="p">(</span><span class="s">"None"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">noneCullFace</span><span class="p">)</span>
                <span class="p">),</span> <span class="mf">5.0</span><span class="p">,</span> <span class="s">"std-radio-button"</span>
            <span class="p">)</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nf">radioButtonHBox</span><span class="p">(</span>
                <span class="s">"Draw Mode"</span><span class="p">,</span>
                <span class="nf">listOf</span><span class="p">(</span><span class="nc">Pair</span><span class="p">(</span><span class="s">"Fill"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">fillDrawMode</span><span class="p">),</span> <span class="nc">Pair</span><span class="p">(</span><span class="s">"Lines"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">linesDrawMode</span><span class="p">)),</span>
                <span class="mf">5.0</span><span class="p">,</span>
                <span class="s">"std-radio-button"</span>
            <span class="p">)</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">(</span>
                <span class="mf">10.0</span><span class="p">,</span>
                <span class="nf">checkBoxOf</span><span class="p">(</span><span class="s">"Show Wire Frame"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">showWireFrame</span><span class="p">,</span> <span class="s">"std-checkbox"</span><span class="p">),</span>
                <span class="nf">checkBoxOf</span><span class="p">(</span><span class="s">"Show Control Points"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">showControlPoints</span><span class="p">,</span> <span class="s">"std-checkbox"</span><span class="p">)</span>
            <span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span> <span class="n">withCollapsable</span> <span class="k">false</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">hullTop</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">HBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="nf">padWith</span><span class="p">(</span><span class="mf">10.0</span><span class="p">)</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nf">hullTopGridPane</span><span class="p">()</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">VBox</span><span class="p">(</span>
            <span class="mf">20.0</span><span class="p">,</span>
            <span class="nf">buttonOf</span><span class="p">(</span><span class="s">"Generate"</span><span class="p">)</span> <span class="p">{</span> <span class="n">genOp</span><span class="p">.</span><span class="nf">invoke</span><span class="p">(</span><span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">GENERATE</span><span class="p">)</span> <span class="p">},</span>
            <span class="nf">buttonOf</span><span class="p">(</span><span class="s">"Cluster Tools"</span><span class="p">)</span> <span class="p">{</span> <span class="n">genOp</span><span class="p">.</span><span class="nf">invoke</span><span class="p">(</span><span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">BUILD_CLUSTER</span><span class="p">)</span> <span class="p">}</span>
        <span class="p">)</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">hullTopGridPane</span><span class="p">()</span> <span class="p">=</span> <span class="nc">TwoColumnGridPane</span><span class="p">()</span>
        <span class="p">.</span><span class="nf">addRadioButtonHBoxRow</span><span class="p">(</span>
            <span class="s">"Point Set"</span><span class="p">,</span>
            <span class="nf">listOf</span><span class="p">(</span><span class="nc">Pair</span><span class="p">(</span><span class="s">"Visible"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">useVisible</span><span class="p">),</span> <span class="nc">Pair</span><span class="p">(</span><span class="s">"All"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">useAll</span><span class="p">)),</span>
            <span class="mf">5.0</span><span class="p">,</span>
            <span class="s">"std-radio-button"</span>
        <span class="p">)</span>
        <span class="p">.</span><span class="nf">addRow</span><span class="p">(</span><span class="s">"Distance Tolerance"</span><span class="p">)</span> <span class="p">{</span>
            <span class="nc">HBox</span><span class="p">(</span>
                <span class="mf">10.0</span><span class="p">,</span>
                <span class="nf">checkBoxOf</span><span class="p">(</span><span class="s">"Auto"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">toleranceAuto</span><span class="p">,</span> <span class="s">"std-checkbox"</span><span class="p">),</span>
                <span class="nf">doubleSpinnerOf</span><span class="p">(</span><span class="mf">0.1</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="s">"std-spinner"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">toleranceManual</span><span class="p">.</span><span class="nf">asObject</span><span class="p">()).</span><span class="nf">setStep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
                    <span class="p">.</span><span class="nf">bindDisable</span><span class="p">(</span><span class="n">model</span><span class="p">.</span><span class="n">toleranceAuto</span><span class="p">)</span>
            <span class="p">)</span>
        <span class="p">}</span>
        <span class="p">.</span><span class="nf">addChoiceBoxRow</span><span class="p">(</span><span class="s">"Find by Label"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">factorLabelList</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">selectedFactorLabel</span><span class="p">,</span> <span class="s">"std-choice-box"</span><span class="p">,</span> <span class="k">true</span><span class="p">)</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">manifoldBox</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">5.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nf">h3Of</span><span class="p">(</span><span class="s">"Generated Manifolds"</span><span class="p">)</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">(</span><span class="mf">20.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nf">buttonOf</span><span class="p">(</span><span class="s">"Clear All"</span><span class="p">)</span> <span class="p">{</span> <span class="n">genOp</span><span class="p">.</span><span class="nf">invoke</span><span class="p">(</span><span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">CLEAR_ALL</span><span class="p">)</span> <span class="p">}</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nf">buttonOf</span><span class="p">(</span><span class="s">"Export All"</span><span class="p">)</span> <span class="p">{</span> <span class="n">genOp</span><span class="p">.</span><span class="nf">invoke</span><span class="p">(</span><span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">EXPORT_ALL</span><span class="p">)</span> <span class="p">}</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">ListView</span><span class="p">&lt;</span><span class="nc">Manifold</span><span class="p">&gt;()</span> <span class="n">withItems</span> <span class="n">model</span><span class="p">.</span><span class="n">manifoldList</span> <span class="n">withCellFactory</span> <span class="nc">Callback</span> <span class="p">{</span> <span class="nc">ManifoldListCell</span><span class="p">()</span> <span class="p">}</span> <span class="n">connectSelection</span> <span class="n">model</span><span class="p">.</span><span class="n">selectedManifold</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>One of the first things you should notice is that except for the container classes, none of the <code class="language-plaintext highlighter-rouge">Nodes</code> 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 <code class="language-plaintext highlighter-rouge">WidgetsFX</code>.</p>

<div class="notice--primary">
 <img src="/assets/logos/brain.png" alt="primary" style="float:left;margin-right: 10px" />
 <p style="overflow:auto; float:none">
   This points out one of the biggest problems with the standard JavaFX library - a lack of constructors that allow a parameter for value binding.
 </p>
</div>

<p>For those layout classes, I’ve used three standard techniques for populating them:</p>

<ol>
  <li>Providing the children as constructor parameters.</li>
  <li>Using <code class="language-plaintext highlighter-rouge">getChildren().add()</code> via <code class="language-plaintext highlighter-rouge">children += </code> inside <code class="language-plaintext highlighter-rouge">.apply{}</code>.</li>
  <li>Using the extension function <code class="language-plaintext highlighter-rouge">Pane.addChild()</code></li>
</ol>

<p>In practice, I found that <code class="language-plaintext highlighter-rouge">Pane.addChild()</code> outside of <code class="language-plaintext highlighter-rouge">apply{}</code> was no better than just including the children as constructor parameters.  It’s also not clear if <code class="language-plaintext highlighter-rouge">Pane.addChild()</code> is any clearer than <code class="language-plaintext highlighter-rouge">children += </code> inside of an <code class="language-plaintext highlighter-rouge">apply{}</code> block.</p>

<p>There are a fair number of <code class="language-plaintext highlighter-rouge">GridPanes</code> in this layout.  <code class="language-plaintext highlighter-rouge">GridPane</code> 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 <code class="language-plaintext highlighter-rouge">GridPane</code>, where the <code class="language-plaintext highlighter-rouge">Labels</code> and <code class="language-plaintext highlighter-rouge">Controls</code> are stacked in successive rows, with the <code class="language-plaintext highlighter-rouge">Spinner</code> inputs in one column and the <code class="language-plaintext highlighter-rouge">Slider</code> inputs in another column.  Is there really any need to keep the elements aligned by row?</p>

<p>While I don’t think I would use a <code class="language-plaintext highlighter-rouge">GridPane</code> in this case (two <code class="language-plaintext highlighter-rouge">VBoxes</code> in an <code class="language-plaintext highlighter-rouge">HBox</code> would be simpler), I did create the extension functions <code class="language-plaintext highlighter-rouge">GridPane.stackedSlider</code>, and <code class="language-plaintext highlighter-rouge">GridPane.stackedIntSpinner</code> to get the repeated elements out of the <code class="language-plaintext highlighter-rouge">GridPane</code> configuration.</p>

<p>For the <code class="language-plaintext highlighter-rouge">infix</code> decorator functions, I’ve used them as <code class="language-plaintext highlighter-rouge">infix</code> 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.</p>

<p>I 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 <code class="language-plaintext highlighter-rouge">apply{}</code> blocks.  Today, I find the <code class="language-plaintext highlighter-rouge">apply{}</code> approach to be overly verbose in many cases.</p>

<p>The 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.</p>

<p>I do feel that this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="k">fun</span> <span class="nf">manifoldPropertyBox</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
    <span class="n">children</span> <span class="p">+=</span> <span class="nf">promptOf</span><span class="p">(</span><span class="s">"Selected Manifold Properties"</span><span class="p">)</span>
    <span class="n">children</span> <span class="p">+=</span> <span class="nf">titledPaneOf</span><span class="p">(</span><span class="s">"Material"</span><span class="p">)</span> <span class="p">{</span>
        <span class="nc">TwoColumnGridPane</span><span class="p">()</span>
            <span class="p">.</span><span class="nf">addColorPickerRow</span><span class="p">(</span><span class="s">"Diffuse Colour"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">manifoldDiffuseColour</span><span class="p">,</span> <span class="s">"std-color-picker"</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">addColorPickerRow</span><span class="p">(</span><span class="s">"Wire Mesh Colour"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">manifoldWireMeshColour</span><span class="p">,</span> <span class="s">"std-color-picker"</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">addColorPickerRow</span><span class="p">(</span><span class="s">"Specular Colour"</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">manifoldSpecularColour</span><span class="p">,</span> <span class="s">"std-color-picker"</span><span class="p">)</span>
    <span class="p">}</span> <span class="n">withCollapsable</span> <span class="k">false</span>
    <span class="n">children</span> <span class="p">+=</span> <span class="nf">meshViewPane</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>is far easier to understand than:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;VBox</span> <span class="na">spacing=</span><span class="s">"10.0"</span> <span class="na">BorderPane.alignment=</span><span class="s">"CENTER"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;children&gt;</span>
    <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Selected Manifold Properties"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;TitledPane</span> <span class="na">collapsible=</span><span class="s">"false"</span> <span class="na">text=</span><span class="s">"Material"</span> <span class="na">VBox.vgrow=</span><span class="s">"ALWAYS"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;content&gt;</span>
              <span class="nt">&lt;VBox</span> <span class="na">spacing=</span><span class="s">"5.0"</span><span class="nt">&gt;</span>
                  <span class="nt">&lt;children&gt;</span>
                      <span class="nt">&lt;HBox</span> <span class="na">alignment=</span><span class="s">"CENTER_LEFT"</span> <span class="na">spacing=</span><span class="s">"10.0"</span><span class="nt">&gt;</span>
                          <span class="nt">&lt;children&gt;</span>
                                <span class="nt">&lt;Label</span> <span class="na">prefWidth=</span><span class="s">"125.0"</span> <span class="na">text=</span><span class="s">"Diffuse Color"</span><span class="nt">/&gt;</span>
                                <span class="nt">&lt;ColorPicker</span> <span class="na">fx:id=</span><span class="s">"manifoldDiffuseColorPicker"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefHeight=</span><span class="s">"50.0"</span>
                                             <span class="na">prefWidth=</span><span class="s">"150.0"</span><span class="nt">/&gt;</span>
                          <span class="nt">&lt;/children&gt;</span>
                    <span class="nt">&lt;/HBox&gt;</span>
                    <span class="nt">&lt;HBox</span> <span class="na">alignment=</span><span class="s">"CENTER_LEFT"</span> <span class="na">spacing=</span><span class="s">"10.0"</span><span class="nt">&gt;</span>
                          <span class="nt">&lt;children&gt;</span>
                                <span class="nt">&lt;Label</span> <span class="na">prefWidth=</span><span class="s">"125.0"</span> <span class="na">text=</span><span class="s">"Wire Mesh Color"</span><span class="nt">/&gt;</span>
                                <span class="nt">&lt;ColorPicker</span> <span class="na">fx:id=</span><span class="s">"manifoldWireMeshColorPicker"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefHeight=</span><span class="s">"50.0"</span>
                                                 <span class="na">prefWidth=</span><span class="s">"150.0"</span><span class="nt">/&gt;</span>
                         <span class="nt">&lt;/children&gt;</span>
                    <span class="nt">&lt;/HBox&gt;</span>
                    <span class="nt">&lt;HBox</span> <span class="na">alignment=</span><span class="s">"CENTER_LEFT"</span> <span class="na">spacing=</span><span class="s">"10.0"</span><span class="nt">&gt;</span>
                         <span class="nt">&lt;children&gt;</span>
                              <span class="nt">&lt;Label</span> <span class="na">prefWidth=</span><span class="s">"125.0"</span> <span class="na">text=</span><span class="s">"Specular Color"</span><span class="nt">/&gt;</span>
                             <span class="nt">&lt;ColorPicker</span> <span class="na">fx:id=</span><span class="s">"manifoldSpecularColorPicker"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefHeight=</span><span class="s">"50.0"</span>
                                     <span class="na">prefWidth=</span><span class="s">"150.0"</span><span class="nt">/&gt;</span>
                         <span class="nt">&lt;/children&gt;</span>
                    <span class="nt">&lt;/HBox&gt;</span>
                 <span class="nt">&lt;/children&gt;</span>
             <span class="nt">&lt;/VBox&gt;</span>
       <span class="nt">&lt;/content&gt;</span>
   <span class="nt">&lt;/TitledPane&gt;</span>
  .
  .
  .
<span class="nt">&lt;/VBox&gt;</span>   
</code></pre></div></div>
<p>Especially when you take into consideration the ~40 lines of code that configure these <code class="language-plaintext highlighter-rouge">ColorPickers</code> in the FXML Controller.  In the Kotlin code, these 3 lines completely configue the <code class="language-plaintext highlighter-rouge">ColorPickers</code> and they are never referenced again…anywhere.</p>

<h3 id="listview-cells">ListView Cells</h3>

<p>The original design didn’t properly handle the two <code class="language-plaintext highlighter-rouge">ListViews</code> properly at all.  These two classes provide <code class="language-plaintext highlighter-rouge">Cell</code> layouts that emulate what the original code did:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">DistanceListCell</span> <span class="p">:</span> <span class="nc">ListCell</span><span class="p">&lt;</span><span class="nc">Distance</span><span class="p">&gt;()</span> <span class="p">{</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">label</span> <span class="p">=</span> <span class="nc">Label</span><span class="p">()</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">distanceValueLabel</span> <span class="p">=</span> <span class="nc">Label</span><span class="p">()</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">visibleCB</span> <span class="p">=</span> <span class="nc">CheckBox</span><span class="p">(</span><span class="s">"Visible"</span><span class="p">)</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">layout</span> <span class="p">=</span> <span class="nc">HBox</span><span class="p">(</span><span class="mf">5.0</span><span class="p">,</span> <span class="n">visibleCB</span><span class="p">,</span> <span class="n">label</span><span class="p">,</span> <span class="n">distanceValueLabel</span><span class="p">)</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">updateItem</span><span class="p">(</span><span class="n">newItem</span><span class="p">:</span> <span class="nc">Distance</span><span class="p">?,</span> <span class="n">isEmpty</span><span class="p">:</span> <span class="nc">Boolean</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">item</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span>
            <span class="n">label</span><span class="p">.</span><span class="nf">textProperty</span><span class="p">().</span><span class="nf">unbind</span><span class="p">()</span>
            <span class="n">distanceValueLabel</span><span class="p">.</span><span class="nf">textProperty</span><span class="p">().</span><span class="nf">unbind</span><span class="p">()</span>
            <span class="n">visibleCB</span><span class="p">.</span><span class="nf">selectedProperty</span><span class="p">().</span><span class="nf">unbindBidirectional</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">visible</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="k">super</span><span class="p">.</span><span class="nf">updateItem</span><span class="p">(</span><span class="n">newItem</span><span class="p">,</span> <span class="n">isEmpty</span><span class="p">)</span>
        <span class="n">graphic</span> <span class="p">=</span> <span class="k">null</span>
        <span class="n">text</span> <span class="p">=</span> <span class="k">null</span>
        <span class="k">if</span> <span class="p">(!</span><span class="n">isEmpty</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">newItem</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span>
                <span class="n">label</span><span class="p">.</span><span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">label</span><span class="p">)</span>
                <span class="n">visibleCB</span><span class="p">.</span><span class="nf">selectedProperty</span><span class="p">().</span><span class="nf">bindBidirectional</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">visible</span><span class="p">)</span>
                <span class="n">distanceValueLabel</span><span class="p">.</span><span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="nc">Bindings</span><span class="p">.</span><span class="nf">concat</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">metric</span><span class="p">,</span> <span class="s">": "</span><span class="p">,</span> <span class="n">it</span><span class="p">.</span><span class="n">distance</span><span class="p">.</span><span class="nf">asString</span><span class="p">()))</span>
                <span class="n">graphic</span> <span class="p">=</span> <span class="n">layout</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">ManifoldListCell</span> <span class="p">:</span> <span class="nc">ListCell</span><span class="p">&lt;</span><span class="nc">Manifold</span><span class="p">&gt;()</span> <span class="p">{</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">label</span> <span class="p">=</span> <span class="nc">TextField</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="nf">focusedProperty</span><span class="p">().</span><span class="nf">subscribe</span> <span class="p">{</span> <span class="n">newVal</span> <span class="p">-&gt;</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">newVal</span><span class="p">)</span> <span class="n">listView</span><span class="p">.</span><span class="n">selectionModel</span><span class="p">.</span><span class="nf">select</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">visibleCB</span> <span class="p">=</span> <span class="nc">CheckBox</span><span class="p">(</span><span class="s">"Visible"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="nf">focusedProperty</span><span class="p">().</span><span class="nf">subscribe</span> <span class="p">{</span> <span class="n">newVal</span> <span class="p">-&gt;</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">newVal</span><span class="p">)</span> <span class="n">listView</span><span class="p">.</span><span class="n">selectionModel</span><span class="p">.</span><span class="nf">select</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">layout</span> <span class="p">=</span> <span class="nc">HBox</span><span class="p">(</span><span class="mf">5.0</span><span class="p">,</span> <span class="n">visibleCB</span><span class="p">,</span> <span class="n">label</span><span class="p">)</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">updateItem</span><span class="p">(</span><span class="n">newItem</span><span class="p">:</span> <span class="nc">Manifold</span><span class="p">?,</span> <span class="n">isEmpty</span><span class="p">:</span> <span class="nc">Boolean</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">item</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span>
            <span class="n">label</span><span class="p">.</span><span class="nf">textProperty</span><span class="p">().</span><span class="nf">unbindBidirectional</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">label</span><span class="p">)</span>
            <span class="n">visibleCB</span><span class="p">.</span><span class="nf">selectedProperty</span><span class="p">().</span><span class="nf">unbindBidirectional</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">visible</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="k">super</span><span class="p">.</span><span class="nf">updateItem</span><span class="p">(</span><span class="n">newItem</span><span class="p">,</span> <span class="n">isEmpty</span><span class="p">)</span>
        <span class="n">graphic</span> <span class="p">=</span> <span class="k">null</span>
        <span class="n">text</span> <span class="p">=</span> <span class="k">null</span>
        <span class="k">if</span> <span class="p">(!</span><span class="n">isEmpty</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">newItem</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span>
                <span class="n">label</span><span class="p">.</span><span class="nf">textProperty</span><span class="p">().</span><span class="nf">bindBidirectional</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">label</span><span class="p">)</span>
                <span class="n">visibleCB</span><span class="p">.</span><span class="nf">selectedProperty</span><span class="p">().</span><span class="nf">bindBidirectional</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">visible</span><span class="p">)</span>
                <span class="n">graphic</span> <span class="p">=</span> <span class="n">layout</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="the-controller">The Controller</h2>

<p>Here’s the code for the Controller:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">FromFxmlController</span><span class="p">(</span><span class="n">sharedElements</span><span class="p">:</span> <span class="nc">SharedElements</span><span class="p">,</span> <span class="n">externalFunctions</span><span class="p">:</span> <span class="nc">SharedFunctions</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">model</span> <span class="p">=</span> <span class="nc">FromFxmlModel</span><span class="p">(</span><span class="n">sharedElements</span><span class="p">)</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">viewBuilder</span> <span class="p">=</span> <span class="nc">FromFxmlViewBuilder</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="k">this</span><span class="o">::</span><span class="n">dataOperation</span><span class="p">,</span> <span class="k">this</span><span class="o">::</span><span class="n">generalOperation</span><span class="p">)</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">interactor</span> <span class="p">=</span> <span class="nc">FromFxmlInteractor</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="n">externalFunctions</span><span class="p">)</span>

    <span class="k">fun</span> <span class="nf">getView</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="n">viewBuilder</span><span class="p">.</span><span class="nf">build</span><span class="p">()</span> <span class="nf">asFileDrop</span> <span class="p">{</span>
        <span class="nf">runStandardVoidTask</span><span class="p">({</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">loadUmap</span><span class="p">(</span><span class="n">it</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="p">},</span> <span class="p">{</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">completeLoadUmap</span><span class="p">()</span> <span class="p">})</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">dataOperation</span><span class="p">(</span><span class="n">window</span><span class="p">:</span> <span class="nc">Window</span><span class="p">,</span> <span class="n">operation</span><span class="p">:</span> <span class="nc">FileOperation</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">when</span> <span class="p">(</span><span class="n">operation</span><span class="p">)</span> <span class="p">{</span>
            <span class="nc">FileOperation</span><span class="p">.</span><span class="nc">UMAP_SAVE</span> <span class="p">-&gt;</span> <span class="nf">saveUmap</span><span class="p">(</span><span class="n">window</span><span class="p">)</span>
            <span class="nc">FileOperation</span><span class="p">.</span><span class="nc">UMAP_LOAD</span> <span class="p">-&gt;</span> <span class="nf">chooseAndloadUmap</span><span class="p">(</span><span class="n">window</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">generalOperation</span><span class="p">(</span><span class="n">operation</span><span class="p">:</span> <span class="nc">GeneralOperation</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">when</span> <span class="p">(</span><span class="n">operation</span><span class="p">)</span> <span class="p">{</span>
            <span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">BUILD_CLUSTER</span> <span class="p">-&gt;</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">buildCluster</span><span class="p">()</span>
            <span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">GENERATE</span> <span class="p">-&gt;</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">generate</span><span class="p">()</span>
            <span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">CLEAR_ALL</span> <span class="p">-&gt;</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">clearAll</span><span class="p">()</span>
            <span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">EXPORT_ALL</span> <span class="p">-&gt;</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">exportAll</span><span class="p">()</span>
            <span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">CLEAR_DISTANCES</span> <span class="p">-&gt;</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">clearDistances</span><span class="p">()</span>
            <span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">PROJECT</span> <span class="p">-&gt;</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">project</span><span class="p">()</span>
            <span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">EXPORT_MATRIX</span> <span class="p">-&gt;</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">exportMatrix</span><span class="p">()</span>
            <span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">SAVE_PROJECTIONS</span> <span class="p">-&gt;</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">saveProjections</span><span class="p">()</span>
            <span class="nc">GeneralOperation</span><span class="p">.</span><span class="nc">RUN_PCA</span> <span class="p">-&gt;</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">runPca</span><span class="p">()</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">saveUmap</span><span class="p">(</span><span class="n">window</span><span class="p">:</span> <span class="nc">Window</span><span class="p">)</span> <span class="p">{</span>
        <span class="nc">FileChooser</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">title</span> <span class="p">=</span> <span class="s">"Choose UMAP Config file output.."</span>
            <span class="n">initialFileName</span> <span class="p">=</span> <span class="s">"UmapConfig.json"</span>
            <span class="n">initialDirectory</span> <span class="p">=</span> <span class="n">model</span><span class="p">.</span><span class="n">latestDir</span><span class="p">.</span><span class="n">value</span> <span class="o">?:</span> <span class="nc">File</span><span class="p">(</span><span class="s">"."</span><span class="p">)</span>
        <span class="p">}.</span><span class="nf">showSaveDialog</span><span class="p">(</span><span class="n">window</span><span class="p">)</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span>
            <span class="nf">runStandardVoidTask</span><span class="p">({</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">saveUmap</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">},</span> <span class="p">{</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">completeSaveUmap</span><span class="p">()</span> <span class="p">})</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">chooseAndloadUmap</span><span class="p">(</span><span class="n">window</span><span class="p">:</span> <span class="nc">Window</span><span class="p">)</span> <span class="p">{</span>
        <span class="nc">FileChooser</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">title</span> <span class="p">=</span> <span class="s">"Choose UMAP Config to load..."</span>
            <span class="n">initialDirectory</span> <span class="p">=</span> <span class="n">model</span><span class="p">.</span><span class="n">latestDir</span><span class="p">.</span><span class="n">value</span> <span class="o">?:</span> <span class="nc">File</span><span class="p">(</span><span class="s">"."</span><span class="p">)</span>
        <span class="p">}.</span><span class="nf">showOpenDialog</span><span class="p">(</span><span class="n">window</span><span class="p">)</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span>
            <span class="nf">runStandardVoidTask</span><span class="p">({</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">loadUmap</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">},</span> <span class="p">{</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">completeLoadUmap</span><span class="p">()</span> <span class="p">})</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This is pretty simple.  There’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 <code class="language-plaintext highlighter-rouge">FileChooser</code> as part of a workflow to handle the file operations.</p>

<h2 id="the-model">The Model</h2>

<p>Here is the Model code:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">FromFxmlModel</span><span class="p">(</span><span class="n">sharedElements</span><span class="p">:</span> <span class="nc">SharedElements</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">externallySelectedDistance</span><span class="p">:</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">Distance</span><span class="p">&gt;</span> <span class="p">=</span> <span class="n">sharedElements</span><span class="p">.</span><span class="n">selectedDistance</span>
    <span class="kd">val</span> <span class="py">externallySelectedManifold</span><span class="p">:</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">Manifold</span><span class="p">&gt;</span> <span class="p">=</span> <span class="n">sharedElements</span><span class="p">.</span><span class="n">selectedManifold</span>
    <span class="kd">val</span> <span class="py">toleranceManual</span><span class="p">:</span> <span class="nc">DoubleProperty</span> <span class="p">=</span> <span class="n">sharedElements</span><span class="p">.</span><span class="n">toleranceManual</span>

    <span class="kd">val</span> <span class="py">numberOfComponents</span><span class="p">:</span> <span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">numberOfEpochs</span><span class="p">:</span> <span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="mi">200</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">nearestNeighbour</span><span class="p">:</span> <span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="mi">15</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">negativeSampleRate</span><span class="p">:</span> <span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">localConnectivity</span><span class="p">:</span> <span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">repulsionStr</span><span class="p">:</span> <span class="nc">DoubleProperty</span> <span class="p">=</span> <span class="nc">SimpleDoubleProperty</span><span class="p">(</span><span class="mf">1.0</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">spread</span><span class="p">:</span> <span class="nc">DoubleProperty</span> <span class="p">=</span> <span class="nc">SimpleDoubleProperty</span><span class="p">(</span><span class="mf">1.0</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">minimumDistance</span><span class="p">:</span> <span class="nc">DoubleProperty</span> <span class="p">=</span> <span class="nc">SimpleDoubleProperty</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">opMixRatio</span><span class="p">:</span> <span class="nc">DoubleProperty</span> <span class="p">=</span> <span class="nc">SimpleDoubleProperty</span><span class="p">(</span><span class="mf">0.5</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">targetWeight</span><span class="p">:</span> <span class="nc">DoubleProperty</span> <span class="p">=</span> <span class="nc">SimpleDoubleProperty</span><span class="p">(</span><span class="mf">0.5</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">threshold</span><span class="p">:</span> <span class="nc">DoubleProperty</span> <span class="p">=</span> <span class="nc">SimpleDoubleProperty</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>

    <span class="kd">val</span> <span class="py">numberOfPcaComponents</span><span class="p">:</span> <span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">()</span>
    <span class="kd">val</span> <span class="py">dummyBoolean1</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleBooleanProperty</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">dummyBoolean2</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleBooleanProperty</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">pcaScalingFactor</span><span class="p">:</span> <span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">fitStartIndex</span><span class="p">:</span> <span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">fitEndIndex</span><span class="p">:</span> <span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="mi">50</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">rangeFitting</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleBooleanProperty</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">dummyBoolean4</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleBooleanProperty</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">analysisMethodSvd</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleBooleanProperty</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">analysisMethodPca</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleBooleanProperty</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>

    <span class="kd">val</span> <span class="py">connectorThickness</span><span class="p">:</span> <span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="mi">17</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">connectorColour</span><span class="p">:</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">SimpleObjectProperty</span><span class="p">()</span>
    <span class="kd">val</span> <span class="py">pointToPoint</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleBooleanProperty</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">pointToGroup</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleBooleanProperty</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>

    <span class="kd">val</span> <span class="py">latestDir</span><span class="p">:</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">File</span><span class="p">?&gt;</span> <span class="p">=</span> <span class="nc">SimpleObjectProperty</span><span class="p">()</span>
    <span class="kd">val</span> <span class="py">manifoldDiffuseColour</span><span class="p">:</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">SimpleObjectProperty</span><span class="p">(</span><span class="nc">Color</span><span class="p">.</span><span class="nc">CYAN</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">manifoldWireMeshColour</span><span class="p">:</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">SimpleObjectProperty</span><span class="p">(</span><span class="nc">Color</span><span class="p">.</span><span class="nc">BLACK</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">manifoldSpecularColour</span><span class="p">:</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">SimpleObjectProperty</span><span class="p">(</span><span class="nc">Color</span><span class="p">.</span><span class="nc">BLACK</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">frontCullFace</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleBooleanProperty</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">backCullFace</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleBooleanProperty</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">noneCullFace</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleBooleanProperty</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">fillDrawMode</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleBooleanProperty</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">linesDrawMode</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleBooleanProperty</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">showWireFrame</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="n">sharedElements</span><span class="p">.</span><span class="n">showWireFrame</span>
    <span class="kd">val</span> <span class="py">showControlPoints</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="n">sharedElements</span><span class="p">.</span><span class="n">showControlPoints</span>
    <span class="kd">val</span> <span class="py">useAll</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="n">sharedElements</span><span class="p">.</span><span class="n">useAll</span>
    <span class="kd">val</span> <span class="py">useVisible</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="n">sharedElements</span><span class="p">.</span><span class="n">useVisible</span>
    <span class="kd">val</span> <span class="py">toleranceAuto</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleBooleanProperty</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>

    <span class="kd">val</span> <span class="py">distanceList</span><span class="p">:</span> <span class="nc">ObservableList</span><span class="p">&lt;</span><span class="nc">Distance</span><span class="p">&gt;</span> <span class="p">=</span> <span class="n">sharedElements</span><span class="p">.</span><span class="n">distanceList</span>
    <span class="kd">val</span> <span class="py">manifoldList</span><span class="p">:</span> <span class="nc">ObservableList</span><span class="p">&lt;</span><span class="nc">Manifold</span><span class="p">&gt;</span> <span class="p">=</span> <span class="n">sharedElements</span><span class="p">.</span><span class="n">manifoldList</span>
    <span class="kd">val</span> <span class="py">factorLabelList</span><span class="p">:</span> <span class="nc">ObservableList</span><span class="p">&lt;</span><span class="nc">String</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">FXCollections</span><span class="p">.</span><span class="nf">observableArrayList</span><span class="p">()</span>
    <span class="kd">val</span> <span class="py">selectedFactorLabel</span> <span class="p">=</span> <span class="n">sharedElements</span><span class="p">.</span><span class="n">selectedFactorLabel</span>
    <span class="kd">val</span> <span class="py">selectedDistance</span><span class="p">:</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">Distance</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">SimpleObjectProperty</span><span class="p">()</span>
    <span class="kd">val</span> <span class="py">selectedManifold</span><span class="p">:</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">Manifold</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">SimpleObjectProperty</span><span class="p">()</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="n">sharedElements</span><span class="p">.</span><span class="n">manifoldCullFace</span><span class="p">.</span><span class="nf">bind</span><span class="p">(</span><span class="nc">Bindings</span><span class="p">.</span><span class="nf">createObjectBinding</span><span class="p">({</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">frontCullFace</span><span class="p">.</span><span class="n">value</span><span class="p">)</span> <span class="k">return</span><span class="nd">@createObjectBinding</span> <span class="nc">CullFace</span><span class="p">.</span><span class="nc">FRONT</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">backCullFace</span><span class="p">.</span><span class="n">value</span><span class="p">)</span> <span class="k">return</span><span class="nd">@createObjectBinding</span> <span class="nc">CullFace</span><span class="p">.</span><span class="nc">BACK</span>
            <span class="k">return</span><span class="nd">@createObjectBinding</span> <span class="nc">CullFace</span><span class="p">.</span><span class="nc">NONE</span>
        <span class="p">},</span> <span class="n">frontCullFace</span><span class="p">,</span> <span class="n">backCullFace</span><span class="p">,</span> <span class="n">noneCullFace</span><span class="p">))</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This is just a POJO of JavaFX <code class="language-plaintext highlighter-rouge">Observable</code> classes.  The fields that are tied to the external application are instantiated as references to the corresponding field in <code class="language-plaintext highlighter-rouge">SharedElements</code>.  <code class="language-plaintext highlighter-rouge">SharedElements.manifoldCullFace</code> corresponds to whichever of three <code class="language-plaintext highlighter-rouge">BooleanProperties</code> is <code class="language-plaintext highlighter-rouge">true</code>, and is bound that way.</p>

<p><code class="language-plaintext highlighter-rouge">SharedElements</code> is not exposed to any other component of the MVCI construct, and looks like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">SharedElements</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">distanceList</span><span class="p">:</span> <span class="nc">ObservableList</span><span class="p">&lt;</span><span class="nc">Distance</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">FXCollections</span><span class="p">.</span><span class="nf">observableArrayList</span><span class="p">()</span>
    <span class="kd">val</span> <span class="py">manifoldList</span><span class="p">:</span> <span class="nc">ObservableList</span><span class="p">&lt;</span><span class="nc">Manifold</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">FXCollections</span><span class="p">.</span><span class="nf">observableArrayList</span><span class="p">()</span>
    <span class="kd">val</span> <span class="py">selectedDistance</span><span class="p">:</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">Distance</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">SimpleObjectProperty</span><span class="p">()</span>
    <span class="kd">val</span> <span class="py">selectedManifold</span><span class="p">:</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">Manifold</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">SimpleObjectProperty</span><span class="p">()</span>
    <span class="kd">val</span> <span class="py">toleranceManual</span><span class="p">:</span> <span class="nc">DoubleProperty</span> <span class="p">=</span> <span class="nc">SimpleDoubleProperty</span><span class="p">()</span>
    <span class="kd">val</span> <span class="py">useAll</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleBooleanProperty</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">useVisible</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleBooleanProperty</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">manifoldCullFace</span><span class="p">:</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">CullFace</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">SimpleObjectProperty</span><span class="p">()</span>
    <span class="kd">val</span> <span class="py">showWireFrame</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleBooleanProperty</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">showControlPoints</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleBooleanProperty</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">selectedFactorLabel</span><span class="p">:</span> <span class="nc">StringProperty</span> <span class="p">=</span> <span class="nc">SimpleStringProperty</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>In truth, I got fed up searching through all of the <code class="language-plaintext highlighter-rouge">ManifoldEvents</code> to find out what data was being passed back and forth to other parts of the application.  So I’m sure that this <code class="language-plaintext highlighter-rouge">SharedElements</code> object is missing quite a few elements.  There’s enough here to make the point, though, and without the rest of the application it doesn’t make any difference for this demonstration.</p>

<h2 id="the-interactor">The Interactor</h2>

<p>The last MVCI component is the Interactor:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">FromFxmlInteractor</span><span class="p">(</span><span class="k">private</span> <span class="kd">val</span> <span class="py">model</span><span class="p">:</span> <span class="nc">FromFxmlModel</span><span class="p">,</span> <span class="k">private</span> <span class="kd">val</span> <span class="py">externalFunctions</span><span class="p">:</span> <span class="nc">SharedFunctions</span><span class="p">)</span> <span class="p">{</span>

    <span class="k">private</span> <span class="kd">var</span> <span class="py">umapDto</span><span class="p">:</span> <span class="nc">UmapDto</span><span class="p">?</span> <span class="p">=</span> <span class="k">null</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="nf">createDummyData</span><span class="p">()</span>
        <span class="n">model</span><span class="p">.</span><span class="n">selectedDistance</span><span class="p">.</span><span class="nf">subscribe</span> <span class="p">{</span> <span class="n">oldValue</span><span class="p">,</span> <span class="n">newValue</span> <span class="p">-&gt;</span>
            <span class="n">oldValue</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span>
                <span class="n">model</span><span class="p">.</span><span class="n">connectorThickness</span><span class="p">.</span><span class="nf">unbindBidirectional</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">width</span><span class="p">)</span>
                <span class="n">model</span><span class="p">.</span><span class="n">connectorColour</span><span class="p">.</span><span class="nf">unbindBidirectional</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">colour</span><span class="p">)</span>
            <span class="p">}</span>
            <span class="n">newValue</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span>
                <span class="n">model</span><span class="p">.</span><span class="n">connectorThickness</span><span class="p">.</span><span class="nf">bindBidirectional</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">width</span><span class="p">)</span>
                <span class="n">model</span><span class="p">.</span><span class="n">connectorColour</span><span class="p">.</span><span class="nf">bindBidirectional</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">colour</span><span class="p">)</span>
            <span class="p">}</span>
            <span class="n">model</span><span class="p">.</span><span class="n">externallySelectedDistance</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">newValue</span>
        <span class="p">}</span>
        <span class="n">model</span><span class="p">.</span><span class="n">selectedManifold</span><span class="p">.</span><span class="nf">subscribe</span> <span class="p">{</span> <span class="n">oldValue</span><span class="p">,</span> <span class="n">newValue</span> <span class="p">-&gt;</span>
            <span class="n">oldValue</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span>
                <span class="n">model</span><span class="p">.</span><span class="n">manifoldDiffuseColour</span><span class="p">.</span><span class="nf">unbindBidirectional</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">diffuseColour</span><span class="p">)</span>
                <span class="n">model</span><span class="p">.</span><span class="n">manifoldSpecularColour</span><span class="p">.</span><span class="nf">unbindBidirectional</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">specularColour</span><span class="p">)</span>
                <span class="n">model</span><span class="p">.</span><span class="n">manifoldWireMeshColour</span><span class="p">.</span><span class="nf">unbindBidirectional</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">wireframeColour</span><span class="p">)</span>
            <span class="p">}</span>
            <span class="n">newValue</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span>
                <span class="n">model</span><span class="p">.</span><span class="n">manifoldDiffuseColour</span><span class="p">.</span><span class="nf">bindBidirectional</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">diffuseColour</span><span class="p">)</span>
                <span class="n">model</span><span class="p">.</span><span class="n">manifoldSpecularColour</span><span class="p">.</span><span class="nf">bindBidirectional</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">specularColour</span><span class="p">)</span>
                <span class="n">model</span><span class="p">.</span><span class="n">manifoldWireMeshColour</span><span class="p">.</span><span class="nf">bindBidirectional</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">wireframeColour</span><span class="p">)</span>
            <span class="p">}</span>
            <span class="n">model</span><span class="p">.</span><span class="n">externallySelectedManifold</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">newValue</span>
        <span class="p">}</span>

    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">createDummyData</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">model</span><span class="p">.</span><span class="n">distanceList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="nc">Distance</span><span class="p">(</span><span class="s">"Label 1"</span><span class="p">,</span> <span class="s">"Millimetres"</span><span class="p">,</span> <span class="mf">17.0</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="nc">Color</span><span class="p">.</span><span class="nc">GREEN</span><span class="p">))</span>
        <span class="n">model</span><span class="p">.</span><span class="n">distanceList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="nc">Distance</span><span class="p">(</span><span class="s">"Label 2"</span><span class="p">,</span> <span class="s">"Nanometres"</span><span class="p">,</span> <span class="mf">22.0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="nc">Color</span><span class="p">.</span><span class="nc">CYAN</span><span class="p">))</span>
        <span class="n">model</span><span class="p">.</span><span class="n">distanceList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="nc">Distance</span><span class="p">(</span><span class="s">"Label 3"</span><span class="p">,</span> <span class="s">"Millimetres"</span><span class="p">,</span> <span class="mf">8.0</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="nc">Color</span><span class="p">.</span><span class="nc">AZURE</span><span class="p">))</span>
        <span class="n">model</span><span class="p">.</span><span class="n">distanceList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="nc">Distance</span><span class="p">(</span><span class="s">"Label 4"</span><span class="p">,</span> <span class="s">"Millimetres"</span><span class="p">,</span> <span class="mf">45.0</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="nc">Color</span><span class="p">.</span><span class="nc">RED</span><span class="p">))</span>
        <span class="n">model</span><span class="p">.</span><span class="n">manifoldList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="nc">Manifold</span><span class="p">(</span><span class="s">"Label 1"</span><span class="p">))</span>
        <span class="n">model</span><span class="p">.</span><span class="n">manifoldList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="nc">Manifold</span><span class="p">(</span><span class="s">"Label 2"</span><span class="p">))</span>
        <span class="n">model</span><span class="p">.</span><span class="n">manifoldList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="nc">Manifold</span><span class="p">(</span><span class="s">"Label 3"</span><span class="p">))</span>
        <span class="n">model</span><span class="p">.</span><span class="n">manifoldList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="nc">Manifold</span><span class="p">(</span><span class="s">"Label 4"</span><span class="p">))</span>
    <span class="p">}</span>

    <span class="k">fun</span> <span class="nf">buildCluster</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">externalFunctions</span><span class="p">.</span><span class="n">buildCluster</span><span class="p">.</span><span class="nf">invoke</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">fun</span> <span class="nf">generate</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">externalFunctions</span><span class="p">.</span><span class="n">generate</span><span class="p">.</span><span class="nf">invoke</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">fun</span> <span class="nf">clearAll</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">externalFunctions</span><span class="p">.</span><span class="n">clearAll</span><span class="p">.</span><span class="nf">invoke</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">fun</span> <span class="nf">exportAll</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">externalFunctions</span><span class="p">.</span><span class="n">exportAll</span><span class="p">.</span><span class="nf">invoke</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">fun</span> <span class="nf">clearDistances</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">externalFunctions</span><span class="p">.</span><span class="n">clearDistances</span><span class="p">.</span><span class="nf">invoke</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">fun</span> <span class="nf">project</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">externalFunctions</span><span class="p">.</span><span class="n">project</span><span class="p">.</span><span class="nf">invoke</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">fun</span> <span class="nf">exportMatrix</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">externalFunctions</span><span class="p">.</span><span class="n">exportMatrix</span><span class="p">.</span><span class="nf">invoke</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">fun</span> <span class="nf">saveProjections</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">externalFunctions</span><span class="p">.</span><span class="n">saveProjections</span><span class="p">.</span><span class="nf">invoke</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">fun</span> <span class="nf">runPca</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">externalFunctions</span><span class="p">.</span><span class="n">runPCA</span><span class="p">.</span><span class="nf">invoke</span><span class="p">()</span>
    <span class="p">}</span>


    <span class="k">fun</span> <span class="nf">saveUmap</span><span class="p">(</span><span class="n">file</span><span class="p">:</span> <span class="nc">File</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">model</span><span class="p">.</span><span class="n">latestDir</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">file</span>
    <span class="p">}</span>


    <span class="k">fun</span> <span class="nf">completeSaveUmap</span><span class="p">()</span> <span class="p">{}</span>

    <span class="k">fun</span> <span class="nf">loadUmap</span><span class="p">(</span><span class="n">file</span><span class="p">:</span> <span class="nc">File</span><span class="p">)</span> <span class="p">{</span>
        <span class="nf">println</span><span class="p">(</span><span class="s">"Hey!  Loading a file"</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">fun</span> <span class="nf">completeLoadUmap</span><span class="p">()</span> <span class="p">{}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">init{}</code> section is much like a constructor, and here it creates all of the relationships between elements of the Model that would be considered “business/application logic”.  In this case it is mostly dealing with the relationships between the actively selected Manifold or Distance and some of the other properties.</p>

<p>The 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.</p>

<p>All of the other methods, however, are pretty much in their final form.  They just need to invoke the functional elements provide by <code class="language-plaintext highlighter-rouge">ExternalFunctions</code>, which looks like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">SharedFunctions</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">buildCluster</span><span class="p">:</span> <span class="p">()</span> <span class="p">-&gt;</span> <span class="nc">Unit</span> <span class="p">=</span> <span class="p">{}</span>
    <span class="kd">val</span> <span class="py">generate</span><span class="p">:</span> <span class="p">()</span> <span class="p">-&gt;</span> <span class="nc">Unit</span> <span class="p">=</span> <span class="p">{}</span>
    <span class="kd">val</span> <span class="py">clearAll</span><span class="p">:</span> <span class="p">()</span> <span class="p">-&gt;</span> <span class="nc">Unit</span> <span class="p">=</span> <span class="p">{}</span>
    <span class="kd">val</span> <span class="py">exportAll</span><span class="p">:</span> <span class="p">()</span> <span class="p">-&gt;</span> <span class="nc">Unit</span> <span class="p">=</span> <span class="p">{}</span>
    <span class="kd">val</span> <span class="py">clearDistances</span><span class="p">:</span> <span class="p">()</span> <span class="p">-&gt;</span> <span class="nc">Unit</span> <span class="p">=</span> <span class="p">{}</span>
    <span class="kd">val</span> <span class="py">project</span><span class="p">:</span> <span class="p">()</span> <span class="p">-&gt;</span> <span class="nc">Unit</span> <span class="p">=</span> <span class="p">{}</span>
    <span class="kd">val</span> <span class="py">exportMatrix</span><span class="p">:</span> <span class="p">()</span> <span class="p">-&gt;</span> <span class="nc">Unit</span> <span class="p">=</span> <span class="p">{}</span>
    <span class="kd">val</span> <span class="py">saveProjections</span><span class="p">:</span> <span class="p">()</span> <span class="p">-&gt;</span> <span class="nc">Unit</span> <span class="p">=</span> <span class="p">{}</span>
    <span class="kd">val</span> <span class="py">runPCA</span><span class="p">:</span> <span class="p">()</span> <span class="p">-&gt;</span> <span class="nc">Unit</span> <span class="p">=</span> <span class="p">{}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>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 <code class="language-plaintext highlighter-rouge">ManifoldEvent</code> firings in the original FXML Controller.</p>

<h1 id="conclusion">Conclusion</h1>

<p>I’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’s really just a bunch of <code class="language-plaintext highlighter-rouge">Controls</code> and <code class="language-plaintext highlighter-rouge">Buttons</code> and <code class="language-plaintext highlighter-rouge">Lists</code> that manipulate some data and trigger some actions.</p>

<p>The 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.</p>

<p>I need to stress that the code that I’ve published here runs as a stand-alone application.  It doesn’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.</p>

<p>Clearly, a lot more was done here than just replace the FXML with code, although it’s fairly clear that the layout code is much simpler than the FXML plus FXML Controller from the original.</p>

<h2 id="the-layout">The Layout</h2>

<p>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.</p>

<p>One thing that was immediately clear to me when I came back to it was that none of these <code class="language-plaintext highlighter-rouge">Tabs</code> have anything to do with each other except that they cohabit in the same <code class="language-plaintext highlighter-rouge">TabPane</code>.  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’t be encumbered with the code from the other <code class="language-plaintext highlighter-rouge">Tabs</code>.</p>

<p>Furthermore, each of these <code class="language-plaintext highlighter-rouge">Tabs</code> could have their own, independent, MVCI structure associated with them.  There could be a “master” MVCI structure associated with the <code class="language-plaintext highlighter-rouge">TabPane</code> 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.</p>

<div class="notice--primary">
 <img src="/assets/logos/brain.png" alt="primary" style="float:left;margin-right: 10px" />
 <p style="overflow:auto; float:none">
   Even if a Button on one Tab required the external function it invoked to use data from another Tab this wouldn't matter because the Button actions aren't transferring any data - that's already handled by the shared data elements.
 </p>
</div>

<h2 id="using-a-framework">Using a Framework</h2>

<p>I simply cannot imagine building anything like this without implementing a framework of some kind.</p>

<p>One of the things that became glaringly apparent after the conversion was that this screen, aside from some file handling, doesn’t actually <em>do</em> anything itself.  You can see this just from looking at the Interactor.  It doesn’t have much code that actually does anything.  It just dispatches actions off to some other part of the application.</p>

<p>Certainly, if you had written the application, or if you were very familiar with the entire application, you’d <em>know</em> that this screen didn’t actually do anything.  But this is absolutely <strong>not</strong> clear from a casual glance at the original code.</p>

<h2 id="reactive-vs-imperative-design">Reactive vs Imperative Design</h2>

<p>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.</p>

<p>Using 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 <em>all</em> defined inside that single object.</p>

<h2 id="coupling">Coupling</h2>

<p>Coupling in this new design is extremely controlled, and easy to understand.</p>

<p>The Model is the main source of coupling, but it also isolates as well.  There’s no way for anything outside of the View to know if a <code class="language-plaintext highlighter-rouge">Boolean</code> value in the Model is presented to the user via a <code class="language-plaintext highlighter-rouge">RadioButton</code>, a <code class="language-plaintext highlighter-rouge">ToggleButton</code>, a <code class="language-plaintext highlighter-rouge">CheckBox</code> or some custom <code class="language-plaintext highlighter-rouge">Control</code>.  But, no matter how it’s handled in the View, the Interactor can always simply deal with the <code class="language-plaintext highlighter-rouge">Boolean</code> value that it is bound to.</p>

<p>In a similar manner, the <code class="language-plaintext highlighter-rouge">SharedElements</code> is the main source of both coupling and isolation between this screen and the rest of the application.</p>

<div class="notice--primary">
 <img src="/assets/logos/brain.png" alt="primary" style="float:left;margin-right: 10px" />
 <p style="overflow:auto; float:none">
   Coupling is usually the single biggest source of unecessary complexity in any application.  Controlling coupling is the best way to improve code quality.
 </p>
</div>

<p>I simply cannot stress this too much.  Virtually every good (or “clean”) coding technique, is designed to control and eliminate coupling as much possible.  Looking at this “Trinity” project, excessive coupling is everywhere and it makes everything much more complicated than it needs to be.  I’ve tried to limit coupling as much as possible in my version, and I think it is reflected in the lack of complexity.</p>

<h2 id="the-kotlin">The Kotlin</h2>

<p>It’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 <code class="language-plaintext highlighter-rouge">Nodes</code> is done in a single line.</p>

<p>Maybe (probably?) you don’t want to learn Kotlin to do this.  In Java, I think you’d have to create classic builders with the configuration elements included as decorators.  But you could do a lot of this stuff that way.</p>

<p>What I will say is that if you are looking for a tool to make layout creation and maintenance easier, you’ll get a lot more mileage out of learning Kotlin than you will by mastering SceneBuilder and FXML.</p>]]></content><author><name>Dave Barrett</name><email>pragmaticcoding8@gmail.com</email></author><category term="javafx" /><summary type="html"><![CDATA[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.]]></summary></entry><entry><title type="html">Reactive Programming in JavaFX</title><link href="https://www.pragmaticcoding.ca/javafx/elements/reactive-javafx" rel="alternate" type="text/html" title="Reactive Programming in JavaFX" /><published>2025-07-16T17:00:00+00:00</published><updated>2025-07-16T17:00:00+00:00</updated><id>https://www.pragmaticcoding.ca/javafx/elements/reactive-javafx</id><content type="html" xml:base="https://www.pragmaticcoding.ca/javafx/elements/reactive-javafx"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>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?</p>

<p>People always tend to focus in these discussions on the look and feel of Swing.  Out-of-the-box GUI’s built in Swing tend to look like refuges from the 1990’s, which isn’t surprising considering the age of Swing.  JavaFX tends to look more modern by default, although still very “corporate”.  But it seems easier to customize the look and feel with JavaFX.</p>

<p>But I’m not sure that any of that is enough reason to go through a painful conversion from Swing to JavaFX.</p>

<div class="notice--primary">
 <img src="/assets/logos/brain.png" alt="primary" style="float:left;margin-right: 10px" />
 <p style="overflow:auto; float:none">
   The most important difference between JavaFX and Swing is that JavaFX is designed to support Reactive GUI development.<br />
 </p>
</div>

<p>When you look at the scope of Observable classes and utilities this - at least to me - becomes obvious.</p>

<p>Yes, Swing <em>does</em> support the “Observer Pattern”, 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 <code class="language-plaintext highlighter-rouge">Nodes</code>, make it virtually trivial to implement a Reactive framework.</p>

<div class="notice--primary">
 <img src="/assets/logos/brain.png" alt="primary" style="float:left;margin-right: 10px" />
 <p style="overflow:auto; float:none">
   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.<br />
 </p>
</div>

<p>The problem, though, is that it is also possible to write a JavaFX application in an imperative/declarative fashion.  And it works, although it’s not as clean as using a Reactive approach.</p>

<p>Even worse, nobody talks about “Reactive JavaFX”.  It’s not mentioned at all in the Oracle documentation or tutorials.  It’s a shame.</p>

<p>In this article we are going to look at how Reactive GUI’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’ll see how Reactive design makes the application simpler by removing coupling.  Finally, we’ll look at how Reactive Design fits in with frameworks like MVC, MVVM and MVCI.</p>

<h1 id="what-is-reactive-design">What is Reactive Design?</h1>

<p>Any “Reactive” design involves creating a data representation of the “State” 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.</p>

<p>The layout programmer’s job, therefore, is define how the layout will look and behave with any given set of values in the State.</p>

<p>When you start to work with a Reactive system, you realize that there are two kinds of things that can happen in a GUI:</p>

<ol>
  <li>
    <p>Changes to State<br />For instance a user types something in a textfield, moves a slider, or selects an item in a list.</p>
  </li>
  <li>
    <p>Actions<br />This could be a user clicking on a button, or actions associated with the user moving a slider or selecting an item in a list.</p>
  </li>
</ol>

<p>In a Declarative (or “Imperative”) approach to GUI design, everything is an “action”.  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 “focus lost” action might be triggered that would cause code to run that moves data around or changes the layout.</p>

<p>A Reactive system usually looks something like this:</p>

<p><img src="/assets/elements/Reactive.png" alt="Diagram" /></p>

<p>The layout has elements that control how it behaves that are “bound” or “synchronized” in both directions with the “Presentation Model”, which is the data representation of “State”.</p>

<p>Additionally, 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 “Action Handler” could tell the layout to take some action, although I’m not sure that all Reactive systems allow this.</p>

<h1 id="types-of-reactive-systems">Types of Reactive Systems</h1>

<p>Personally, I’ve encountered three different Reactive systems: JavaFX, Jetpack Compose, and React.</p>

<p>Jetpack 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.</p>

<p>From what I have seen, these break down into two different approaches to Reactivity.  I’ve never seen them defined anywhere, so I am going to call them “Compositional Reactivity” and “Layout Reactivity”.  Let’s take a look at them…</p>

<h2 id="compostional-reactivity">Compostional Reactivity</h2>

<p>This is the implementation of Reactivity that is use by both Jetpack Compose and React.</p>

<p>Essentially, the code that “composes” 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.</p>

<p>With 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.</p>

<p>Here’s an example from the official Jetpack Compose tutorial:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Composable</span>
<span class="k">fun</span> <span class="nf">WaterCounter</span><span class="p">(</span><span class="n">modifier</span><span class="p">:</span> <span class="nc">Modifier</span> <span class="p">=</span> <span class="nc">Modifier</span><span class="p">)</span> <span class="p">{</span>
   <span class="nc">Column</span><span class="p">(</span><span class="n">modifier</span> <span class="p">=</span> <span class="n">modifier</span><span class="p">.</span><span class="nf">padding</span><span class="p">(</span><span class="mi">16</span><span class="p">.</span><span class="n">dp</span><span class="p">))</span> <span class="p">{</span>
       <span class="c1">// Changes to count are now tracked by Compose</span>
       <span class="kd">val</span> <span class="py">count</span><span class="p">:</span> <span class="nc">MutableState</span><span class="p">&lt;</span><span class="nc">Int</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nf">mutableStateOf</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

       <span class="nc">Text</span><span class="p">(</span><span class="s">"You've had ${count.value} glasses."</span><span class="p">)</span>
        <span class="nc">Button</span><span class="p">(</span><span class="n">onClick</span> <span class="p">=</span> <span class="p">{</span> <span class="n">count</span><span class="p">.</span><span class="n">value</span><span class="p">++</span> <span class="p">},</span> <span class="nc">Modifier</span><span class="p">.</span><span class="nf">padding</span><span class="p">(</span><span class="n">top</span> <span class="p">=</span> <span class="mi">8</span><span class="p">.</span><span class="n">dp</span><span class="p">))</span> <span class="p">{</span>
           <span class="nc">Text</span><span class="p">(</span><span class="s">"Add one"</span><span class="p">)</span>
       <span class="p">}</span>
   <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">@Composable</code> 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 <code class="language-plaintext highlighter-rouge">Column</code> and sticks a <code class="language-plaintext highlighter-rouge">Text</code> and a <code class="language-plaintext highlighter-rouge">Button</code> in it.</p>

<p>The line that instantiates <code class="language-plaintext highlighter-rouge">count</code> as <code class="language-plaintext highlighter-rouge">MutableState&lt;Int&gt;</code> is where it defines the element of State that the this function is dependent on.  Changes to <code class="language-plaintext highlighter-rouge">count</code> will trigger re-execution of this code - but let’s just call it “recomposition”.</p>

<p>Notice that the <code class="language-plaintext highlighter-rouge">Text</code> just shows a static String that includes the current value of <code class="language-plaintext highlighter-rouge">count</code> at the time that it executes.  There is no way to change the value displayed by a <code class="language-plaintext highlighter-rouge">Text</code>.</p>

<p>The <code class="language-plaintext highlighter-rouge">Button</code> just increments <code class="language-plaintext highlighter-rouge">count</code>.  Here you see how an action updates the Presentation Model, and that update triggers the recomposition of the layout.</p>

<p>As soon as <code class="language-plaintext highlighter-rouge">count</code> changes, this piece of layout is determined to be defunct and needs to be re-composed, so this function will be re-executed, the <code class="language-plaintext highlighter-rouge">Column</code> will be rebuilt and the <code class="language-plaintext highlighter-rouge">Text</code> will be recreated and populated with a String that now contains the new value of <code class="language-plaintext highlighter-rouge">count</code>.</p>

<p>To the user, it just looks like the value in the <code class="language-plaintext highlighter-rouge">Text</code> changed.  But from a programming perspective, a piece of the layout has been recomposed.</p>

<p>You can put any logic that you want in there.  Try this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Composable</span>
<span class="k">fun</span> <span class="nf">WaterCounter</span><span class="p">(</span><span class="n">modifier</span><span class="p">:</span> <span class="nc">Modifier</span> <span class="p">=</span> <span class="nc">Modifier</span><span class="p">)</span> <span class="p">{</span>
   <span class="nc">Column</span><span class="p">(</span><span class="n">modifier</span> <span class="p">=</span> <span class="n">modifier</span><span class="p">.</span><span class="nf">padding</span><span class="p">(</span><span class="mi">16</span><span class="p">.</span><span class="n">dp</span><span class="p">))</span> <span class="p">{</span>
       <span class="c1">// Changes to count are now tracked by Compose</span>
       <span class="kd">val</span> <span class="py">count</span><span class="p">:</span> <span class="nc">MutableState</span><span class="p">&lt;</span><span class="nc">Int</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nf">mutableStateOf</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

       <span class="k">if</span> <span class="p">(</span><span class="n">count</span> <span class="p">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
          <span class="nc">Text</span><span class="p">(</span><span class="s">"Glug...glug...glug..."</span><span class="p">)</span>
       <span class="p">}</span>
       <span class="nc">Text</span><span class="p">(</span><span class="s">"You've had ${count.value} glasses."</span><span class="p">)</span>
       <span class="nc">Button</span><span class="p">(</span><span class="n">onClick</span> <span class="p">=</span> <span class="p">{</span> <span class="n">count</span><span class="p">.</span><span class="n">value</span><span class="p">++</span> <span class="p">},</span> <span class="nc">Modifier</span><span class="p">.</span><span class="nf">padding</span><span class="p">(</span><span class="n">top</span> <span class="p">=</span> <span class="mi">8</span><span class="p">.</span><span class="n">dp</span><span class="p">))</span> <span class="p">{</span>
           <span class="nc">Text</span><span class="p">(</span><span class="s">"Add one"</span><span class="p">)</span>
       <span class="p">}</span>
   <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The first time through <code class="language-plaintext highlighter-rouge">count</code> will be “0” and that “Glug..glug…glug…” <code class="language-plaintext highlighter-rouge">Text</code> will not appear.  Every other time, count will be non-zero and that <code class="language-plaintext highlighter-rouge">Text</code> will appear.</p>

<h2 id="layout-reactivity">Layout Reactivity</h2>

<p>With “Layout Reactivity” 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.</p>

<p>Let’s see how you would do the same type of counting exercise as in the Jetpack Compose example:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fun</span> <span class="nf">waterCounter</span><span class="p">()</span> <span class="p">:</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">counter</span> <span class="p">:</span><span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
    <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Glug...glug...glug..."</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
       <span class="nf">visibleProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">counter</span><span class="p">.</span><span class="nf">greaterThan</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span>
       <span class="nf">managedProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="nf">visibleProperty</span><span class="p">())</span>
    <span class="p">}</span>
    <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
       <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">counter</span><span class="p">.</span><span class="nf">map</span><span class="p">{</span> <span class="s">"You've had $it glasses."</span> <span class="p">})</span>
    <span class="p">}</span>
    <span class="n">childern</span> <span class="p">+=</span> <span class="nc">Button</span><span class="p">(</span><span class="s">"Add one"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
       <span class="nf">setOnClick</span> <span class="p">{</span> <span class="n">evt</span> <span class="p">-&gt;</span> <span class="n">counter</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="mi">1</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Here we have a static layout that behaves dynamically:  It’s “static” in that layout itself doesn’t change.  No <code class="language-plaintext highlighter-rouge">Nodes</code> are added or removed from the layout.  It behaves dynamically, because the individual <code class="language-plaintext highlighter-rouge">Nodes</code> do things in reaction to the Presentation Model.</p>

<p>That “Glug…” <code class="language-plaintext highlighter-rouge">Text</code> is always there.  However, its visibility is bound to whether or not <code class="language-plaintext highlighter-rouge">count</code> is greater than zero.  Its <code class="language-plaintext highlighter-rouge">managed Property</code> which controls whether or not the layout manager gives it space on the screen is also bound to the same condition.  When <code class="language-plaintext highlighter-rouge">count</code> is zero, it’s neither visible, nor given any space on the screen.</p>

<p>The text in the second <code class="language-plaintext highlighter-rouge">Label</code> is bound to <code class="language-plaintext highlighter-rouge">counter</code> through a <code class="language-plaintext highlighter-rouge">map{}</code> functiont that provides the rest of the String.</p>

<p>Once again, the <code class="language-plaintext highlighter-rouge">Button</code> simply triggers an action that updates the Presentation Model which, in turn, causes the layout to react and change its behaviour.</p>

<h2 id="these-are-very-different-approaches">These are Very Different Approaches</h2>

<p>I think you can see that to a user, there is no difference between either approach.  The GUI’s appear to behave in an identical manner.</p>

<p>It’s the implementation which is <strong>completely</strong> different.</p>

<p>In the Jetpack Compose approach, that “Glug…” <code class="language-plaintext highlighter-rouge">Text</code> didn’t even exist in the layout before the <code class="language-plaintext highlighter-rouge">Button</code> was clicked.  In the JavaFX version, it was always there - just invisible.</p>

<p>The truth is that both approaches boil down to the same thing.  At some point the screen has to be redrawn to show that “Glug…” <code class="language-plaintext highlighter-rouge">Label\Text</code>.  But with JavaFX, that redrawing happens deep, deep inside the guts of the Layout Manager, and as programmers we don’t ever need to think about it.  In Jetpack Compose and React, that’s just about <em>all</em> that you think about - “How will you build the layout when the data looks like this..or this..or this?”</p>

<p>Obviously, Jetpack Compose and React are far more popular than JavaFX, so when people think about “Reactive” systems, they are probably imagining a Compositionally Reactive design.</p>

<h1 id="how-reactive-design-makes-systems-better">How Reactive Design Makes Systems Better</h1>

<p>Back in the introduction, I claimed that Reactive Design makes systems nearly an order of magnitude easier to enhance, debug and maintain.  Let’s look at how that might be…</p>

<h2 id="the-same-example-in-an-imperative-design">The Same Example In an Imperative Design</h2>

<p>Before we go any further, let’s look at the example, and how you would write it in JavaFX like it was Swing:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fun</span> <span class="nf">waterCounter</span><span class="p">()</span> <span class="p">:</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">counter</span> <span class="p">:</span> <span class="nc">Int</span> <span class="p">=</span> <span class="mi">0</span>
    <span class="kd">val</span> <span class="py">glugLabel</span> <span class="p">=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Glug...glug...glug..."</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">countLabel</span> <span class="p">=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"You've had $counter glasses."</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">button</span> <span class="p">=</span> <span class="nc">Button</span><span class="p">(</span><span class="s">"Add one"</span><span class="p">)</span>
    <span class="n">button</span><span class="p">.</span><span class="nf">setOnAction</span> <span class="p">{</span> <span class="n">evt</span> <span class="p">-&gt;</span>
       <span class="n">countLabel</span><span class="p">.</span><span class="nf">setText</span><span class="p">(</span><span class="s">"You've had $counter glasses."</span><span class="p">)</span>
       <span class="nf">getChildren</span><span class="p">().</span><span class="nf">setAll</span><span class="p">(</span><span class="nf">listOf</span><span class="p">(</span><span class="n">glugLabel</span><span class="p">,</span> <span class="n">countLabel</span><span class="p">,</span> <span class="n">button</span><span class="p">))</span>
    <span class="p">}</span>
    <span class="n">children</span> <span class="p">+=</span> <span class="nf">listOf</span><span class="p">(</span><span class="n">countLabel</span><span class="p">,</span> <span class="n">button</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Instead of a <code class="language-plaintext highlighter-rouge">Binding</code>, the value of <code class="language-plaintext highlighter-rouge">countLabel</code> is changed via <code class="language-plaintext highlighter-rouge">setText()</code>, and instead of using a static layout with <code class="language-plaintext highlighter-rouge">glugLabel</code> toggling between invisible and visible, the layout is modified to add <code class="language-plaintext highlighter-rouge">glugLabel</code> in response to the <code class="language-plaintext highlighter-rouge">Button</code> click.</p>

<h2 id="coupling">Coupling</h2>

<p>One thing you’ll notice in this Imperative example is that we now have variables to hold <code class="language-plaintext highlighter-rouge">Labels</code> and <code class="language-plaintext highlighter-rouge">Button</code>.  That’s because we need references to them so that we can update them from inside the <code class="language-plaintext highlighter-rouge">Button</code> action.  You’ll also see that <code class="language-plaintext highlighter-rouge">button</code> is calling a method on the enclosing VBox - <code class="language-plaintext highlighter-rouge">getChildren()</code>.</p>

<p>This are examples of “coupling”.</p>

<div class="notice--primary">
 <img src="/assets/logos/brain.png" alt="primary" style="float:left;margin-right: 10px" />
 <p style="overflow:auto; float:none">
   Coupling is probably the single most important factor that makes applications difficult to understand, maintain and enhance.<br />
 </p>
</div>

<p>When we expose <code class="language-plaintext highlighter-rouge">glugLabel</code>, <code class="language-plaintext highlighter-rouge">countLabel</code> and the enclosing <code class="language-plaintext highlighter-rouge">VBox</code> to <code class="language-plaintext highlighter-rouge">button</code>, we don’t just reveal their existence, but also their implementation.</p>

<p>Once we’ve shared the implementation details of something, then we’ve created coupling.  It’s coupling because we cannot change that implementation without looking at how that implementation knowledge is used through the system.</p>

<p>Even here, where the variable scope is just 10 lines of code, it can cause problems.  Imagine that we wanted to change the text of <code class="language-plaintext highlighter-rouge">counterLabel</code> to this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">countLabel</span> <span class="p">=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"You've had $counter pints of beer."</span><span class="p">)</span>
</code></pre></div></div>
<p>We’ll have a problem if we just stop there, because <code class="language-plaintext highlighter-rouge">button</code> is going to set it back to <code class="language-plaintext highlighter-rouge">"You've had $counter glasses."</code> as soon as it is clicked.  Any change to the implementation of <code class="language-plaintext highlighter-rouge">countLabel</code> means our application might be broken.</p>

<p>Let’s say that we decided to change our implementation of <code class="language-plaintext highlighter-rouge">counterLabel</code> to be something other than a <code class="language-plaintext highlighter-rouge">Label</code>.  Perhaps an <code class="language-plaintext highlighter-rouge">ImageView</code> sprite with a number of <code class="language-plaintext highlighter-rouge">Images</code> of different numbers of beer glasses, tied somehow to <code class="language-plaintext highlighter-rouge">counter</code>.  Now, our <code class="language-plaintext highlighter-rouge">Button</code> action won’t work at all, and we’ll have to change its logic to manipulate an <code class="language-plaintext highlighter-rouge">ImageView</code> instead.  At least here we’ll get a compiler error.</p>

<p>This situation gets worse if <code class="language-plaintext highlighter-rouge">button</code> is defined somewhere else.  Then, we’ll need to push <code class="language-plaintext highlighter-rouge">countLabel</code> and our <code class="language-plaintext highlighter-rouge">VBox</code> up to fields in our layout class, so that we can access them with <code class="language-plaintext highlighter-rouge">button</code>:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code>   <span class="k">private</span> <span class="kd">val</span> <span class="py">countLabel</span> <span class="p">=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"You've had 0 glasses."</span><span class="p">)</span>
   <span class="k">private</span> <span class="kd">val</span> <span class="py">countPane</span> <span class="p">:</span> <span class="nc">Pane</span> <span class="p">=</span> <span class="nf">waterCounter</span><span class="p">()</span>
     <span class="p">.</span>
     <span class="p">.</span>
     <span class="p">.</span>
   <span class="k">fun</span> <span class="nf">waterCounter</span><span class="p">()</span> <span class="p">:</span> <span class="nc">Pane</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
         <span class="p">.</span>
         <span class="p">.</span>
      <span class="n">children</span> <span class="p">+=</span> <span class="n">countLabel</span>   
         <span class="p">.</span>
         <span class="p">.</span>
   <span class="p">}</span>   
      <span class="p">.</span>
      <span class="p">.</span>
      <span class="p">.</span>
   <span class="k">fun</span> <span class="nf">someOtherPane</span><span class="p">()</span> <span class="p">:</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">().</span><span class="nf">apply</span><span class="p">{</span>
         <span class="p">.</span>
         <span class="p">.</span>
       <span class="kd">val</span> <span class="py">counter</span> <span class="p">:</span> <span class="nc">Int</span> <span class="p">=</span> <span class="mi">0</span>
       <span class="kd">val</span> <span class="py">glugLabel</span> <span class="p">=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Glug...glug...glug..."</span><span class="p">)</span>
       <span class="kd">val</span> <span class="py">button</span> <span class="p">=</span> <span class="nc">Button</span><span class="p">(</span><span class="s">"Add one"</span><span class="p">)</span>
       <span class="n">button</span><span class="p">.</span><span class="nf">setOnAction</span> <span class="p">{</span> <span class="n">evt</span> <span class="p">-&gt;</span>
          <span class="n">countLabel</span><span class="p">.</span><span class="nf">setText</span><span class="p">(</span><span class="s">"You've had ${counter++} glasses."</span><span class="p">)</span>
          <span class="n">countPane</span><span class="p">.</span><span class="nf">getChildren</span><span class="p">().</span><span class="nf">setAll</span><span class="p">(</span><span class="nf">listOf</span><span class="p">(</span><span class="o">..</span><span class="p">.,</span> <span class="n">glugLabel</span><span class="p">,</span> <span class="n">countLabel</span><span class="p">,</span> <span class="o">..</span><span class="p">.))</span>
       <span class="p">}</span>
       <span class="n">children</span> <span class="p">+=</span> <span class="n">button</span>
   <span class="p">}</span>    
</code></pre></div></div>
<p>Now, we need to know the contents of <code class="language-plaintext highlighter-rouge">countPane</code> in order to alter it without losing things already in it.  Any changes to <code class="language-plaintext highlighter-rouge">waterCounter()</code> will require changes to <code class="language-plaintext highlighter-rouge">someOtherPane()</code> as well.  Also, <code class="language-plaintext highlighter-rouge">countPane</code> has to be exposed as <em>at least</em> a <code class="language-plaintext highlighter-rouge">Pane</code> since <code class="language-plaintext highlighter-rouge">Region</code> doesn’t expose <code class="language-plaintext highlighter-rouge">getChildren()</code> publicly.</p>

<p>Even worse, in any implementation of this, we still have <code class="language-plaintext highlighter-rouge">button</code> reconfiguring <code class="language-plaintext highlighter-rouge">countLabel</code>.  Ideally, the only thing that configures <code class="language-plaintext highlighter-rouge">countLabel</code> should be <code class="language-plaintext highlighter-rouge">countLabel</code> itself, and the layout code that instantiates <code class="language-plaintext highlighter-rouge">countLabel</code>.</p>

<p>Finally, in any case where you have a reference to an object (<code class="language-plaintext highlighter-rouge">Node</code> 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 <code class="language-plaintext highlighter-rouge">waterCounter(){}</code>, but in the last example the scope was the entire layout building class.</p>

<p>In this lastest example, if you did decide to change <code class="language-plaintext highlighter-rouge">countLabel</code> to something else, maybe that sprite <code class="language-plaintext highlighter-rouge">ImageView</code> or a <code class="language-plaintext highlighter-rouge">Slider</code> or a pie chart, you’d have to make sure that no other part of the layout class uses the implementation of <code class="language-plaintext highlighter-rouge">countLabel</code>.  Maybe there’s some other <code class="language-plaintext highlighter-rouge">Node</code> that looks at the length of <code class="language-plaintext highlighter-rouge">countLabel.getText()</code> to determine some aspect of its layout.</p>

<h2 id="the-presentation-model-as-a-layout-builder-field">The Presentation Model as a Layout Builder Field</h2>

<p>In the first Reactive example, we had <code class="language-plaintext highlighter-rouge">counter</code> instantiated as a <code class="language-plaintext highlighter-rouge">StringProperty</code> variable reference local to <code class="language-plaintext highlighter-rouge">waterCounter(){}</code>.  If we move <code class="language-plaintext highlighter-rouge">button</code> somewhere else, then we’ll need to expand the scope of <code class="language-plaintext highlighter-rouge">counter</code> to a field in the layout builder class.  The code would look like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="kd">val</span> <span class="py">counter</span> <span class="p">:</span> <span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
      <span class="p">.</span>
      <span class="p">.</span>
      <span class="p">.</span>
<span class="k">fun</span> <span class="nf">waterCounter</span><span class="p">()</span> <span class="p">:</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
    <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Glug...glug...glug..."</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
       <span class="nf">visibleProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">counter</span><span class="p">.</span><span class="nf">greaterThan</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span>
       <span class="nf">managedProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="nf">visibleProperty</span><span class="p">())</span>
    <span class="p">}</span>
    <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
       <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">counter</span><span class="p">.</span><span class="nf">map</span><span class="p">{</span> <span class="s">"You've had $it glasses."</span> <span class="p">})</span>
    <span class="p">}</span>

<span class="p">}</span>
     <span class="p">.</span>
     <span class="p">.</span>
     <span class="p">.</span>
<span class="k">fun</span> <span class="nf">someOtherPane</span><span class="p">()</span> <span class="p">{</span>
         <span class="p">.</span>
         <span class="p">.</span>
         <span class="p">.</span>
    <span class="n">childern</span> <span class="p">+=</span> <span class="nc">Button</span><span class="p">(</span><span class="s">"Add one"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="nf">setOnClick</span> <span class="p">{</span> <span class="n">evt</span> <span class="p">-&gt;</span> <span class="n">counter</span><span class="p">.</span><span class="n">value</span> <span class="p">+=</span> <span class="mi">1</span> <span class="p">}</span>
    <span class="p">}</span>         
         <span class="p">.</span>
         <span class="p">.</span>
         <span class="p">.</span>
<span class="p">}</span>     
</code></pre></div></div>
<p>You can see that <code class="language-plaintext highlighter-rouge">button</code> remains completely ignorant of the existence of the counter <code class="language-plaintext highlighter-rouge">Label</code>.</p>

<p>There still is a dependency - there has to be.  The counter <code class="language-plaintext highlighter-rouge">Label</code> is dependent on <code class="language-plaintext highlighter-rouge">counter</code>, and the <code class="language-plaintext highlighter-rouge">Button</code> knows about the nature of <code class="language-plaintext highlighter-rouge">counter</code>.</p>

<p>There are some things that make this a very different kind of coupling:</p>

<ol>
  <li>The nature of <code class="language-plaintext highlighter-rouge">counter</code> is very generic.<br />It is an <code class="language-plaintext highlighter-rouge">IntegerProperty</code>, which in the world of <code class="language-plaintext highlighter-rouge">Properties</code> is no more specific than declaring a regular variable as <code class="language-plaintext highlighter-rouge">Int</code>.</li>
  <li>Neither the counter <code class="language-plaintext highlighter-rouge">Label</code>, nor the <code class="language-plaintext highlighter-rouge">Button</code> is dependent on, even aware of the other.</li>
  <li>Neither the counter <code class="language-plaintext highlighter-rouge">Label</code>, nor the <code class="language-plaintext highlighter-rouge">Button</code> is aware of, or dependent on the implementation the other.</li>
  <li>Neither the counter <code class="language-plaintext highlighter-rouge">Label</code>, nor the <code class="language-plaintext highlighter-rouge">Button</code> is aware of the implementation of <code class="language-plaintext highlighter-rouge">counter</code>.</li>
  <li>Any other <code class="language-plaintext highlighter-rouge">Node</code> in the layout can interact with <code class="language-plaintext highlighter-rouge">counter</code> without impacting the implementation of either the <code class="language-plaintext highlighter-rouge">Button</code> or the counter <code class="language-plaintext highlighter-rouge">Label</code>.</li>
</ol>

<p>There has to be some understanding of the meaning of <code class="language-plaintext highlighter-rouge">counter</code> within some scope.  That scope might just be within the <code class="language-plaintext highlighter-rouge">waterCounter(){}</code> function, but it might be the entire layout builder class.  It’s hard to imagine any degree of coupling that is less than this which is still functional.</p>

<p>In a very important way, though, we have compartmentalized and isolated that coupling.  In this example, it’s <code class="language-plaintext highlighter-rouge">counter</code>, and we’ve essentially declared it the key <em>the</em> coupling point.  Any <code class="language-plaintext highlighter-rouge">Node</code> that we create that interacts with <code class="language-plaintext highlighter-rouge">counter</code> needs to share that understanding of the meaning of <code class="language-plaintext highlighter-rouge">counter</code>, and behave accordingly.  But it doesn’t have to understand how any other <code class="language-plaintext highlighter-rouge">Nodes</code> in the layout are interacting with <code class="language-plaintext highlighter-rouge">counter</code>.</p>

<h1 id="reactive-vs-imperative-design-in-a-framework">Reactive Vs Imperative Design in a Framework</h1>

<p>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.</p>

<p>Let’s go back to our example…</p>

<h2 id="imperative-design-with-a-framework">Imperative Design With a Framework</h2>

<p>Imagine that whatever change needs to happen to <code class="language-plaintext highlighter-rouge">counter</code> is determined to be “application logic”, so it can no longer reside in the layout.  We’ll need to change the logic for the <code class="language-plaintext highlighter-rouge">Button</code> action.  Let’s look at a first try at the imperative design approach first:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="err">layout</span><span class="nc">Builder</span><span class="p">(</span><span class="k">private</span> <span class="kd">val</span> <span class="py">counterChanger</span> <span class="p">:</span> <span class="p">(</span><span class="nc">Int</span><span class="p">,</span> <span class="p">(</span><span class="nc">Int</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">Unit</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">Unit</span><span class="p">)</span> <span class="p">{</span>
      <span class="p">.</span>
      <span class="p">.</span>
      <span class="p">.</span>
    <span class="k">fun</span> <span class="nf">someOtherPane</span><span class="p">()</span> <span class="p">:</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">().</span><span class="nf">apply</span><span class="p">{</span>
          <span class="p">.</span>
          <span class="p">.</span>
          <span class="p">.</span>
        <span class="kd">val</span> <span class="py">counter</span> <span class="p">:</span> <span class="nc">Int</span> <span class="p">=</span> <span class="mi">0</span>
        <span class="kd">val</span> <span class="py">glugLabel</span> <span class="p">=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Glug...glug...glug..."</span><span class="p">)</span>
        <span class="kd">val</span> <span class="py">button</span> <span class="p">=</span> <span class="nc">Button</span><span class="p">(</span><span class="s">"Add one"</span><span class="p">)</span>
        <span class="n">button</span><span class="p">.</span><span class="nf">setOnAction</span> <span class="p">{</span> <span class="n">evt</span> <span class="p">-&gt;</span>
           <span class="n">counterChanger</span><span class="p">.</span><span class="nf">invoke</span><span class="p">(</span><span class="n">counter</span><span class="p">)</span> <span class="p">{</span> <span class="n">newCounter</span> <span class="p">-&gt;</span>
               <span class="n">counter</span> <span class="p">=</span> <span class="n">newCounter</span>
               <span class="n">countLabel</span><span class="p">.</span><span class="nf">setText</span><span class="p">(</span><span class="s">"You've had ${counter} glasses."</span><span class="p">)</span>
               <span class="k">if</span> <span class="p">(</span><span class="n">counter</span> <span class="p">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
                   <span class="n">countPane</span><span class="p">.</span><span class="nf">getChildren</span><span class="p">().</span><span class="nf">setAll</span><span class="p">(</span><span class="nf">listOf</span><span class="p">(</span><span class="o">..</span><span class="p">.,</span> <span class="n">glugLabel</span><span class="p">,</span> <span class="n">countLabel</span><span class="p">,</span> <span class="o">..</span><span class="p">.))</span>
               <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                   <span class="n">countPane</span><span class="p">.</span><span class="nf">getChildren</span><span class="p">().</span><span class="nf">setAll</span><span class="p">(</span><span class="nf">listOf</span><span class="p">(</span><span class="o">..</span><span class="p">.,</span> <span class="n">countLabel</span><span class="p">,</span> <span class="o">..</span><span class="p">.))</span>
               <span class="p">}</span>
           <span class="p">}</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="n">button</span>
    <span class="p">}</span>  
<span class="p">}</span>
</code></pre></div></div>
<p>Let’s look at that <code class="language-plaintext highlighter-rouge">(Int, (Int) -&gt; Unit) -&gt; Unit</code> first.  This is the equivalent of the Java, <code class="language-plaintext highlighter-rouge">BiConsumer&lt;Integer, Consumer&lt;Integer&gt;&gt;</code>.  It’s a functional element that accepts an Integer and another function that also accepts an Integer.</p>

<p>We don’t know what <code class="language-plaintext highlighter-rouge">counterChanger</code> does.  Maybe it calls a website or some other external API, either of which would block the FXAT.  So <code class="language-plaintext highlighter-rouge">counterChanger</code> might use <code class="language-plaintext highlighter-rouge">Task</code> to run the processing on a background thread.  That means that we cannot just use <code class="language-plaintext highlighter-rouge">(Int) -&gt; Int</code>, or <code class="language-plaintext highlighter-rouge">Function&lt;Integer, Integer&gt;</code> because we can’t wait for the answer to come back.  Instead, we define a <code class="language-plaintext highlighter-rouge">Consumer</code> that will handle the answer when it comes back, and we send that off along with the current value of <code class="language-plaintext highlighter-rouge">counter</code>.</p>

<p>We also cannot assume that <code class="language-plaintext highlighter-rouge">counter</code> is going to increase through <code class="language-plaintext highlighter-rouge">counterChanger</code>.  So we can’t just automatically add <code class="language-plaintext highlighter-rouge">glugLabel</code> when <code class="language-plaintext highlighter-rouge">button</code> is clicked.  So we’ll need some logic to decide that.</p>

<p>You can see, though, that we still have a lot of coupling between <code class="language-plaintext highlighter-rouge">button</code> and the application logic that deals with <code class="language-plaintext highlighter-rouge">counter</code>.  For instance, we know that it requires the current value of <code class="language-plaintext highlighter-rouge">counter</code> and nothing else.  What if there were other factors involved?  How about <code class="language-plaintext highlighter-rouge">Spinner</code> somewhere on the screen with a maximum value for <code class="language-plaintext highlighter-rouge">counter</code>?  How about a <code class="language-plaintext highlighter-rouge">CheckBox</code> or <code class="language-plaintext highlighter-rouge">ToggleButton</code> that determined if the <code class="language-plaintext highlighter-rouge">counter</code> is incremented or decremented.  Or something else that determined by how much?  What if <code class="language-plaintext highlighter-rouge">counterChanger</code> also needs to reset that “how much” value back to 1 each time?  All of this would change how <code class="language-plaintext highlighter-rouge">button</code> was implemented.</p>

<p>Let’s look at the next step towards solving this…</p>

<h3 id="the-presentation-model-in-the-framework">The Presentation Model in the Framework</h3>

<p>Any of the frameworks - MVC, MVVM or MVCI - is going to have the Presentation Model represented somewhere.  In MVC, it’s part of the “Model”, in MVVM it’s in the “ViewModel” and in MVCI it <em>is</em> the “Model” component.  For the sake of simplicity, let’s assume that we’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 <code class="language-plaintext highlighter-rouge">model.getPresentationModel()</code> or <code class="language-plaintext highlighter-rouge">viewModel.getPresentationModel()</code>.</p>

<p>Once we do that, we can assume that all of the components of the framework have easy access to the Presentation Model.</p>

<p>In 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’t need to pass <code class="language-plaintext highlighter-rouge">counter</code> around any more.  So we get this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="err">layout</span><span class="nc">Builder</span><span class="p">(</span><span class="k">private</span> <span class="kd">val</span> <span class="py">presentationModel</span> <span class="p">:</span> <span class="nc">PresentationModel</span><span class="p">,</span>
                    <span class="k">private</span> <span class="kd">val</span> <span class="py">counterChanger</span> <span class="p">:</span> <span class="p">(()</span> <span class="p">-&gt;</span> <span class="nc">Unit</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">Unit</span><span class="p">)</span> <span class="p">{</span>
      <span class="p">.</span>
      <span class="p">.</span>
      <span class="p">.</span>
    <span class="k">fun</span> <span class="nf">someOtherPane</span><span class="p">()</span> <span class="p">:</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">().</span><span class="nf">apply</span><span class="p">{</span>
          <span class="p">.</span>
          <span class="p">.</span>
          <span class="p">.</span>
        <span class="kd">val</span> <span class="py">glugLabel</span> <span class="p">=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Glug...glug...glug..."</span><span class="p">)</span>
        <span class="kd">val</span> <span class="py">button</span> <span class="p">=</span> <span class="nc">Button</span><span class="p">(</span><span class="s">"Add one"</span><span class="p">)</span>
        <span class="n">button</span><span class="p">.</span><span class="nf">setOnAction</span> <span class="p">{</span> <span class="n">evt</span> <span class="p">-&gt;</span>
           <span class="n">counterChanger</span><span class="p">.</span><span class="nf">invoke</span> <span class="p">{</span>
               <span class="n">countLabel</span><span class="p">.</span><span class="nf">setText</span><span class="p">(</span><span class="s">"You've had ${presentationModel.counter} glasses."</span><span class="p">)</span>
               <span class="k">if</span> <span class="p">(</span><span class="n">presentationModel</span><span class="p">.</span><span class="n">counter</span> <span class="p">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
                   <span class="n">countPane</span><span class="p">.</span><span class="nf">getChildren</span><span class="p">().</span><span class="nf">setAll</span><span class="p">(</span><span class="nf">listOf</span><span class="p">(</span><span class="o">..</span><span class="p">.,</span> <span class="n">glugLabel</span><span class="p">,</span> <span class="n">countLabel</span><span class="p">,</span> <span class="o">..</span><span class="p">.))</span>
               <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                   <span class="n">countPane</span><span class="p">.</span><span class="nf">getChildren</span><span class="p">().</span><span class="nf">setAll</span><span class="p">(</span><span class="nf">listOf</span><span class="p">(</span><span class="o">..</span><span class="p">.,</span> <span class="n">countLabel</span><span class="p">,</span> <span class="o">..</span><span class="p">.))</span>
               <span class="p">}</span>
           <span class="p">}</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="n">button</span>
    <span class="p">}</span>  
<span class="p">}</span>
</code></pre></div></div>
<p>Whatever <code class="language-plaintext highlighter-rouge">counterChanger</code> does, it’s impact is to modify <code class="language-plaintext highlighter-rouge">counter</code> inside <code class="language-plaintext highlighter-rouge">presentationModel</code>.  Now, <code class="language-plaintext highlighter-rouge">counterChanger</code> is essentially a <code class="language-plaintext highlighter-rouge">Consumer&lt;Runnable&gt;</code>.</p>

<p>What does <code class="language-plaintext highlighter-rouge">counterChanger</code> do?</p>

<p>We don’t know the specifics, but there is an understanding that it will “change <code class="language-plaintext highlighter-rouge">PresentationModel.counter</code>”.  Since it’s defined as a <code class="language-plaintext highlighter-rouge">Consumer</code> of a <code class="language-plaintext highlighter-rouge">Runnable</code>, it will execute that <code class="language-plaintext highlighter-rouge">Runnable</code> when it has finished updating <code class="language-plaintext highlighter-rouge">PresentationModel.counter</code>.</p>

<p>Let’s look at one of those, “What if?” questions from the end of the last section.  The “How much?” question, and the idea of resetting it after each call to <code class="language-plaintext highlighter-rouge">counterChanger</code>.  We’ll assume we have a <code class="language-plaintext highlighter-rouge">Spinner&lt;Int&gt;</code> somewhere in our layout, and it’s called <code class="language-plaintext highlighter-rouge">howMuchSpinner</code>.   We’ll also add a value into our Presentation Model:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">PresentationModel</span> <span class="p">{</span>
   <span class="kd">var</span> <span class="py">counter</span> <span class="p">:</span> <span class="nc">Int</span> <span class="p">=</span> <span class="mi">0</span>
   <span class="kd">var</span> <span class="py">howMuch</span> <span class="p">:</span> <span class="nc">Int</span> <span class="p">=</span> <span class="mi">1</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now, this is what our layout code looks like:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="err">layout</span><span class="nc">Builder</span><span class="p">(</span><span class="k">private</span> <span class="kd">val</span> <span class="py">presentationModel</span> <span class="p">:</span> <span class="nc">PresentationModel</span><span class="p">,</span>
                    <span class="k">private</span> <span class="kd">val</span> <span class="py">counterChanger</span> <span class="p">:</span> <span class="p">(()</span> <span class="p">-&gt;</span> <span class="nc">Unit</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">Unit</span><span class="p">)</span> <span class="p">{</span>
      <span class="p">.</span>
      <span class="p">.</span>
      <span class="p">.</span>
    <span class="k">fun</span> <span class="nf">someOtherPane</span><span class="p">()</span> <span class="p">:</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">().</span><span class="nf">apply</span><span class="p">{</span>
          <span class="p">.</span>
          <span class="p">.</span>
          <span class="p">.</span>
        <span class="kd">val</span> <span class="py">glugLabel</span> <span class="p">=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Glug...glug...glug..."</span><span class="p">)</span>
        <span class="kd">val</span> <span class="py">button</span> <span class="p">=</span> <span class="nc">Button</span><span class="p">(</span><span class="s">"Add one"</span><span class="p">)</span>
        <span class="n">button</span><span class="p">.</span><span class="nf">setOnAction</span> <span class="p">{</span> <span class="n">evt</span> <span class="p">-&gt;</span>
           <span class="n">presentationModel</span><span class="p">.</span><span class="n">howMuch</span> <span class="p">=</span> <span class="n">howMuchSpinner</span><span class="p">.</span><span class="nf">getValue</span><span class="p">()</span>
           <span class="n">counterChanger</span><span class="p">.</span><span class="nf">invoke</span> <span class="p">{</span>
               <span class="n">howMuchSpinner</span><span class="p">.</span><span class="nf">setValue</span><span class="p">(</span><span class="n">presentationModel</span><span class="p">.</span><span class="n">howMuch</span><span class="p">)</span>
               <span class="n">countLabel</span><span class="p">.</span><span class="nf">setText</span><span class="p">(</span><span class="s">"You've had ${presentationModel.counter} glasses."</span><span class="p">)</span>
               <span class="k">if</span> <span class="p">(</span><span class="n">presentationModel</span><span class="p">.</span><span class="n">counter</span> <span class="p">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
                   <span class="n">countPane</span><span class="p">.</span><span class="nf">getChildren</span><span class="p">().</span><span class="nf">setAll</span><span class="p">(</span><span class="nf">listOf</span><span class="p">(</span><span class="o">..</span><span class="p">.,</span> <span class="n">glugLabel</span><span class="p">,</span> <span class="n">countLabel</span><span class="p">,</span> <span class="o">..</span><span class="p">.))</span>
               <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                   <span class="n">countPane</span><span class="p">.</span><span class="nf">getChildren</span><span class="p">().</span><span class="nf">setAll</span><span class="p">(</span><span class="nf">listOf</span><span class="p">(</span><span class="o">..</span><span class="p">.,</span> <span class="n">countLabel</span><span class="p">,</span> <span class="o">..</span><span class="p">.))</span>
               <span class="p">}</span>
           <span class="p">}</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="n">button</span>
    <span class="p">}</span>  
<span class="p">}</span>
</code></pre></div></div>
<p>Our <code class="language-plaintext highlighter-rouge">button</code> still doesn’t know exactly <em>what</em> <code class="language-plaintext highlighter-rouge">counterChanger</code> does.  But it does need to know that its implementation requires <code class="language-plaintext highlighter-rouge">howMuch</code> and that it might change <code class="language-plaintext highlighter-rouge">howMuch</code>.  It also needs to know that there is a <code class="language-plaintext highlighter-rouge">Spinner</code> called <code class="language-plaintext highlighter-rouge">howMuchSpinner</code> and that it needs to extract its value to update <code class="language-plaintext highlighter-rouge">presentationModel</code> before it invokes <code class="language-plaintext highlighter-rouge">counterChanger</code>.</p>

<p>Unfortunately, this is the end of the road for the Imperative approach.  As long as the “State” of the GUI is stored inside the <code class="language-plaintext highlighter-rouge">Nodes</code> that make up the layout, there is no way to avoid building the logic to pick the pieces that you need out of those <code class="language-plaintext highlighter-rouge">Nodes</code> and updating the Presentation Model.  And that, all by itself, demands that the layout logic is coupled to the implementation of the application logic.</p>

<p>But, you ask…What if you just always copy all of the <code class="language-plaintext highlighter-rouge">Node</code> data into the Presentation Model before any business logic call, and then copy it all back into the <code class="language-plaintext highlighter-rouge">Nodes</code> afterwards???</p>

<p>Well, if you’re going to do that, you might as well go with a Reactive design.  Let’s look at how that would work:</p>

<h2 id="reactive-design-framework">Reactive Design Framework</h2>

<p>We’ll start out by looking at the Presentation Model:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">PresentationModel</span> <span class="p">{</span>
   <span class="kd">val</span> <span class="py">counter</span> <span class="p">:</span> <span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
   <span class="kd">val</span> <span class="py">howMuch</span> <span class="p">:</span> <span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Not that exciting, we’ve just changed the two fields over to <code class="language-plaintext highlighter-rouge">IntegerProperty</code>.  Here’s our layout code:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="err">layout</span><span class="nc">Builder</span><span class="p">(</span><span class="kd">val</span> <span class="py">presentationModel</span> <span class="p">:</span> <span class="nc">PresentationModel</span><span class="p">,</span>
                    <span class="kd">val</span> <span class="py">counterUpdater</span> <span class="p">:</span> <span class="p">()</span> <span class="p">-&gt;</span> <span class="nc">Unit</span><span class="p">)</span> <span class="p">{</span>
      <span class="p">.</span>
      <span class="p">.</span>
      <span class="p">.</span>
    <span class="k">fun</span> <span class="nf">waterCounter</span><span class="p">()</span> <span class="p">:</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Glug...glug...glug..."</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
           <span class="nf">visibleProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">presentationModel</span><span class="p">.</span><span class="n">counter</span><span class="p">.</span><span class="nf">greaterThan</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span>
           <span class="nf">managedProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="nf">visibleProperty</span><span class="p">())</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
           <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">presentationModel</span><span class="p">.</span><span class="n">counter</span><span class="p">.</span><span class="nf">map</span><span class="p">{</span> <span class="s">"You've had $it glasses."</span> <span class="p">})</span>
        <span class="p">}</span>
    <span class="p">}</span>
         <span class="p">.</span>
         <span class="p">.</span>
         <span class="p">.</span>
    <span class="k">fun</span> <span class="nf">someOtherPane</span><span class="p">()</span> <span class="p">{</span>
             <span class="p">.</span>
             <span class="p">.</span>
             <span class="p">.</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">Button</span><span class="p">(</span><span class="s">"Add one"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nf">setOnClick</span> <span class="p">{</span> <span class="n">evt</span> <span class="p">-&gt;</span> <span class="n">counterUpdater</span><span class="p">.</span><span class="nf">invoke</span><span class="p">()</span> <span class="p">}</span>
        <span class="p">}</span>         
             <span class="p">.</span>
             <span class="p">.</span>
             <span class="p">.</span>
    <span class="p">}</span>     
<span class="p">}</span>
</code></pre></div></div>
<p>Hmmmm…that doesn’t look different from the last Reactive version.  We now have <code class="language-plaintext highlighter-rouge">presentationModel</code> passed in as constructor parameter and <code class="language-plaintext highlighter-rouge">counter</code> has been removed as a stand-alone field.  References to <code class="language-plaintext highlighter-rouge">counter</code> have been changed to <code class="language-plaintext highlighter-rouge">presentationModel.counter</code>.</p>

<p>The only other change is that the <code class="language-plaintext highlighter-rouge">Button</code> event now calls the <code class="language-plaintext highlighter-rouge">counterUpdater</code> which, in this version, is the Kotlin equivalent to <code class="language-plaintext highlighter-rouge">Runnable</code>.  We don’t need to pass anything to it, because everything is already synchronized with <code class="language-plaintext highlighter-rouge">presentationModel</code>, which the application logic has access to.</p>

<p>Note that the <code class="language-plaintext highlighter-rouge">Button</code> logic does <strong>nothing</strong> except invoke <code class="language-plaintext highlighter-rouge">counterUpdater</code>.  It has no need to know about any other <code class="language-plaintext highlighter-rouge">Nodes</code> in the layout, nor does it have any need to know <strong>anything</strong> about the implementation of <code class="language-plaintext highlighter-rouge">counterUpdater</code>.</p>

<p>You’ll also note that I haven’t put in any code at all that deals with <code class="language-plaintext highlighter-rouge">PresentationModel.howMuch</code>.  Presumably, there’s some kind of <code class="language-plaintext highlighter-rouge">Node</code>, or <code class="language-plaintext highlighter-rouge">Nodes</code> that synchronize with <code class="language-plaintext highlighter-rouge">PresentationModel.howMuch</code>, but they are not relevant to this discussion at all.</p>

<p>None of anything that we are discussing here is coupled to the implementation of <code class="language-plaintext highlighter-rouge">PresentationModel.howMuch</code> at all.</p>

<h3 id="this-approach-breaks-mvc">This Approach “Breaks” MVC</h3>

<p>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 <strong>must</strong> go through the Controller.</p>

<p>Right 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 <code class="language-plaintext highlighter-rouge">Nodes</code>, nor can you bind unidirectionally from a <code class="language-plaintext highlighter-rouge">Node</code> to the Presentation Model.</p>

<p>Should you care?</p>

<p>Probably not.  But, if you do ignore this rule, and if you implement your Presentation Model as an enclosing object around your State <code class="language-plaintext highlighter-rouge">Properties</code> (even if it is still contained within the Model), then you’re 90% of the way from MVC to MVCI.  And you might as well use MVCI - it’s better for this stuff anyways.</p>

<h2 id="application-logic-in-a-reactive-design">Application Logic in a Reactive Design</h2>

<p>Let’s look at the other end of the framework, the application logic…</p>

<p>In 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’t have open access to its elements.  But that’s a different issue, and we can’t go into it here.  So we’ll concentrate on MVCI, with the understanding that much of it applies to MVC as well.</p>

<p>Typically, 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’s take a look at how we might implement our <code class="language-plaintext highlighter-rouge">counterUpdater</code> logic:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Interactor</span><span class="p">(</span><span class="k">private</span> <span class="kd">val</span> <span class="py">model</span> <span class="p">:</span> <span class="nc">PresentationModel</span><span class="p">)</span>  <span class="p">{</span>

    <span class="k">fun</span> <span class="nf">updateCounter</span><span class="p">()</span> <span class="p">:</span> <span class="nc">Int</span> <span class="p">{</span>
       <span class="k">return</span> <span class="nf">someService</span><span class="p">(</span><span class="n">model</span><span class="p">.</span><span class="n">counter</span><span class="p">.</span><span class="n">value</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">howMuch</span><span class="p">.</span><span class="n">value</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">fun</span> <span class="nf">newCounterValue</span><span class="p">(</span><span class="n">newVal</span> <span class="p">:</span> <span class="nc">Int</span><span class="p">)</span> <span class="p">{</span>
       <span class="n">model</span><span class="p">.</span><span class="n">counter</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">newVal</span>
       <span class="n">model</span><span class="p">.</span><span class="n">howMuch</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="mi">1</span>
    <span class="p">}</span>

<span class="p">}</span>
</code></pre></div></div>
<p>For context, this would be called from something like this, in the Controller:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fun</span> <span class="nf">updateCounter</span><span class="p">()</span> <span class="p">{</span>
   <span class="kd">val</span> <span class="py">task</span> <span class="p">:</span> <span class="nc">Task</span><span class="p">&lt;</span><span class="nc">Int</span><span class="p">&gt;</span> <span class="p">=</span> <span class="kd">object</span> <span class="err">: </span><span class="nc">Task</span><span class="p">()</span> <span class="p">{</span>
      <span class="k">override</span> <span class="k">fun</span> <span class="nf">call</span><span class="p">()</span> <span class="p">:</span> <span class="nc">Int</span> <span class="p">{</span>
         <span class="k">return</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">updateCounter</span><span class="p">()</span>
      <span class="p">}</span>
   <span class="p">}</span>
   <span class="n">task</span><span class="p">.</span><span class="nf">setOnSucceeded</span> <span class="p">{</span><span class="n">evt</span> <span class="p">-&gt;</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">newCounterValue</span><span class="p">(</span><span class="n">task</span><span class="p">.</span><span class="k">get</span><span class="p">())}</span>
   <span class="nc">Thread</span><span class="p">(</span><span class="n">task</span><span class="p">).</span><span class="nf">start</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>If you are familiar with <code class="language-plaintext highlighter-rouge">Task</code> then you’ll see that <code class="language-plaintext highlighter-rouge">Interactor.updateCounter()</code> is called on the background thread, and <code class="language-plaintext highlighter-rouge">Interactor.newCounterValue()</code> is called from FXAT.  This latter method, therefore, is allowed to update <code class="language-plaintext highlighter-rouge">Properties</code> bound to the Scene Graph.  You should also notice, that the Interactor doesn’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.</p>

<h3 id="exposing-the-properties-to-the-interactor">Exposing the Properties to the Interactor</h3>

<p>You might have noticed that in our Interactor we have lines like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">model</span><span class="p">.</span><span class="n">counter</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">newVal</span>
</code></pre></div></div>
<p>Clearly, to do this, the Interactor has to be aware of the <code class="language-plaintext highlighter-rouge">Property</code> nature of <code class="language-plaintext highlighter-rouge">model.counter</code> or, in other words, the Interactor is dependent on the implementation of <code class="language-plaintext highlighter-rouge">model.counter</code> as a <code class="language-plaintext highlighter-rouge">Property</code>.  Can we limit this?</p>

<p>You can.  You can create an <code class="language-plaintext highlighter-rouge">Interface</code> that essentially has the getters and setters for the values of the <code class="language-plaintext highlighter-rouge">Properties</code> and then pass the Presentation Model to the Interactor as that <code class="language-plaintext highlighter-rouge">Interface</code>.  Like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">interface</span> <span class="nc">InteractorModel</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="py">counter</span><span class="p">:</span> <span class="nc">Int</span>
    <span class="kd">var</span> <span class="py">howMuch</span><span class="p">:</span> <span class="nc">Int</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="nc">PresentationModel</span> <span class="p">:</span> <span class="nc">InteractorModel</span> <span class="p">{</span>

    <span class="kd">val</span> <span class="py">counterProperty</span><span class="p">:</span> <span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">howMuchProperty</span><span class="p">:</span> <span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

    <span class="k">override</span> <span class="kd">var</span> <span class="py">counter</span><span class="p">:</span> <span class="nc">Int</span>
        <span class="k">get</span><span class="p">()</span> <span class="p">=</span> <span class="n">counterProperty</span><span class="p">.</span><span class="k">get</span><span class="p">()</span>
        <span class="k">set</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="p">=</span> <span class="n">counterProperty</span><span class="p">.</span><span class="k">set</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>

    <span class="k">override</span> <span class="kd">var</span> <span class="py">howMuch</span><span class="p">:</span> <span class="nc">Int</span>
        <span class="k">get</span><span class="p">()</span> <span class="p">=</span> <span class="n">howMuchProperty</span><span class="p">.</span><span class="k">get</span><span class="p">()</span>
        <span class="k">set</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="p">=</span> <span class="n">howMuchProperty</span><span class="p">.</span><span class="k">set</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This looks a bit different in Kotlin.  We’ve defined <code class="language-plaintext highlighter-rouge">Int</code> fields in the <code class="language-plaintext highlighter-rouge">Interface</code>, which is allowed in Kotlin.  Then we’ve implemented these fields in <code class="language-plaintext highlighter-rouge">PresentationModel</code> such that they delegate their <code class="language-plaintext highlighter-rouge">set()</code> and <code class="language-plaintext highlighter-rouge">get()</code> methods to the <code class="language-plaintext highlighter-rouge">get()</code> and <code class="language-plaintext highlighter-rouge">set()</code> methods of the respective <code class="language-plaintext highlighter-rouge">IntegerProperties</code>.  Now, we can do this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Interactor</span><span class="p">(</span><span class="k">private</span> <span class="kd">val</span> <span class="py">model</span><span class="p">:</span> <span class="nc">InteractorModel</span><span class="p">)</span> <span class="p">{</span>

    <span class="k">fun</span> <span class="nf">updateCounter</span><span class="p">()</span> <span class="p">:</span> <span class="nc">Int</span> <span class="p">{</span>
        <span class="k">return</span> <span class="nf">someService</span><span class="p">(</span><span class="n">model</span><span class="p">.</span><span class="n">counter</span><span class="p">,</span> <span class="n">model</span><span class="p">.</span><span class="n">howMuch</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">fun</span> <span class="nf">newCounterValue</span><span class="p">(</span><span class="n">newVal</span> <span class="p">:</span> <span class="nc">Int</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">model</span><span class="p">.</span><span class="n">counter</span> <span class="p">=</span> <span class="n">newVal</span>
        <span class="n">model</span><span class="p">.</span><span class="n">howMuch</span><span class="p">=</span> <span class="mi">1</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>And the Interactor can only interact with <code class="language-plaintext highlighter-rouge">model.counter</code> and <code class="language-plaintext highlighter-rouge">model.howMuch</code> as <code class="language-plaintext highlighter-rouge">Int</code>.  The layout code, would now use bindings to <code class="language-plaintext highlighter-rouge">model.counterProperty</code> and <code class="language-plaintext highlighter-rouge">model.howMuchProperty</code>.</p>

<p>But what if you want to prevent the layout from changing <code class="language-plaintext highlighter-rouge">model.counter</code>?  That actually seems like a reasonable requirement.  We could expose <code class="language-plaintext highlighter-rouge">model.counterProperty</code> as <code class="language-plaintext highlighter-rouge">ObservableIntegerValue</code>, but the layout is still going to be able to update <code class="language-plaintext highlighter-rouge">model.counter</code>.  How do we prevent that?</p>

<p>We just do the same thing as we did for the Interactor, except instead of exposing the getters and setters, expose only the <code class="language-plaintext highlighter-rouge">Properties</code>:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">interface</span> <span class="nc">LayoutModel</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">counterProperty</span><span class="p">:</span> <span class="nc">ReadOnlyIntegerProperty</span>
    <span class="kd">val</span> <span class="py">howMuchProperty</span><span class="p">:</span> <span class="nc">IntegerProperty</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="nc">PresentationModel</span> <span class="p">:</span> <span class="nc">InteractorModel</span><span class="p">,</span> <span class="nc">LayoutModel</span> <span class="p">{</span>

    <span class="k">private</span> <span class="kd">val</span> <span class="py">counterPropertyImpl</span><span class="p">:</span> <span class="nc">ReadOnlyIntegerWrapper</span> <span class="p">=</span> <span class="nc">ReadOnlyIntegerWrapper</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
    <span class="k">override</span> <span class="kd">val</span> <span class="py">counterProperty</span><span class="p">:</span> <span class="nc">ReadOnlyIntegerProperty</span> <span class="p">=</span> <span class="n">counterPropertyImpl</span><span class="p">.</span><span class="n">readOnlyProperty</span>
    <span class="k">override</span> <span class="kd">val</span> <span class="py">howMuchProperty</span><span class="p">:</span> <span class="nc">IntegerProperty</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

    <span class="k">override</span> <span class="kd">var</span> <span class="py">counter</span><span class="p">:</span> <span class="nc">Int</span>
        <span class="k">get</span><span class="p">()</span> <span class="p">=</span> <span class="n">counterPropertyImpl</span><span class="p">.</span><span class="k">get</span><span class="p">()</span>
        <span class="k">set</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="p">=</span> <span class="n">counterPropertyImpl</span><span class="p">.</span><span class="k">set</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>

    <span class="k">override</span> <span class="kd">var</span> <span class="py">howMuch</span><span class="p">:</span> <span class="nc">Int</span>
        <span class="k">get</span><span class="p">()</span> <span class="p">=</span> <span class="n">howMuchProperty</span><span class="p">.</span><span class="k">get</span><span class="p">()</span>
        <span class="k">set</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="p">=</span> <span class="n">howMuchProperty</span><span class="p">.</span><span class="k">set</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>I’ve called the new <code class="language-plaintext highlighter-rouge">Interface LayoutModel</code> because <code class="language-plaintext highlighter-rouge">ViewModel</code> would get confusing.  This <code class="language-plaintext highlighter-rouge">Interface</code> exposes <code class="language-plaintext highlighter-rouge">howMuchProperty</code> as <code class="language-plaintext highlighter-rouge">IntegerProperty</code> and <code class="language-plaintext highlighter-rouge">counterProperty</code> as <code class="language-plaintext highlighter-rouge">ReadOnlyIntegerProperty</code>.  In the <code class="language-plaintext highlighter-rouge">PresentationModel</code> we now have a private <code class="language-plaintext highlighter-rouge">IntegerProperty</code> instantiated as <code class="language-plaintext highlighter-rouge">ReadOnlyIntegerWrapper</code>.  Then we implement the <code class="language-plaintext highlighter-rouge">Interface</code> by instantiating <code class="language-plaintext highlighter-rouge">counterProperty</code> with <code class="language-plaintext highlighter-rouge">counterPropertyImpl.getReadOnlyProperty()</code>.</p>

<p>Obviously, we pass <code class="language-plaintext highlighter-rouge">PresentationModel</code> to the layout builder class as <code class="language-plaintext highlighter-rouge">LayoutModel</code> and the layout builder only has acces to the fields as <code class="language-plaintext highlighter-rouge">Properties</code>, and <code class="language-plaintext highlighter-rouge">counter</code> is read-only.</p>

<p>The 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 <code class="language-plaintext highlighter-rouge">Observable</code> objects that it can connect to the layout through <code class="language-plaintext highlighter-rouge">Bindings</code> and <code class="language-plaintext highlighter-rouge">Listeners</code>.  In the end, the application logic performs, <code class="language-plaintext highlighter-rouge">model.counter = newVal</code> and the <code class="language-plaintext highlighter-rouge">Label</code> on the screen instantly changes.</p>

<h1 id="conclusion">Conclusion</h1>

<p>I think that JavaFX gets missed as a Reactive GUI environment because it’s different from the other, commonly used systems like React.</p>

<p>I spent a bit of time looking through the results of a web search for “JavaFX reactive”, and it wasn’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 <code class="language-plaintext highlighter-rouge">Observable</code> classes and not much else.</p>

<p>It’s almost disappointing that you <em>can</em> build a working application using a declarative/imperative approach to GUI design with JavaFX, because that’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’t think I’ve ever seen an example of an FXML based Reactive JavaFX application.  They’re probably out there, but nobody seems to be talking about it.</p>

<p>It’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 “wrong” 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.</p>

<p>When 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 <code class="language-plaintext highlighter-rouge">Observables</code> 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, <strong>without knowing about each other.</strong>  That allows them to interact without becoming coupled.</p>]]></content><author><name>Dave Barrett</name><email>pragmaticcoding8@gmail.com</email></author><category term="javafx" /><summary type="html"><![CDATA[What is Reactive programming, and how does it work in JavaFX]]></summary></entry><entry><title type="html">Task Partial Results: Lists</title><link href="https://www.pragmaticcoding.ca/javafx/elements/task-list-progress" rel="alternate" type="text/html" title="Task Partial Results: Lists" /><published>2025-07-02T17:00:00+00:00</published><updated>2025-07-02T17:00:00+00:00</updated><id>https://www.pragmaticcoding.ca/javafx/elements/task-list-progress</id><content type="html" xml:base="https://www.pragmaticcoding.ca/javafx/elements/task-list-progress"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>In an earlier <a href="/javafx/elements/task-progress">article</a> I looked at <code class="language-plaintext highlighter-rouge">Task</code> and how to track progress and partial results as a <code class="language-plaintext highlighter-rouge">Task</code> is running.  This can be a challenge, as any <code class="language-plaintext highlighter-rouge">Observable</code> value that can be linked to your layout can <strong>only</strong> be updated from the FXAT, while the action of the <code class="language-plaintext highlighter-rouge">Task</code> is designed to be run on a background thread.  The usual answer to this is to wrap any calls to update those <code class="language-plaintext highlighter-rouge">Observable</code> objects in a <code class="language-plaintext highlighter-rouge">Platform.runLater()</code> call.</p>

<p>In that earlier article, we looked at how the <code class="language-plaintext highlighter-rouge">Task.updateValue()</code> and <code class="language-plaintext highlighter-rouge">Task.updateMessage()</code> methods are designed to limit the number of <code class="language-plaintext highlighter-rouge">Platform.runlater()</code> calls that are executed, in order to avoid flooding the FXAT with jobs that would affect the performance of the GUI.</p>

<p>We also looked at creating a custom <code class="language-plaintext highlighter-rouge">Property</code> in <code class="language-plaintext highlighter-rouge">Task</code> that could be updated in the same manner as <code class="language-plaintext highlighter-rouge">Task.value</code> and <code class="language-plaintext highlighter-rouge">Task.message</code> and then bound to elements in your layout.  Those methods use an internal <code class="language-plaintext highlighter-rouge">AtomicReference</code> field, that can safely be updated by multiple threads, to hold an temporary value that can later be transferred to the <code class="language-plaintext highlighter-rouge">Property</code> through <code class="language-plaintext highlighter-rouge">Platform.runLater()</code>.</p>

<p>But <code class="language-plaintext highlighter-rouge">AtomicReference</code> relies on an “atomic” <code class="language-plaintext highlighter-rouge">getAndSet()</code> method to retrieve and update the value that it holds in a single, thread-safe, operation.  But what if you don’t want to replace the value, but add to it?</p>

<p>This is the situaton that you get when your monitored results are in the form of an <code class="language-plaintext highlighter-rouge">ObservableList</code>.</p>

<h2 id="what-the-javadocs-say">What the JavaDocs Say:</h2>
<p>Let’s have a look at the informational section at the top of the JavaDocs for <code class="language-plaintext highlighter-rouge">Task</code>:</p>

<blockquote>
  <p>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.</p>

  <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>     <span class="kd">public</span> <span class="kd">class</span> <span class="nc">PartialResultsTask</span> <span class="kd">extends</span> <span class="nc">Task</span><span class="o">&lt;</span><span class="nc">ObservableList</span><span class="o">&lt;</span><span class="nc">Rectangle</span><span class="o">&gt;&gt;</span> <span class="o">{</span>
         <span class="c1">// Uses Java 7 diamond operator</span>
         <span class="kd">private</span> <span class="nc">ReadOnlyObjectWrapper</span><span class="o">&lt;</span><span class="nc">ObservableList</span><span class="o">&lt;</span><span class="nc">Rectangle</span><span class="o">&gt;&gt;</span> <span class="n">partialResults</span> <span class="o">=</span>
                 <span class="k">new</span> <span class="nc">ReadOnlyObjectWrapper</span><span class="o">&lt;&gt;(</span><span class="k">this</span><span class="o">,</span> <span class="s">"partialResults"</span><span class="o">,</span>
                         <span class="nc">FXCollections</span><span class="o">.</span><span class="na">observableArrayList</span><span class="o">(</span><span class="k">new</span> <span class="nc">ArrayList</span><span class="o">&lt;</span><span class="nc">Rectangle</span><span class="o">&gt;()));</span>
   <span class="c1">//</span>
         <span class="kd">public</span> <span class="kd">final</span> <span class="nc">ObservableList</span><span class="o">&lt;</span><span class="nc">Rectangle</span><span class="o">&gt;</span> <span class="nf">getPartialResults</span><span class="o">()</span> <span class="o">{</span> <span class="k">return</span> <span class="n">partialResults</span><span class="o">.</span><span class="na">get</span><span class="o">();</span> <span class="o">}</span>
         <span class="kd">public</span> <span class="kd">final</span> <span class="nc">ReadOnlyObjectProperty</span><span class="o">&lt;</span><span class="nc">ObservableList</span><span class="o">&lt;</span><span class="nc">Rectangle</span><span class="o">&gt;&gt;</span> <span class="nf">partialResultsProperty</span><span class="o">()</span> <span class="o">{</span>
             <span class="k">return</span> <span class="n">partialResults</span><span class="o">.</span><span class="na">getReadOnlyProperty</span><span class="o">();</span>
         <span class="o">}</span>
    <span class="c1">//              </span>
         <span class="nd">@Override</span> <span class="kd">protected</span> <span class="nc">ObservableList</span><span class="o">&lt;</span><span class="nc">Rectangle</span><span class="o">&gt;</span> <span class="nf">call</span><span class="o">()</span> <span class="kd">throws</span> <span class="nc">Exception</span> <span class="o">{</span>
             <span class="n">updateMessage</span><span class="o">(</span><span class="s">"Creating Rectangles..."</span><span class="o">);</span>
             <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="o">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="mi">100</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
                 <span class="k">if</span> <span class="o">(</span><span class="n">isCancelled</span><span class="o">())</span> <span class="k">break</span><span class="o">;</span>
                 <span class="kd">final</span> <span class="nc">Rectangle</span> <span class="n">r</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Rectangle</span><span class="o">(</span><span class="mi">10</span><span class="o">,</span> <span class="mi">10</span><span class="o">);</span>
                 <span class="n">r</span><span class="o">.</span><span class="na">setX</span><span class="o">(</span><span class="mi">10</span> <span class="o">*</span> <span class="n">i</span><span class="o">);</span>
                 <span class="nc">Platform</span><span class="o">.</span><span class="na">runLater</span><span class="o">(</span><span class="k">new</span> <span class="nc">Runnable</span><span class="o">()</span> <span class="o">{</span>
                     <span class="nd">@Override</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="o">{</span>
                         <span class="n">partialResults</span><span class="o">.</span><span class="na">get</span><span class="o">().</span><span class="na">add</span><span class="o">(</span><span class="n">r</span><span class="o">);</span>
                     <span class="o">}</span>
                 <span class="o">});</span>
                 <span class="n">updateProgress</span><span class="o">(</span><span class="n">i</span><span class="o">,</span> <span class="mi">100</span><span class="o">);</span>
             <span class="o">}</span>
             <span class="k">return</span> <span class="n">partialResults</span><span class="o">.</span><span class="na">get</span><span class="o">();</span>
         <span class="o">}</span>
     <span class="o">}</span>
</code></pre></div>  </div>
</blockquote>

<p>They give us “one approach…”, but no more.  This approach can work if the the updates to the list are relatively infrequent…</p>

<div class="notice_question--primary">
 <img src="/assets/logos/BRAIN_Question.png" alt="primary" style="float:left;margin-right: 10px" />
 <p style="overflow:auto; float:none">
   How would you handle a situation where there are thousands of updates to the `ObservableList` from multiple threads that are coming milliseconds apart?
 </p>
</div>

<p>There’s no help from the JavaDocs for this situation.</p>

<h1 id="a-quick-review-of-the-taskupdatemessage">A Quick Review of the Task.updateMessage()</h1>

<p>You really should go back and read that earlier <a href="/javafx/elements/task-progress">article</a>, but we’ll have a quick look at the process for updating a single value.</p>

<p><code class="language-plaintext highlighter-rouge">Task</code> has a number of <code class="language-plaintext highlighter-rouge">Properties</code> that are intended to be updated and changed from background threads, and it is instructive to see how <code class="language-plaintext highlighter-rouge">Task</code> internally handles these updates.</p>

<p><code class="language-plaintext highlighter-rouge">Task</code> has a field called <code class="language-plaintext highlighter-rouge">message</code> and the infrasture to update it from background threads.  The source code for <code class="language-plaintext highlighter-rouge">Task.updateMessage()</code> looks like this:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="nc">AtomicReference</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">messageUpdate</span><span class="o">;</span>
   <span class="o">.</span>
   <span class="o">.</span>
   <span class="o">.</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">updateMessage</span><span class="o">(</span><span class="nc">String</span> <span class="n">var1</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">isFxApplicationThread</span><span class="o">())</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">message</span><span class="o">.</span><span class="na">set</span><span class="o">(</span><span class="n">var1</span><span class="o">);</span>
    <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">messageUpdate</span><span class="o">.</span><span class="na">getAndSet</span><span class="o">(</span><span class="n">var1</span><span class="o">)</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">runLater</span><span class="o">(</span><span class="k">new</span> <span class="nc">Runnable</span><span class="o">()</span> <span class="o">{</span>
            <span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="o">{</span>
                <span class="nc">String</span> <span class="n">var1</span> <span class="o">=</span> <span class="o">(</span><span class="nc">String</span><span class="o">)</span><span class="nc">Task</span><span class="o">.</span><span class="na">this</span><span class="o">.</span><span class="na">messageUpdate</span><span class="o">.</span><span class="na">getAndSet</span><span class="o">((</span><span class="nc">Object</span><span class="o">)</span><span class="kc">null</span><span class="o">);</span>
                <span class="nc">Task</span><span class="o">.</span><span class="na">this</span><span class="o">.</span><span class="na">message</span><span class="o">.</span><span class="na">set</span><span class="o">(</span><span class="n">var1</span><span class="o">);</span>
            <span class="o">}</span>
        <span class="o">});</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>We have an internal, <code class="language-plaintext highlighter-rouge">private</code>, variable called <code class="language-plaintext highlighter-rouge">messageUpdate</code> which is an <code class="language-plaintext highlighter-rouge">AtomicReference</code>, and that will hold a transient value for updates to the <code class="language-plaintext highlighter-rouge">message Property</code>.  Because it is an <code class="language-plaintext highlighter-rouge">AtomicReference</code>, it is thread-safe, and can be updated freely without concurrency issues.</p>

<p>The <code class="language-plaintext highlighter-rouge">updateMessage()</code> method, if run from the FXAT, will simply update <code class="language-plaintext highlighter-rouge">Task.message</code> directly.</p>

<p>When called from a background thread, however, it does something more complicated.  First, it uses <code class="language-plaintext highlighter-rouge">AtomiceReference.getAndSet()</code> to update <code class="language-plaintext highlighter-rouge">messageUpdate</code> and to check it’s previous value.  If that previous value was <code class="language-plaintext highlighter-rouge">null</code>, then it knows that there wasn’t any previous transient value waiting to be transferred to <code class="language-plaintext highlighter-rouge">Task.message</code> via <code class="language-plaintext highlighter-rouge">Platform.runLater()</code> and it needs to initiate that process.  So it creates a <code class="language-plaintext highlighter-rouge">Runnable</code> and schedules it on the FXAT via <code class="language-plaintext highlighter-rouge">Platform.runLater()</code>.</p>

<p>That <code class="language-plaintext highlighter-rouge">Runnable</code> does two things:</p>

<ol>
  <li>It transfers the current value of <code class="language-plaintext highlighter-rouge">Task.messageUpdate</code> to <code class="language-plaintext highlighter-rouge">Task.message</code>.</li>
  <li>It sets the value of <code class="language-plaintext highlighter-rouge">Task.messageUpdate</code> to <code class="language-plaintext highlighter-rouge">null</code>.</li>
</ol>

<p>If <code class="language-plaintext highlighter-rouge">Task.updateMessage()</code> is called from a background thread any time between <code class="language-plaintext highlighter-rouge">Platform.runLater()</code> being called and the <code class="language-plaintext highlighter-rouge">Runnable</code> being executed on the FXAT, then the previous value of <code class="language-plaintext highlighter-rouge">messageUpdate</code> will <strong>not</strong> be <code class="language-plaintext highlighter-rouge">null</code>, and it will not, therefore, need to launch a new <code class="language-plaintext highlighter-rouge">Runnable</code> on the FXAT.  In fact, the only thing that will happen will be that <code class="language-plaintext highlighter-rouge">Task.messageUpdate</code> has its value changed.  When the <code class="language-plaintext highlighter-rouge">Runnable</code> does execute on the FXAT, it will update <code class="language-plaintext highlighter-rouge">Task.message</code> with only that latest value that is in <code class="language-plaintext highlighter-rouge">Task.messageUpdate</code>.</p>

<p>In this way, <code class="language-plaintext highlighter-rouge">Task.updateMessage()</code> allows gazillions of updates from the background threads, but doesn’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 <code class="language-plaintext highlighter-rouge">Task.message</code> would probably never have made it to the screen if <code class="language-plaintext highlighter-rouge">Platform.runlater()</code> had been invoked for every one of them in any case.  They certainly wouldn’t have been seen by the user.</p>

<h2 id="about-atomicreference">About AtomicReference</h2>

<p><code class="language-plaintext highlighter-rouge">AtomicReference</code> is thread-safe because its “read” and “set” operations are conducted in a single, “atomic” operation.  This means that there’s no possibility that the value could have been changed by some other thread accessing the same object in between the “read” stage and the “set” stage.</p>

<p>But this comes with a cost.</p>

<p>There is no way to <em>modify</em> a value in <code class="language-plaintext highlighter-rouge">AtomicReference</code>, we can only <em>replace</em> it.  Yes, there is a way to use the current value in an <code class="language-plaintext highlighter-rouge">AtomicReference</code> to calculate a new value for it, <code class="language-plaintext highlighter-rouge">AtomicReference.getAndUpdate()</code> but this will also <em>replace</em> the value.  So, we can do something like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">atomicReference</span> <span class="p">=</span> <span class="nc">AtomicReference</span><span class="p">&lt;</span><span class="nc">Int</span><span class="p">&gt;(</span><span class="mi">10</span><span class="p">)</span>
<span class="kd">val</span> <span class="py">oldValue</span> <span class="p">=</span> <span class="n">atomicReference</span><span class="p">.</span><span class="nf">getAndUpdate</span><span class="p">{</span><span class="n">it</span> <span class="p">+</span> <span class="mi">5</span><span class="p">}</span>
</code></pre></div></div>
<p>This allows us to create a new value for <code class="language-plaintext highlighter-rouge">atomicReference</code> based on its old value, but doesn’t simply modify the value already inside it.</p>

<p>This means that we cannot do this:</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">atomicReference</span> <span class="p">=</span> <span class="nc">AtomicReference</span><span class="p">&lt;</span><span class="nc">MutableList</span><span class="p">&lt;</span><span class="nc">Int</span><span class="p">&gt;&gt;(</span><span class="nf">mutableListOf</span><span class="p">())</span>
<span class="kd">val</span> <span class="py">oldValue</span> <span class="p">=</span> <span class="n">atomicReference</span><span class="p">.</span><span class="nf">getAndUpdate</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="mi">5</span><span class="p">))</span>
</code></pre></div></div>
<p>because the compiler will complain that <code class="language-plaintext highlighter-rouge">it.add(5)</code> returns <code class="language-plaintext highlighter-rouge">Boolean</code> when it was expecting <code class="language-plaintext highlighter-rouge">MutableList&lt;Int&gt;</code>.  This means that we will have to find some other way to deal with it.</p>

<h1 id="simply-running-the-updates-in-platformrunlater">Simply Running the Updates in Platform.runLater()</h1>

<p>Before we try anything else, let’s see what happens if we take the approach from the JavaDocs, and just put every update on the FXAT via a call to <code class="language-plaintext highlighter-rouge">Platform.runLater()</code>.  Because, if this works, then we’re all done!</p>

<p>I’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.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">TaskProgress5App</span> <span class="p">:</span> <span class="nc">Application</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">stage</span><span class="p">.</span><span class="n">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="nf">createContent</span><span class="p">(),</span> <span class="mf">800.0</span><span class="p">,</span> <span class="mf">500.0</span><span class="p">)</span>
        <span class="n">stage</span><span class="p">.</span><span class="n">title</span> <span class="p">=</span> <span class="s">"Task Progress Example"</span>
        <span class="n">stage</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="nd">@OptIn</span><span class="p">(</span><span class="nc">ExperimentalAtomicApi</span><span class="o">::</span><span class="k">class</span><span class="p">)</span>
    <span class="k">private</span> <span class="k">fun</span> <span class="nf">createContent</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">BorderPane</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">task</span> <span class="p">=</span> <span class="kd">object</span> <span class="err">: </span><span class="nc">Task</span><span class="p">&lt;</span><span class="nc">Unit</span><span class="p">&gt;()</span> <span class="p">{</span>

            <span class="kd">val</span> <span class="py">valueList</span><span class="p">:</span> <span class="nc">ObservableList</span><span class="p">&lt;</span><span class="nc">String</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">FXCollections</span><span class="p">.</span><span class="nf">observableArrayList</span><span class="p">()</span>
            <span class="kd">val</span> <span class="py">waitingEvents</span> <span class="p">=</span> <span class="nc">AtomicInt</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

            <span class="nf">init</span> <span class="p">{</span>
                <span class="nf">updateMessage</span><span class="p">(</span><span class="s">""</span><span class="p">)</span>
                <span class="nf">updateProgress</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span>
                <span class="nf">setOnSucceeded</span> <span class="p">{</span> <span class="n">evt</span> <span class="p">-&gt;</span> <span class="nf">updateProgress</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span> <span class="p">}</span>
            <span class="p">}</span>

            <span class="k">private</span> <span class="k">fun</span> <span class="nf">appendValue</span><span class="p">(</span><span class="n">var1</span><span class="p">:</span> <span class="nc">String</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="nc">Platform</span><span class="p">.</span><span class="nf">isFxApplicationThread</span><span class="p">())</span> <span class="p">{</span>
                    <span class="n">valueList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">var1</span><span class="p">)</span>
                <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                    <span class="nf">println</span><span class="p">(</span><span class="s">"Queuing: ${waitingEvents.incrementAndFetch()}"</span><span class="p">)</span>
                    <span class="nc">Platform</span><span class="p">.</span><span class="nf">runLater</span> <span class="p">{</span>
                        <span class="nf">println</span><span class="p">(</span><span class="s">"Performing: ${waitingEvents.decrementAndFetch()}"</span><span class="p">)</span>
                        <span class="n">valueList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">var1</span><span class="p">)</span>
                    <span class="p">}</span>
                <span class="p">}</span>
            <span class="p">}</span>

            <span class="k">override</span> <span class="k">fun</span> <span class="nf">call</span><span class="p">()</span> <span class="p">{</span>
                <span class="nf">handleProcessing2</span><span class="p">(</span>
                    <span class="p">{</span> <span class="n">done</span><span class="p">,</span> <span class="n">total</span> <span class="p">-&gt;</span> <span class="nf">updateProgress</span><span class="p">(</span><span class="n">done</span><span class="p">,</span> <span class="n">total</span><span class="p">)</span> <span class="p">},</span>
                    <span class="p">{</span> <span class="nf">appendValue</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">},</span>
                    <span class="p">{</span> <span class="nf">updateMessage</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">})</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="n">center</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">task</span><span class="p">.</span><span class="nf">messageProperty</span><span class="p">())</span>
            <span class="p">}</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">ProgressBar</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="nf">progressProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">task</span><span class="p">.</span><span class="nf">progressProperty</span><span class="p">())</span>
                <span class="n">minWidth</span> <span class="p">=</span> <span class="mf">200.0</span>
            <span class="p">}</span>
            <span class="n">alignment</span> <span class="p">=</span> <span class="nc">Pos</span><span class="p">.</span><span class="nc">CENTER</span>
        <span class="p">}</span>
        <span class="n">right</span> <span class="p">=</span> <span class="nc">TextArea</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="nc">ListToString</span><span class="p">(</span><span class="n">task</span><span class="p">.</span><span class="n">valueList</span><span class="p">))</span>
        <span class="p">}</span>
        <span class="n">bottom</span> <span class="p">=</span> <span class="nc">HBox</span><span class="p">(</span><span class="nc">Button</span><span class="p">(</span><span class="s">"Run Task"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nf">setOnAction</span> <span class="p">{</span> <span class="nc">Thread</span><span class="p">(</span><span class="n">task</span><span class="p">).</span><span class="nf">start</span><span class="p">()</span> <span class="p">}</span>
        <span class="p">}).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">alignment</span> <span class="p">=</span> <span class="nc">Pos</span><span class="p">.</span><span class="nc">CENTER</span>
            <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">20.0</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="nd">@OptIn</span><span class="p">(</span><span class="nc">ExperimentalAtomicApi</span><span class="o">::</span><span class="k">class</span><span class="p">)</span>
<span class="k">fun</span> <span class="nf">handleProcessing2</span><span class="p">(</span>
    <span class="n">progressUpdater</span><span class="p">:</span> <span class="p">(</span><span class="nc">Long</span><span class="p">,</span> <span class="nc">Long</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">Unit</span><span class="p">,</span>
    <span class="n">dataCollector</span><span class="p">:</span> <span class="p">(</span><span class="nc">String</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">Unit</span><span class="p">,</span>
    <span class="n">messageUpdater</span><span class="p">:</span> <span class="p">(</span><span class="nc">String</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">Unit</span>
<span class="p">)</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">maxCount</span> <span class="p">=</span> <span class="mi">1000</span>
    <span class="kd">val</span> <span class="py">threadCount</span> <span class="p">=</span> <span class="mi">50</span>
    <span class="kd">val</span> <span class="py">job</span> <span class="p">=</span> <span class="nc">Runnable</span> <span class="p">{</span>
        <span class="nf">dataCollector</span><span class="p">(</span><span class="s">"Thread ${Thread.currentThread().name} starting"</span><span class="p">)</span>
        <span class="k">for</span> <span class="p">(</span><span class="n">x</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="n">maxCount</span><span class="p">)</span> <span class="p">{</span>
            <span class="nf">dataCollector</span><span class="p">(</span><span class="s">"Thread ${Thread.currentThread().name}: $x"</span><span class="p">)</span>
            <span class="nc">Thread</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="nc">Duration</span><span class="p">.</span><span class="nf">ofMillis</span><span class="p">(</span><span class="mi">10</span><span class="p">))</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="kd">val</span> <span class="py">sTime</span> <span class="p">=</span> <span class="nc">LocalTime</span><span class="p">.</span><span class="nf">now</span><span class="p">()</span>

    <span class="nf">progressUpdater</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span>
    <span class="nf">messageUpdater</span><span class="p">(</span><span class="s">"Initializing"</span><span class="p">)</span>
    <span class="nc">Thread</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="nc">Duration</span><span class="p">.</span><span class="nf">ofMillis</span><span class="p">(</span><span class="mi">1000</span><span class="p">))</span>
    <span class="nf">progressUpdater</span><span class="p">(-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span>
    <span class="nf">messageUpdater</span><span class="p">(</span><span class="s">"Processing"</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">threadPool</span> <span class="p">=</span> <span class="nc">Executors</span><span class="p">.</span><span class="nf">newFixedThreadPool</span><span class="p">(</span><span class="n">threadCount</span><span class="p">)</span>
    <span class="nf">repeat</span><span class="p">(</span><span class="n">threadCount</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">threadPool</span><span class="p">.</span><span class="nf">execute</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
    <span class="p">}</span>
    <span class="nf">messageUpdater</span><span class="p">(</span><span class="s">"ThreadPool Started"</span><span class="p">)</span>
    <span class="n">threadPool</span><span class="p">.</span><span class="nf">shutdown</span><span class="p">()</span>
    <span class="kd">var</span> <span class="py">counter</span> <span class="p">=</span> <span class="mi">0</span>
    <span class="k">while</span> <span class="p">(!</span><span class="n">threadPool</span><span class="p">.</span><span class="nf">awaitTermination</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="nc">TimeUnit</span><span class="p">.</span><span class="nc">SECONDS</span><span class="p">))</span>
        <span class="nf">messageUpdater</span><span class="p">(</span><span class="s">"Still Waiting ${counter++}"</span><span class="p">)</span>
    <span class="nf">messageUpdater</span><span class="p">(</span><span class="s">"Completed ${sTime.until(LocalTime.now(), ChronoUnit.MILLIS)}"</span><span class="p">)</span>
    <span class="nf">progressUpdater</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="nc">ListToString</span><span class="p">(</span><span class="k">private</span> <span class="kd">val</span> <span class="py">theList</span><span class="p">:</span> <span class="nc">ObservableList</span><span class="p">&lt;</span><span class="nc">String</span><span class="p">&gt;)</span> <span class="p">:</span> <span class="nc">StringBinding</span><span class="p">()</span> <span class="p">{</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="k">super</span><span class="p">.</span><span class="nf">bind</span><span class="p">(</span><span class="n">theList</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">computeValue</span><span class="p">():</span> <span class="nc">String</span><span class="p">?</span> <span class="p">=</span>
        <span class="n">theList</span><span class="p">.</span><span class="nf">joinToString</span><span class="p">(</span><span class="s">"\n"</span><span class="p">)</span>
<span class="p">}</span>

<span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="nc">Application</span><span class="p">.</span><span class="nf">launch</span><span class="p">(</span><span class="nc">TaskProgress5App</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>We have a <code class="language-plaintext highlighter-rouge">TextField</code> that has its <code class="language-plaintext highlighter-rouge">textProperty()</code> bound to <code class="language-plaintext highlighter-rouge">Task.valueList</code> through the <code class="language-plaintext highlighter-rouge">Binding</code> called, <code class="language-plaintext highlighter-rouge">ListToString</code>.  This means that as <code class="language-plaintext highlighter-rouge">Task.valueList</code> grows, the text in the <code class="language-plaintext highlighter-rouge">TextArea</code> should grow with it.</p>

<p>The background job uses an <code class="language-plaintext highlighter-rouge">ExecutorPool</code> of 50 threads to each run a simple job that just generates a <code class="language-plaintext highlighter-rouge">String</code> which is passed back to the <code class="language-plaintext highlighter-rouge">Task</code> and then sleeps for 10 ms.  When the <code class="language-plaintext highlighter-rouge">Task</code> gets each <code class="language-plaintext highlighter-rouge">String</code> from the background thread, it uses <code class="language-plaintext highlighter-rouge">Platform.runLater()</code> to add that <code class="language-plaintext highlighter-rouge">String</code> to <code class="language-plaintext highlighter-rouge">Task.valueList</code>.</p>

<p>This means that, with 50 threads each generating 1000 <code class="language-plaintext highlighter-rouge">Strings</code>, with a 10ms delay between them, we get 50,000 <code class="language-plaintext highlighter-rouge">Strings</code> submitted via 50,000 <code class="language-plaintext highlighter-rouge">Platform.runLater()</code> calls in a 10 second period.  This means it will generate 50,000 FXAT jobs in that 10s.</p>

<p>It looks like this when it is running:</p>

<p><img src="/assets/elements/TaskProgress4.png" alt="Screen Snap 1" /></p>

<p>Honestly, when I first ran this without the <code class="language-plaintext highlighter-rouge">println()</code> statements, it just looked hung.  When I added them, I saw that <code class="language-plaintext highlighter-rouge">waitingEvents</code> rose up to 40K+ within a matter of seconds, the <code class="language-plaintext highlighter-rouge">Task</code> completed, and <strong>then it continued for several minutes</strong> to process all of the jobs on the FXAT and decrement the <code class="language-plaintext highlighter-rouge">waitingEvents</code> counter.</p>

<p>An earlier version of this code had the <code class="language-plaintext highlighter-rouge">ProgressIndicator</code> 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.</p>

<div class="notice--warning">
 <img src="/assets/logos/brain.png" alt="warning" style="float:left;margin-right: 10px" />
 <p style="overflow:auto; float:none">
   Never flood the FXAT with thousands and thousands of jobs submitted via `Platform.runLater()`
 </p>
</div>

<p>When the jobs on the FXAT finally all complete, after 10 minutes or more, it looks like this:</p>

<p><img src="/assets/elements/TaskProgress5.png" alt="Screen Snap 2" /></p>

<p>You 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.</p>

<div class="notice--info">
 <img src="/assets/logos/brain.png" alt="info" style="float:left;margin-right: 10px" />
 <p style="overflow:auto; float:none">
   Clearly, the methodology suggested by the JavaDocs is not a viable approach.
 </p>
</div>

<h1 id="using-synchronized-operations">Using Synchronized Operations</h1>

<p>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 <code class="language-plaintext highlighter-rouge">ObservableList</code>, but we cannot use the thread-safe <code class="language-plaintext highlighter-rouge">Atomic</code> classes to do this.  The answer, is to employ locks on our update <code class="language-plaintext highlighter-rouge">List</code> so that each thread, including the FXAT, can update it without causing data integrity issues.</p>

<p>Let’s look at how we’ll do this….</p>

<p>The only thing that needs to change is our <code class="language-plaintext highlighter-rouge">Task</code>.  The inner workings of the update process are hidden from the rest of the code in the application, so it won’t be impacted:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">task</span> <span class="p">=</span> <span class="kd">object</span> <span class="err">: </span><span class="nc">Task</span><span class="p">&lt;</span><span class="nc">Unit</span><span class="p">&gt;()</span> <span class="p">{</span>

    <span class="k">private</span> <span class="kd">val</span> <span class="py">dataList</span> <span class="p">=</span> <span class="n">mutableListOf</span><span class="p">&lt;</span><span class="nc">String</span><span class="p">&gt;()</span>
    <span class="kd">val</span> <span class="py">valueList</span><span class="p">:</span> <span class="nc">ObservableList</span><span class="p">&lt;</span><span class="nc">String</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">FXCollections</span><span class="p">.</span><span class="nf">observableArrayList</span><span class="p">()</span>
    <span class="kd">val</span> <span class="py">counter</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="nf">updateMessage</span><span class="p">(</span><span class="s">""</span><span class="p">)</span>
        <span class="nf">updateProgress</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span>
        <span class="nf">setOnSucceeded</span> <span class="p">{</span> <span class="n">evt</span> <span class="p">-&gt;</span> <span class="nf">updateProgress</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span> <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">appendValue</span><span class="p">(</span><span class="n">var1</span><span class="p">:</span> <span class="nc">String</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="nc">Platform</span><span class="p">.</span><span class="nf">isFxApplicationThread</span><span class="p">())</span> <span class="p">{</span>
            <span class="n">valueList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">var1</span><span class="p">)</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="kd">val</span> <span class="py">sTime</span> <span class="p">=</span> <span class="nc">LocalTime</span><span class="p">.</span><span class="nf">now</span><span class="p">()</span>
            <span class="nf">synchronized</span><span class="p">(</span><span class="n">dataList</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">dataList</span><span class="p">.</span><span class="nf">isEmpty</span><span class="p">())</span> <span class="p">{</span>
                    <span class="nc">Platform</span><span class="p">.</span><span class="nf">runLater</span> <span class="p">{</span>
                        <span class="kd">val</span> <span class="py">sTime</span> <span class="p">=</span> <span class="nc">LocalTime</span><span class="p">.</span><span class="nf">now</span><span class="p">()</span>
                        <span class="nf">synchronized</span><span class="p">(</span><span class="n">dataList</span><span class="p">)</span> <span class="p">{</span>
                            <span class="n">valueList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span>
                                <span class="s">"FX Event ${counter.value++} happened in ${
</span>                                    <span class="n">sTime</span><span class="p">.</span><span class="nf">until</span><span class="p">(</span><span class="nc">LocalTime</span><span class="p">.</span><span class="nf">now</span><span class="p">(),</span> <span class="nc">ChronoUnit</span><span class="p">.</span><span class="nc">MILLIS</span><span class="p">)</span>
                                <span class="p">}</span><span class="n">ms</span><span class="err">\</span><span class="n">n</span><span class="s">"
</span>                            <span class="p">)</span>
                            <span class="n">valueList</span><span class="p">.</span><span class="nf">addAll</span><span class="p">(</span><span class="n">dataList</span><span class="p">)</span>
                            <span class="n">dataList</span><span class="p">.</span><span class="nf">clear</span><span class="p">()</span>
                        <span class="p">}</span>
                    <span class="p">}</span>
                <span class="p">}</span>
                <span class="n">dataList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">var1</span> <span class="p">+</span> <span class="s">" ${sTime.until(LocalTime.now(), ChronoUnit.MILLIS)}"</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">call</span><span class="p">()</span> <span class="p">{</span>
        <span class="nf">handleProcessing1</span><span class="p">(</span>
            <span class="p">{</span> <span class="n">done</span><span class="p">,</span> <span class="n">total</span> <span class="p">-&gt;</span> <span class="nf">updateProgress</span><span class="p">(</span><span class="n">done</span><span class="p">,</span> <span class="n">total</span><span class="p">)</span> <span class="p">},</span>
            <span class="p">{</span> <span class="nf">appendValue</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">},</span>
            <span class="p">{</span> <span class="nf">updateMessage</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">})</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Here we’ve introduced a <code class="language-plaintext highlighter-rouge">private</code> field called <code class="language-plaintext highlighter-rouge">dataList</code> which is just a <code class="language-plaintext highlighter-rouge">MutableList&lt;String&gt;</code>.  This is what we are going to use to “batch” our updates to the <code class="language-plaintext highlighter-rouge">ObservableList</code> on the FXAT.  The rest of the changes are in <code class="language-plaintext highlighter-rouge">appendValue()</code>.</p>

<p>If you’ve looked at the source code for <code class="language-plaintext highlighter-rouge">Task.updateMessage()</code> above, then you should recognize the pattern here.  First, if we are on the <code class="language-plaintext highlighter-rouge">FXAT</code> then we just add the value to <code class="language-plaintext highlighter-rouge">valueList</code> without any fuss.  If we are <em>not</em> on the FXAT, then we need to add our new <code class="language-plaintext highlighter-rouge">String</code> to <code class="language-plaintext highlighter-rouge">dataList</code> and decide if we need to start a new job on the FXAT.  That determination is done by looking at <code class="language-plaintext highlighter-rouge">dataList</code> to see if it is empty before we add our new value.  If it is, then we need to start a new FXAT job.</p>

<p>That FXAT job does three things:</p>

<ol>
  <li>Adds a message to <code class="language-plaintext highlighter-rouge">valueList</code> that says, “FXAT happened”.  This way we can track how many updates end up in each update “batch”.</li>
  <li>Adds all of the items in <code class="language-plaintext highlighter-rouge">dataList</code> to <code class="language-plaintext highlighter-rouge">valueList</code>.</li>
  <li>Clears out <code class="language-plaintext highlighter-rouge">dataList</code>.</li>
</ol>

<p>All three things are done inside a <code class="language-plaintext highlighter-rouge">synchronized</code> block locked on <code class="language-plaintext highlighter-rouge">dataList</code>.</p>

<p>The last thing that <code class="language-plaintext highlighter-rouge">appendValue()</code> does is to add our new value to <code class="language-plaintext highlighter-rouge">dataList</code> after appending some timing information to it.</p>

<p>In order to avoid concurrency problems, all of this activity: checking if <code class="language-plaintext highlighter-rouge">dataList</code> is empty, possibly launching a new job on the FXAT and appending the new value to <code class="language-plaintext highlighter-rouge">dataList</code> is done in a <code class="language-plaintext highlighter-rouge">synchronized</code> block, locking on <code class="language-plaintext highlighter-rouge">dataList</code>.<br />
We should look at where this code runs:</p>

<ul>
  <li>Every call to <code class="language-plaintext highlighter-rouge">appendValue()</code> runs on one of the 50 threads in the <code class="language-plaintext highlighter-rouge">ExecutorPool</code>.  There can, therefore, be multiple simultaneous access from many different threads to this code at any given time.</li>
  <li>The code that copies data from <code class="language-plaintext highlighter-rouge">dataList</code> to <code class="language-plaintext highlighter-rouge">valueList</code> and clears out <code class="language-plaintext highlighter-rouge">dataList</code> always runs from the FXAT, and may run at the same time that any number of threads are running <code class="language-plaintext highlighter-rouge">appendValue()</code>.</li>
</ul>

<p>The <code class="language-plaintext highlighter-rouge">synchronized</code> blocks around every single access to <code class="language-plaintext highlighter-rouge">dataList</code> ensure that only one thread can execute code in either of those two blocks at any given time.</p>

<p>This means that when a background thread is adding a new value to <code class="language-plaintext highlighter-rouge">dataList</code>, no other background threads can do the same thing, and the <code class="language-plaintext highlighter-rouge">synchronized</code> 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 <code class="language-plaintext highlighter-rouge">synchronized</code> block in <code class="language-plaintext highlighter-rouge">appendValue()</code>.  If they cannot enter, they wait.</p>

<p>For this reason, the time calculation code doesn’t just return <code class="language-plaintext highlighter-rouge">0</code> 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’s a time calculation on both the background threads and the FXAT job.  We also count how many times it runs the FXAT job.</p>

<p>It looks like this:</p>

<p><img src="/assets/elements/TaskProgress6.png" alt="Screen Snap 3" /></p>

<p>We 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.</p>

<p>On 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.</p>

<p>You 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.</p>

<h1 id="about-that-binding">About that Binding</h1>

<p>In this example, the contents of the <code class="language-plaintext highlighter-rouge">TextArea</code> reflect the contents of <code class="language-plaintext highlighter-rouge">Task.valueList</code> through a custom <code class="language-plaintext highlighter-rouge">Binding</code>:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">ListToString</span><span class="p">(</span><span class="k">private</span> <span class="kd">val</span> <span class="py">theList</span><span class="p">:</span> <span class="nc">ObservableList</span><span class="p">&lt;</span><span class="nc">String</span><span class="p">&gt;)</span> <span class="p">:</span> <span class="nc">StringBinding</span><span class="p">()</span> <span class="p">{</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="k">super</span><span class="p">.</span><span class="nf">bind</span><span class="p">(</span><span class="n">theList</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">computeValue</span><span class="p">():</span> <span class="nc">String</span><span class="p">?</span> <span class="p">=</span>
        <span class="n">theList</span><span class="p">.</span><span class="nf">joinToString</span><span class="p">(</span><span class="s">"\n"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This might seem a little “heavy handed” at first.  The entire <code class="language-plaintext highlighter-rouge">String</code> is rebuilt every time that the <code class="language-plaintext highlighter-rouge">ObservableList</code> changes.  Surely it might be better to put a <code class="language-plaintext highlighter-rouge">ListChangeListener</code> on <code class="language-plaintext highlighter-rouge">valueList</code> instead, and then just apply the changes to the <code class="language-plaintext highlighter-rouge">TextArea</code>?</p>

<ol>
  <li>
    <p>We really cannot make any assumptions about how the layout is going to use the <code class="language-plaintext highlighter-rouge">Properties</code> that we expose from our <code class="language-plaintext highlighter-rouge">Task</code>.  We need to design our implementation such that any “normal” usage of the <code class="language-plaintext highlighter-rouge">Property</code> is going to be functional.  That includes a “heavy-handed” <code class="language-plaintext highlighter-rouge">Binding</code> like this.</p>
  </li>
  <li>
    <p>Remember that <code class="language-plaintext highlighter-rouge">String</code> in Java is immutable.  So any attempt to append data to a <code class="language-plaintext highlighter-rouge">String</code> is going to result in copying the entire <code class="language-plaintext highlighter-rouge">String</code> over and adding the new text.  Is this more efficient than <code class="language-plaintext highlighter-rouge">List.joinToString()</code>?  Maybe, but it’s probably more a matter of degree than magnitude.</p>
  </li>
</ol>

<div class="notice--info">
 <img src="/assets/logos/brain.png" alt="info" style="float:left;margin-right: 10px" />
 <p style="overflow:auto; float:none">
   Bindings are recalculated in-line with the code that invalidates their dependencies.
 </p>
</div>

<p><code class="language-plaintext highlighter-rouge">Bindings</code> are not executed through <code class="language-plaintext highlighter-rouge">EventHandlers</code> that are submitted as new jobs an the FXAT.  This means that you can measure their impact on performance directly.  In our FXAT <code class="language-plaintext highlighter-rouge">Runnable</code> if we move the code that generatest the “FX Event…” text from the top of the <code class="language-plaintext highlighter-rouge">synchronized</code> block to the bottom - after <code class="language-plaintext highlighter-rouge">valueList</code> has been modified, and <em>after</em> the <code class="language-plaintext highlighter-rouge">ListToString.computeValue()</code> has run - we get this:</p>

<p><img src="/assets/elements/TaskProgress7.png" alt="Screen Snap 4" /></p>

<p>and this:</p>

<p><img src="/assets/elements/TaskProgress8.png" alt="Screen Snap 5" /></p>

<p>You can see the top and the bottom of the timings, and as <code class="language-plaintext highlighter-rouge">valueList</code> gets bigger, the times get longer.  I checked and this is a progression through the output.</p>

<p>I 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.</p>

<p>I 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 <code class="language-plaintext highlighter-rouge">sleep(10)</code> 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.</p>

<div class="notice_question--primary">
 <img src="/assets/logos/BRAIN_Question.png" alt="primary" style="float:left;margin-right: 10px" />
 <p style="overflow:auto; float:none">
   Is there anything we can do here to limit the time that the FXAT job spends in the syncronized block?
 </p>
</div>

<p>Absolutely!  We need to extract the values from <code class="language-plaintext highlighter-rouge">dataList</code> and then clear it out inside the <code class="language-plaintext highlighter-rouge">synchonized</code> block, but we don’t have to update <code class="language-plaintext highlighter-rouge">valueList</code> inside the synchronized block, since we can only ever update it from the single FXAT thread:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="k">fun</span> <span class="nf">appendValue</span><span class="p">(</span><span class="n">var1</span><span class="p">:</span> <span class="nc">String</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="nc">Platform</span><span class="p">.</span><span class="nf">isFxApplicationThread</span><span class="p">())</span> <span class="p">{</span>
        <span class="n">valueList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">var1</span><span class="p">)</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">sTime</span> <span class="p">=</span> <span class="nc">LocalTime</span><span class="p">.</span><span class="nf">now</span><span class="p">()</span>
        <span class="nf">synchronized</span><span class="p">(</span><span class="n">dataList</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">dataList</span><span class="p">.</span><span class="nf">isEmpty</span><span class="p">())</span> <span class="p">{</span>
                <span class="nc">Platform</span><span class="p">.</span><span class="nf">runLater</span> <span class="p">{</span>
                    <span class="kd">val</span> <span class="py">sTime</span> <span class="p">=</span> <span class="nc">LocalTime</span><span class="p">.</span><span class="nf">now</span><span class="p">()</span>
                    <span class="kd">val</span> <span class="py">tempList</span> <span class="p">=</span> <span class="n">mutableListOf</span><span class="p">&lt;</span><span class="nc">String</span><span class="p">&gt;()</span>
                    <span class="nf">synchronized</span><span class="p">(</span><span class="n">dataList</span><span class="p">)</span> <span class="p">{</span>
                        <span class="n">tempList</span><span class="p">.</span><span class="nf">addAll</span><span class="p">(</span><span class="n">dataList</span><span class="p">)</span>
                        <span class="n">dataList</span><span class="p">.</span><span class="nf">clear</span><span class="p">()</span>
                        <span class="n">valueList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span>
                            <span class="s">"FX Event ${counter.value++} step 1 in ${
</span>                                <span class="n">sTime</span><span class="p">.</span><span class="nf">until</span><span class="p">(</span><span class="nc">LocalTime</span><span class="p">.</span><span class="nf">now</span><span class="p">(),</span> <span class="nc">ChronoUnit</span><span class="p">.</span><span class="nc">MILLIS</span><span class="p">)</span>
                            <span class="p">}</span><span class="n">ms</span><span class="s">"
</span>                        <span class="p">)</span>
                    <span class="p">}</span>
                    <span class="n">valueList</span><span class="p">.</span><span class="nf">addAll</span><span class="p">(</span><span class="n">tempList</span><span class="p">)</span>
                    <span class="n">valueList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span>
                        <span class="s">"FX Event ${counter.value++} done 1 in ${
</span>                            <span class="n">sTime</span><span class="p">.</span><span class="nf">until</span><span class="p">(</span><span class="nc">LocalTime</span><span class="p">.</span><span class="nf">now</span><span class="p">(),</span> <span class="nc">ChronoUnit</span><span class="p">.</span><span class="nc">MILLIS</span><span class="p">)</span>
                        <span class="p">}</span><span class="n">ms</span><span class="err">\</span><span class="n">n</span><span class="s">"
</span>                    <span class="p">)</span>
                <span class="p">}</span>
            <span class="p">}</span>
            <span class="n">dataList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">var1</span> <span class="p">+</span> <span class="s">" ${sTime.until(LocalTime.now(), ChronoUnit.MILLIS)}"</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Here, I’ve added a <code class="language-plaintext highlighter-rouge">tempList</code> local variable which is just a <code class="language-plaintext highlighter-rouge">MutableList&lt;String&gt;</code>.  The elements from <code class="language-plaintext highlighter-rouge">dataList</code> are extracted into it, <code class="language-plaintext highlighter-rouge">dataList</code> is cleared and the lock is released.  Then, outside of the <code class="language-plaintext highlighter-rouge">synchronized</code> block, the contents of <code class="language-plaintext highlighter-rouge">tempList</code> are added to <code class="language-plaintext highlighter-rouge">valueList</code> and the <code class="language-plaintext highlighter-rouge">ListToString Binding</code> will run.  The output looks like this:</p>

<p><img src="/assets/elements/TaskProgress9.png" alt="Screen Snap 6" /></p>

<p>You can see that we’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’ve added an intermediate timer, so you can see how long the <code class="language-plaintext highlighter-rouge">synchronized</code> part takes, and it’s usually 0ms.  The <code class="language-plaintext highlighter-rouge">Binding</code> 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.</p>

<p>In my testing, I found that everything continued to work fine, even as I lowered the <code class="language-plaintext highlighter-rouge">sleep</code> duration in each thread down to one or two milliseconds.</p>

<h1 id="returning-a-value">Returning a Value</h1>

<p>What we’ve done so far is good, but it falls short of solving <code class="language-plaintext highlighter-rouge">Task</code> as an accumulator that delivers a final value.</p>

<p>The problem is that <code class="language-plaintext highlighter-rouge">Task.value</code> is private, and <code class="language-plaintext highlighter-rouge">Task.valueProperty</code> exposes <code class="language-plaintext highlighter-rouge">Task.value</code> as a <code class="language-plaintext highlighter-rouge">ReadOnlyObjectProperty</code>, even to sub-classes of <code class="language-plaintext highlighter-rouge">Task</code>.  The only way that we can update <code class="language-plaintext highlighter-rouge">Task.value</code> is by calling <code class="language-plaintext highlighter-rouge">Task.updateValue()</code>, and this uses the same <code class="language-plaintext highlighter-rouge">AtomicReference</code> logic as we saw in <code class="language-plaintext highlighter-rouge">Task.updateMessage()</code>, it won’t work when we are trying to add items to a <code class="language-plaintext highlighter-rouge">List</code>.</p>

<p>It’s one thing to add a custom <code class="language-plaintext highlighter-rouge">valueList</code> and expose that to the layout, but if we want our <code class="language-plaintext highlighter-rouge">Task</code> to work in a more generic fashion, then we need to have some way to transfer <code class="language-plaintext highlighter-rouge">valueList</code> to <code class="language-plaintext highlighter-rouge">Task.value</code> when the processing is complete.  So, this is the last item we need to address.</p>

<p>There’s one issue that comes from using <code class="language-plaintext highlighter-rouge">Platform.runLater()</code> to batch update the <code class="language-plaintext highlighter-rouge">ObservableList</code>.  The <code class="language-plaintext highlighter-rouge">Task</code> is virtually guaranteed to complete before the all the jobs have been run on the FXAT.</p>

<p>We saw this with the first version, just launching many, many jobs using <code class="language-plaintext highlighter-rouge">Platform.runLater()</code>.  The <code class="language-plaintext highlighter-rouge">Task</code> 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 <code class="language-plaintext highlighter-rouge">Task</code> would be incorrect.</p>

<p>Let’s see how this impacts the layout.  To make things simpler, and easier to see where there are issues, we’ll make the results of the <code class="language-plaintext highlighter-rouge">Task</code> the <em>size</em> of the <code class="language-plaintext highlighter-rouge">ObservableList</code> instead of the <code class="language-plaintext highlighter-rouge">List</code> itself:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="k">fun</span> <span class="nf">createContent</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">BorderPane</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">task</span> <span class="p">=</span> <span class="kd">object</span> <span class="err">: </span><span class="nc">Task</span><span class="p">&lt;</span><span class="nc">Int</span><span class="p">&gt;()</span> <span class="p">{</span>

        <span class="k">private</span> <span class="kd">val</span> <span class="py">dataList</span> <span class="p">=</span> <span class="n">mutableListOf</span><span class="p">&lt;</span><span class="nc">String</span><span class="p">&gt;()</span>
        <span class="kd">val</span> <span class="py">valueList</span><span class="p">:</span> <span class="nc">ListProperty</span><span class="p">&lt;</span><span class="nc">String</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">SimpleListProperty</span><span class="p">(</span><span class="nc">FXCollections</span><span class="p">.</span><span class="nf">observableArrayList</span><span class="p">())</span>
        <span class="kd">val</span> <span class="py">counter</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
        <span class="kd">val</span> <span class="py">manualSize</span> <span class="p">=</span> <span class="nc">SimpleIntegerProperty</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

        <span class="nf">init</span> <span class="p">{</span>
            <span class="nf">updateMessage</span><span class="p">(</span><span class="s">""</span><span class="p">)</span>
            <span class="nf">updateProgress</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span>
            <span class="nf">setOnSucceeded</span> <span class="p">{</span> <span class="n">evt</span> <span class="p">-&gt;</span> <span class="nf">updateProgress</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span> <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">private</span> <span class="k">fun</span> <span class="nf">appendValue</span><span class="p">(</span><span class="n">var1</span><span class="p">:</span> <span class="nc">String</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="nc">Platform</span><span class="p">.</span><span class="nf">isFxApplicationThread</span><span class="p">())</span> <span class="p">{</span>
                <span class="n">valueList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">var1</span><span class="p">)</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="kd">val</span> <span class="py">sTime</span> <span class="p">=</span> <span class="nc">LocalTime</span><span class="p">.</span><span class="nf">now</span><span class="p">()</span>
                <span class="nf">synchronized</span><span class="p">(</span><span class="n">dataList</span><span class="p">)</span> <span class="p">{</span>
                    <span class="k">if</span> <span class="p">(</span><span class="n">dataList</span><span class="p">.</span><span class="nf">isEmpty</span><span class="p">())</span> <span class="p">{</span>
                        <span class="nc">Platform</span><span class="p">.</span><span class="nf">runLater</span> <span class="p">{</span>
                            <span class="kd">val</span> <span class="py">sTime</span> <span class="p">=</span> <span class="nc">LocalTime</span><span class="p">.</span><span class="nf">now</span><span class="p">()</span>
                            <span class="kd">val</span> <span class="py">tempList</span> <span class="p">=</span> <span class="n">mutableListOf</span><span class="p">&lt;</span><span class="nc">String</span><span class="p">&gt;()</span>
                            <span class="nf">synchronized</span><span class="p">(</span><span class="n">dataList</span><span class="p">)</span> <span class="p">{</span>
                                <span class="n">tempList</span><span class="p">.</span><span class="nf">addAll</span><span class="p">(</span><span class="n">dataList</span><span class="p">)</span>
                                <span class="n">dataList</span><span class="p">.</span><span class="nf">clear</span><span class="p">()</span>
                                <span class="n">valueList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span>
                                    <span class="s">"FX Event ${counter.value++} step 1 in ${
</span>                                        <span class="n">sTime</span><span class="p">.</span><span class="nf">until</span><span class="p">(</span><span class="nc">LocalTime</span><span class="p">.</span><span class="nf">now</span><span class="p">(),</span> <span class="nc">ChronoUnit</span><span class="p">.</span><span class="nc">MILLIS</span><span class="p">)</span>
                                    <span class="p">}</span><span class="n">ms</span><span class="s">"
</span>                                <span class="p">)</span>
                            <span class="p">}</span>
                            <span class="n">valueList</span><span class="p">.</span><span class="nf">addAll</span><span class="p">(</span><span class="n">tempList</span><span class="p">)</span>
                            <span class="n">valueList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span>
                                <span class="s">"FX Event ${counter.value++} done 1 in ${
</span>                                    <span class="n">sTime</span><span class="p">.</span><span class="nf">until</span><span class="p">(</span><span class="nc">LocalTime</span><span class="p">.</span><span class="nf">now</span><span class="p">(),</span> <span class="nc">ChronoUnit</span><span class="p">.</span><span class="nc">MILLIS</span><span class="p">)</span>
                                <span class="p">}</span><span class="n">ms</span><span class="err">\</span><span class="n">n</span><span class="s">"
</span>                            <span class="p">)</span>
                        <span class="p">}</span>
                    <span class="p">}</span>
                    <span class="n">dataList</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">var1</span> <span class="p">+</span> <span class="s">" ${sTime.until(LocalTime.now(), ChronoUnit.MILLIS)}"</span><span class="p">)</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">override</span> <span class="k">fun</span> <span class="nf">call</span><span class="p">():</span> <span class="nc">Int</span> <span class="p">{</span>
            <span class="nf">handleProcessing3</span><span class="p">(</span>
                <span class="p">{</span> <span class="n">done</span><span class="p">,</span> <span class="n">total</span> <span class="p">-&gt;</span> <span class="nf">updateProgress</span><span class="p">(</span><span class="n">done</span><span class="p">,</span> <span class="n">total</span><span class="p">)</span> <span class="p">},</span>
                <span class="p">{</span> <span class="nf">appendValue</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">},</span>
                <span class="p">{</span> <span class="nf">updateMessage</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">})</span>
            <span class="kd">val</span> <span class="py">tempSize</span> <span class="p">=</span> <span class="n">valueList</span><span class="p">.</span><span class="n">size</span>
            <span class="nc">Platform</span><span class="p">.</span><span class="nf">runLater</span> <span class="p">{</span> <span class="n">manualSize</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">tempSize</span> <span class="p">}</span>
            <span class="k">return</span> <span class="n">valueList</span><span class="p">.</span><span class="n">size</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="n">center</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">(</span>
            <span class="mf">10.0</span><span class="p">,</span>
            <span class="nf">promptOf</span><span class="p">(</span><span class="s">"Manual Size:"</span><span class="p">),</span>
            <span class="nc">Label</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span> <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">task</span><span class="p">.</span><span class="n">manualSize</span><span class="p">.</span><span class="nf">asString</span><span class="p">())</span> <span class="p">})</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">(</span>
            <span class="mf">10.0</span><span class="p">,</span>
            <span class="nf">promptOf</span><span class="p">(</span><span class="s">"Counted Size:"</span><span class="p">),</span>
            <span class="nc">Label</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span> <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">task</span><span class="p">.</span><span class="n">valueList</span><span class="p">.</span><span class="nf">sizeProperty</span><span class="p">().</span><span class="nf">asString</span><span class="p">())</span> <span class="p">})</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">(</span>
            <span class="mf">10.0</span><span class="p">,</span>
            <span class="nf">promptOf</span><span class="p">(</span><span class="s">"Task Value:"</span><span class="p">),</span>
            <span class="nc">Label</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span> <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">task</span><span class="p">.</span><span class="nf">valueProperty</span><span class="p">().</span><span class="nf">asString</span><span class="p">())</span> <span class="p">})</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">(</span>
            <span class="mf">10.0</span><span class="p">,</span>
            <span class="nf">promptOf</span><span class="p">(</span><span class="s">"Task Completion:"</span><span class="p">),</span>
            <span class="nc">Label</span><span class="p">(</span><span class="s">"None"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="n">task</span><span class="p">.</span><span class="nf">setOnSucceeded</span> <span class="p">{</span> <span class="n">evt</span> <span class="p">-&gt;</span> <span class="k">this</span><span class="p">.</span><span class="n">text</span> <span class="p">=</span> <span class="n">task</span><span class="p">.</span><span class="k">get</span><span class="p">().</span><span class="nf">toString</span><span class="p">()</span> <span class="p">}</span>
            <span class="p">})</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">task</span><span class="p">.</span><span class="nf">messageProperty</span><span class="p">())</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">ProgressBar</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nf">progressProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">task</span><span class="p">.</span><span class="nf">progressProperty</span><span class="p">())</span>
            <span class="n">minWidth</span> <span class="p">=</span> <span class="mf">200.0</span>
        <span class="p">}</span>
        <span class="n">alignment</span> <span class="p">=</span> <span class="nc">Pos</span><span class="p">.</span><span class="nc">CENTER</span>
    <span class="p">}</span>
    <span class="n">right</span> <span class="p">=</span> <span class="nc">TextArea</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="nc">ListToString</span><span class="p">(</span><span class="n">task</span><span class="p">.</span><span class="n">valueList</span><span class="p">))</span>
    <span class="p">}</span>
    <span class="n">bottom</span> <span class="p">=</span> <span class="nc">HBox</span><span class="p">(</span><span class="nc">Button</span><span class="p">(</span><span class="s">"Run Task"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="nf">setOnAction</span> <span class="p">{</span> <span class="nc">Thread</span><span class="p">(</span><span class="n">task</span><span class="p">).</span><span class="nf">start</span><span class="p">()</span> <span class="p">}</span>
    <span class="p">}).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">alignment</span> <span class="p">=</span> <span class="nc">Pos</span><span class="p">.</span><span class="nc">CENTER</span>
        <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">20.0</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>In the layout, I’ve added a few <code class="language-plaintext highlighter-rouge">Labels</code> to display various values of the size of <code class="language-plaintext highlighter-rouge">Task.valueList</code>:</p>

<ul>
  <li>The one tagged with “Counted Size” is simply bound to the size of the <code class="language-plaintext highlighter-rouge">Task.valueList</code>.  I switched <code class="language-plaintext highlighter-rouge">valueList</code> over to a <code class="language-plaintext highlighter-rouge">ListProperty</code> instead of just an <code class="language-plaintext highlighter-rouge">ObservableList</code> because <code class="language-plaintext highlighter-rouge">ListProperty</code> has a <code class="language-plaintext highlighter-rouge">size Property</code>.</li>
  <li>The one tagged “Manual Size” is bound to a <code class="language-plaintext highlighter-rouge">IntegerProperty</code> that I added to <code class="language-plaintext highlighter-rouge">task</code> called <code class="language-plaintext highlighter-rouge">manualSize</code>.  The value is updated manually by <code class="language-plaintext highlighter-rouge">task.call()</code>.</li>
  <li>The one tagged with “Task Value” is bound to <code class="language-plaintext highlighter-rouge">task.valueProperty</code>.</li>
  <li>The one tagged “Task Completion” is manually updated through the <code class="language-plaintext highlighter-rouge">task.onSucceeded Property</code>, which calls <code class="language-plaintext highlighter-rouge">task.get()</code>.</li>
</ul>

<p>All together, this is probably over-kill, but you’ll see the problem.</p>

<p>The type of <code class="language-plaintext highlighter-rouge">task</code> was changed from <code class="language-plaintext highlighter-rouge">Void</code> to <code class="language-plaintext highlighter-rouge">Int</code> so that we now have a value that we can grab.  Ordinarily, for something like this, you’d probably make the type of <code class="language-plaintext highlighter-rouge">task</code> an <code class="language-plaintext highlighter-rouge">ObservableList</code>, but keeping it as <code class="language-plaintext highlighter-rouge">Int</code> makes it easier to see the issues here.</p>

<p>Aside from the added and changed <code class="language-plaintext highlighter-rouge">Properties</code> in <code class="language-plaintext highlighter-rouge">task</code>, the only other change was in <code class="language-plaintext highlighter-rouge">task.call()</code>, which now looks like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">override</span> <span class="k">fun</span> <span class="nf">call</span><span class="p">():</span> <span class="nc">Int</span> <span class="p">{</span>
    <span class="nf">handleProcessing3</span><span class="p">(</span>
        <span class="p">{</span> <span class="n">done</span><span class="p">,</span> <span class="n">total</span> <span class="p">-&gt;</span> <span class="nf">updateProgress</span><span class="p">(</span><span class="n">done</span><span class="p">,</span> <span class="n">total</span><span class="p">)</span> <span class="p">},</span>
        <span class="p">{</span> <span class="nf">appendValue</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">},</span>
        <span class="p">{</span> <span class="nf">updateMessage</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">})</span>
    <span class="kd">val</span> <span class="py">tempSize</span> <span class="p">=</span> <span class="n">valueList</span><span class="p">.</span><span class="n">size</span>
    <span class="nc">Platform</span><span class="p">.</span><span class="nf">runLater</span> <span class="p">{</span> <span class="n">manualSize</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">tempSize</span> <span class="p">}</span>
    <span class="k">return</span> <span class="n">valueList</span><span class="p">.</span><span class="n">size</span>
<span class="p">}</span>
</code></pre></div></div>
<p>As you can see, <code class="language-plaintext highlighter-rouge">task.call()</code> now returns <code class="language-plaintext highlighter-rouge">Int</code>.  Three lines have been added to the bottom of this method.  First we create a local variable called <code class="language-plaintext highlighter-rouge">tempSize</code> and initialize it with the size of <code class="language-plaintext highlighter-rouge">valueList</code>.  To update <code class="language-plaintext highlighter-rouge">task.manualSize</code> we need to be on the FXAT, so we’ll need <code class="language-plaintext highlighter-rouge">Platform.runLater()</code> here.  Then, finally, we return valueList.size.</p>

<p>A few points:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Task.call()</code> itself runs in a background thread.</li>
  <li>We don’t really know what invokes <code class="language-plaintext highlighter-rouge">Task.call()</code>.</li>
  <li>Whatever value goes into <code class="language-plaintext highlighter-rouge">Task.value</code> has to be updated on the FXAT.  This means that whatever calls <code class="language-plaintext highlighter-rouge">Task.call()</code> and receives an answer will need to invoke <code class="language-plaintext highlighter-rouge">Platform.runLater()</code> to update <code class="language-plaintext highlighter-rouge">Task.value</code>.  This implies some kind of delay between the end of <code class="language-plaintext highlighter-rouge">Task.call()</code> and the update of <code class="language-plaintext highlighter-rouge">Task.value()</code>.</li>
</ul>

<p>Here’s the results:</p>

<p><img src="/assets/elements/TaskProgress10.png" alt="Screen Snap 7" /></p>

<p>There’s the problem!</p>

<p>The actual, final size of <code class="language-plaintext highlighter-rouge">valueList</code> after all the FXAT jobs have completed is 50,502.  The size of <code class="language-plaintext highlighter-rouge">valueList</code> at the moment that <code class="language-plaintext highlighter-rouge">task.call()</code> completed was only 50,335.  The value that was captured by <code class="language-plaintext highlighter-rouge">Task.onSucceeded</code> and the value that went into <code class="language-plaintext highlighter-rouge">Task.value</code> was 50,335.</p>

<p>I think you can see that if you weren’t interested in the size, but actually wanted <code class="language-plaintext highlighter-rouge">Task</code> to return the <code class="language-plaintext highlighter-rouge">List</code> itself, you’d be short almost 200 entries.</p>

<h2 id="the-key-element-of-the-problem">The Key Element of the Problem</h2>

<p>The crux of the problem is that <code class="language-plaintext highlighter-rouge">Task</code> is designed such that <code class="language-plaintext highlighter-rouge">Task.call()</code> returns a value and that value is manually used to update <code class="language-plaintext highlighter-rouge">Task.value</code>.  However, <code class="language-plaintext highlighter-rouge">Task.call()</code> 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 <code class="language-plaintext highlighter-rouge">Task.call()</code> completes.</p>

<div class="notice_question--primary">
 <img src="/assets/logos/BRAIN_Question.png" alt="primary" style="float:left;margin-right: 10px" />
 <p style="overflow:auto; float:none">
   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?
 </p>
</div>

<p>It turns out that the key factor is that <code class="language-plaintext highlighter-rouge">Task.call()</code> runs on a background thread.  This means that we have no problems running <code class="language-plaintext highlighter-rouge">Thread.sleep()</code> inside <code class="language-plaintext highlighter-rouge">Task.call()</code>.  All we need is a way to determine if all of the jobs on the FXAT have completed!</p>

<h2 id="pausing-at-the-end-of-taskcall">Pausing at The End of Task.call()</h2>

<p>Before we go there, let’s have a look at how the <code class="language-plaintext highlighter-rouge">ThreadPool</code> is handled:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">threadPool</span> <span class="p">=</span> <span class="nc">Executors</span><span class="p">.</span><span class="nf">newFixedThreadPool</span><span class="p">(</span><span class="mi">50</span><span class="p">)</span>
<span class="nf">repeat</span><span class="p">(</span><span class="mi">50</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">threadPool</span><span class="p">.</span><span class="nf">execute</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
<span class="p">}</span>
<span class="nf">messageUpdater</span><span class="p">(</span><span class="s">"ThreadPool Started"</span><span class="p">)</span>
<span class="n">threadPool</span><span class="p">.</span><span class="nf">shutdown</span><span class="p">()</span>
<span class="kd">var</span> <span class="py">counter</span> <span class="p">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="p">(!</span><span class="n">threadPool</span><span class="p">.</span><span class="nf">awaitTermination</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="nc">TimeUnit</span><span class="p">.</span><span class="nc">SECONDS</span><span class="p">))</span>
    <span class="nf">messageUpdater</span><span class="p">(</span><span class="s">"Still Waiting ${counter++}"</span><span class="p">)</span>
</code></pre></div></div>
<p>That <code class="language-plaintext highlighter-rouge">while{}</code> statement is important.  More correctly, the <code class="language-plaintext highlighter-rouge">ThreadPool.awaitTermination()</code> is very important.  This ensures that our method, running on the main background thread, doesn’t return until after all of the jobs running on all of the threads have completed and the <code class="language-plaintext highlighter-rouge">ThreadPool</code> has shutdown.</p>

<p>So, when we return from <code class="language-plaintext highlighter-rouge">handleProcessing3()</code>, we know that there are no more updates coming to <code class="language-plaintext highlighter-rouge">Task.dataList</code> from any background threads.</p>

<p>This means that the <strong>only</strong> way that <code class="language-plaintext highlighter-rouge">Task.dataList</code> can change after we return from <code class="language-plaintext highlighter-rouge">handleProcessing3()</code> is from a job on the FXAT.  And we know that the only change that such a job makes to <code class="language-plaintext highlighter-rouge">Task.dataList</code> is to clear it out.</p>

<p>Now we know how to determine when the last FXAT job has run, and the updates to <code class="language-plaintext highlighter-rouge">Task.valueList</code> are complete…when <code class="language-plaintext highlighter-rouge">Task.dataList</code> is empty!</p>

<p>And since we are still running on a background thread in <code class="language-plaintext highlighter-rouge">Task.call()</code>, we can wait for it!</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">override</span> <span class="k">fun</span> <span class="nf">call</span><span class="p">():</span> <span class="nc">Int</span> <span class="p">{</span>
    <span class="nf">handleProcessing3</span><span class="p">(</span>
        <span class="p">{</span> <span class="n">done</span><span class="p">,</span> <span class="n">total</span> <span class="p">-&gt;</span> <span class="nf">updateProgress</span><span class="p">(</span><span class="n">done</span><span class="p">,</span> <span class="n">total</span><span class="p">)</span> <span class="p">},</span>
        <span class="p">{</span> <span class="nf">appendValue</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">},</span>
        <span class="p">{</span> <span class="nf">updateMessage</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">})</span>
    <span class="k">while</span> <span class="p">(!</span><span class="n">dataList</span><span class="p">.</span><span class="nf">isEmpty</span><span class="p">())</span> <span class="p">{</span>
        <span class="nf">updateMessage</span><span class="p">(</span><span class="s">"Waiting for completion: ${dataList.size}"</span><span class="p">)</span>
        <span class="nf">println</span><span class="p">(</span><span class="s">"Waiting for completion: ${dataList.size}"</span><span class="p">)</span>
        <span class="nc">Thread</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
    <span class="p">}</span>
    <span class="nc">Thread</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
    <span class="nf">updateMessage</span><span class="p">(</span><span class="s">"All jobs completed"</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">tempSize</span> <span class="p">=</span> <span class="n">valueList</span><span class="p">.</span><span class="n">size</span>
    <span class="nc">Platform</span><span class="p">.</span><span class="nf">runLater</span> <span class="p">{</span> <span class="n">manualSize</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">tempSize</span> <span class="p">}</span>
    <span class="k">return</span> <span class="n">valueList</span><span class="p">.</span><span class="n">size</span>
<span class="p">}</span>
</code></pre></div></div>
<p>I’ve added a little <code class="language-plaintext highlighter-rouge">while{}</code> loop that uses <code class="language-plaintext highlighter-rouge">sleep(10)</code> to wait for the last FXAT job to run.  The first time I ran this I didn’t have the last <code class="language-plaintext highlighter-rouge">sleep(100)</code> after the <code class="language-plaintext highlighter-rouge">while{}</code> loop and it still gave incomplete results.</p>

<p>Remember how we used a temp variable to hold the contents of <code class="language-plaintext highlighter-rouge">dataList</code> so that we could clear out <code class="language-plaintext highlighter-rouge">dataList</code> and release the lock so that the background threads could start loading it up again without waiting for the <code class="language-plaintext highlighter-rouge">Binding.calculateValue()</code> to run?  Well that was causing a time difference between the clearing of <code class="language-plaintext highlighter-rouge">dataList</code> and the population of <code class="language-plaintext highlighter-rouge">valueList</code> on the FXAT, and we need to account for it here.</p>

<p>That last <code class="language-plaintext highlighter-rouge">Thread.sleep(100)</code> gives the FXAT enough time to complete the update of <code class="language-plaintext highlighter-rouge">valueList</code> after <code class="language-plaintext highlighter-rouge">dataList</code> has been cleared out.</p>

<p>This is what it looks like when it’s done:</p>

<p><img src="/assets/elements/TaskProgress11.png" alt="Screen Snap 8" /></p>

<p>As you can see, the last FXAT update takes 65ms to run, so the 100ms sleep is long enough to handle this.</p>

<p>And this is the console output:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; 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
</code></pre></div></div>
<p>You can see that the <code class="language-plaintext highlighter-rouge">while{}</code> loop with the <code class="language-plaintext highlighter-rouge">sleep()</code> ran about 20 times.  And now the counts are all the same, and correct.</p>

<h1 id="conclusion">Conclusion</h1>

<p>I think that the way that <code class="language-plaintext highlighter-rouge">Task</code> is designed to handle frequent updates from background threads is absolutely brilliant.  There a number of things to learn from this:</p>

<h2 id="use-task">Use Task!</h2>

<p>Somehow, people seem to think that using <code class="language-plaintext highlighter-rouge">Task</code> is a heavyweight approach to background processing, and they prefer to just drop a <code class="language-plaintext highlighter-rouge">Runnable</code> into a new <code class="language-plaintext highlighter-rouge">Thread</code> and let it go.</p>

<p>But <code class="language-plaintext highlighter-rouge">Task</code> 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:</p>

<blockquote>
  <p>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.</p>
</blockquote>

<p>So use it.  Use it every time!</p>

<h2 id="look-at-the-javafx-source-code">Look at the JavaFX Source Code</h2>

<p>Sometimes, in the JavaDocs there are hints about interesting things that you simply cannot appreciate until you look “under-the-hood” and see how it is done.  Check out the JavaDocs for <code class="language-plaintext highlighter-rouge">Task.updateMessage()</code>:</p>

<blockquote>
  <p>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.</p>
</blockquote>

<p>I’m not sure that “coalesced” is the right word here, since it doesn’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.</p>

<p>Looking at the source code for things like this shows you the <em>proper</em> way to handle complex issues with JavaFX.  Don’t be scared to look, and most IDE’s will take you right to the internal source code from the method call in your application code with a single click.</p>

<h2 id="only-use-platformrunlater-thoughtfully">Only Use <code class="language-plaintext highlighter-rouge">Platform.runLater()</code> Thoughtfully</h2>

<p>I constantly see people online recommending, “Wrap it in <code class="language-plaintext highlighter-rouge">Platform.runLater()</code>”.  I see this in tutorial articles as well as discussions and on StackOverflow.</p>

<p>I consider this to be an anti-pattern, if not an out-and-out “code smell”.</p>

<p>Sure, in the solution presented here we did use <code class="language-plaintext highlighter-rouge">Platform.runLater()</code>, but in a considered and deliberate manner.  And also, it was wrapped inside a utility class and <strong>not</strong> integrated with any of the application/business logic at all.</p>

<p>You may have noticed that code in <code class="language-plaintext highlighter-rouge">handleProcessing()</code> never changed throughout all of the examples in this article.  That’s because it’s logic was decoupled from the logic in the <code class="language-plaintext highlighter-rouge">Task</code>.  We updated <code class="language-plaintext highlighter-rouge">Task.call()</code> and <code class="language-plaintext highlighter-rouge">Task.appendValue()</code>, but never had to touch the business logic.</p>

<p>At the other end, the GUI was connected to the <code class="language-plaintext highlighter-rouge">Task</code> through that <code class="language-plaintext highlighter-rouge">ListToString Binding</code>.  That <code class="language-plaintext highlighter-rouge">Binding</code> is owned by the layout, as are all other elements related to how the layout utilizes the results, and the partial results, from the <code class="language-plaintext highlighter-rouge">Task</code>.  If it turned out that the <code class="language-plaintext highlighter-rouge">ListToString</code> was causing performance issues with the GUI, then that’s the layout’s problem to solve.</p>

<p>Honestly, converting the <code class="language-plaintext highlighter-rouge">ObservableList</code> to a <code class="language-plaintext highlighter-rouge">String</code> to display it in a <code class="language-plaintext highlighter-rouge">TextArea</code> is a pretty bad design decision.  It would be much better to use a <code class="language-plaintext highlighter-rouge">ListView</code> which is designed to be used in this manner.  You could even change the output from <code class="language-plaintext highlighter-rouge">String</code> to some structured object, and then have better data available to format the display of the information.</p>

<h2 id="beware-the-javadocs">Beware the JavaDocs</h2>

<p>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.</p>

<p>What is also interesting is that the JavaDocs for <code class="language-plaintext highlighter-rouge">Platform.runLater()</code> contain this warning:</p>

<blockquote>
  <p>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.</p>
</blockquote>

<p>The approach suggested by the JavaDocs for <code class="language-plaintext highlighter-rouge">Task</code> totally disregard this warning.</p>

<p>I wish I could say this was a unique occurance, but lots of JavaDoc entries have dubious advice in their introductory sections.</p>

<h2 id="multi-threading-is-always-complicated">Multi-threading is Always Complicated</h2>

<p>In this application we had three kinds of threads:</p>

<ol>
  <li>The FX Application Thread (FXAT)</li>
  <li>The main background thread to manage the processing</li>
  <li>50 <code class="language-plaintext highlighter-rouge">ExecutorPool</code> threads that did the actual work</li>
</ol>

<p>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 <code class="language-plaintext highlighter-rouge">Task.call()</code> logic.  It was extremely important that this thread not run on to completion before all of the <code class="language-plaintext highlighter-rouge">ExecutorPool</code> threads had finished.</p>

<p>These three thread types all shared access to one data element, <code class="language-plaintext highlighter-rouge">Task.dataList</code>.  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.</p>

<h2 id="this-solution-is-still-a-bit-kludgey">This Solution is Still a Bit Kludgey</h2>

<p>One thing that I do not like about this approach is that the intermediate results, while the <code class="language-plaintext highlighter-rouge">Task</code> is running, are accessed from the <code class="language-plaintext highlighter-rouge">Task</code> differently from how the final result is accessed.</p>

<p>To be clear, you can still always access the final result from <code class="language-plaintext highlighter-rouge">Task.valueList</code>, but the rest of the <code class="language-plaintext highlighter-rouge">Task</code> infrastructure is designed around the result being in <code class="language-plaintext highlighter-rouge">Task.value</code>.  For instance, <code class="language-plaintext highlighter-rouge">Task.get()</code> delegates to <code class="language-plaintext highlighter-rouge">Task.value.get()</code>.</p>

<p>But, unfortunately, there’s no clean way to get the partial results into <code class="language-plaintext highlighter-rouge">Task.value</code> because it’s private to <code class="language-plaintext highlighter-rouge">Task</code> and can only be updated through <code class="language-plaintext highlighter-rouge">Task.updateValue()</code>.  Yes, you could copy the entire <code class="language-plaintext highlighter-rouge">ObservableList</code> in <code class="language-plaintext highlighter-rouge">Task.valueList</code> over, but that doesn’t seem to meet the definition of “clean” to me.
There doesn’t seem to be any way to create a replacement for <code class="language-plaintext highlighter-rouge">Task</code> that accumulates rather than replaces, and still works the same way as <code class="language-plaintext highlighter-rouge">Task</code>.</p>

<p>You cannot achieve this by extending <code class="language-plaintext highlighter-rouge">Task</code> because key elements are <code class="language-plaintext highlighter-rouge">private</code>, and key methods are <code class="language-plaintext highlighter-rouge">final</code>.  You cannot simply use <code class="language-plaintext highlighter-rouge">Task</code> as a basis to create a brand new class that implements <code class="language-plaintext highlighter-rouge">FutureTask</code> and <code class="language-plaintext highlighter-rouge">Worker</code> because <code class="language-plaintext highlighter-rouge">Task</code> uses methods from JavaFX packages that aren’t exported to outside modules.  This makes it very difficult to implement the <code class="language-plaintext highlighter-rouge">Event</code> firing mechanism in <code class="language-plaintext highlighter-rouge">Task</code>.</p>

<p>Maybe there is a better way to craft this into a generic solution that can be added to a library, but I can’t find it.</p>]]></content><author><name>Dave Barrett</name><email>pragmaticcoding8@gmail.com</email></author><category term="javafx" /><summary type="html"><![CDATA[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.]]></summary></entry><entry><title type="html">Type Specific TableColumns</title><link href="https://www.pragmaticcoding.ca/javafx/elements/custom-table-columns" rel="alternate" type="text/html" title="Type Specific TableColumns" /><published>2025-06-10T17:00:00+00:00</published><updated>2025-06-10T17:00:00+00:00</updated><id>https://www.pragmaticcoding.ca/javafx/elements/custom-table-columns</id><content type="html" xml:base="https://www.pragmaticcoding.ca/javafx/elements/custom-table-columns"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>If you rigorously follow DRY (Don’t Repeat Yourself), and you should, then you’ll eventually have to think about “Big Picture DRY”.  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.</p>

<p>If you are one of those programmers that likes to use <code class="language-plaintext highlighter-rouge">TableView</code> - and there are lots of you out there - then one of the ways you might be repeating yourself without thinking about it is your <code class="language-plaintext highlighter-rouge">TableColumns</code>.  Or, and this is even more likely, you’re avoiding properly configuring some kinds of <code class="language-plaintext highlighter-rouge">TableColumns</code> because it’s just too much tedious work to do every time.</p>

<p>In this article, we are going to look at how to create custom <code class="language-plaintext highlighter-rouge">TableColumns</code> 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 <code class="language-plaintext highlighter-rouge">TableViewCells</code> work hand-in-glove with custom <code class="language-plaintext highlighter-rouge">TableColumns</code> and how you can create them to work together seemlessly.</p>

<div class="notice--kotlin">
 <img src="/assets/logos/Kotlin.png" alt="Kotlin" style="float:left;margin-right: 10px;margin-top: 8px;" />
 <p style="overflow:auto; float:none">
   While code is this article is written in Kotlin, all of the JavaFX concepts are exactly the same.
   Most of the Kotlin should be intuitively obvious to Java programmers,
   but if you need help understanding it, refer to this <a href="/kotlin/kotlin-examples" title="Read the article" target="_blank">page</a>.
 </p>
</div>

<h2 id="different-data-types-should-be-presented-differently">Different Data Types Should be Presented Differently</h2>

<p><code class="language-plaintext highlighter-rouge">TableColumns</code> are easy to set up when the data in the column is just a simple <code class="language-plaintext highlighter-rouge">String</code>.  But what about numbers?  What about dates and money?</p>

<p>Once you get into these questions, then you’re suddenly looking at custom <code class="language-plaintext highlighter-rouge">TableCells</code> to handle the presentation.  The difference between adding a <code class="language-plaintext highlighter-rouge">TableColumn</code> with nothing more than a <code class="language-plaintext highlighter-rouge">CellValueFactory</code> and doing it right can seem huge.  But it doesn’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.</p>

<p>Let’s start by looking at a <code class="language-plaintext highlighter-rouge">TableView</code> configured with the bare minimum:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Example1</span> <span class="p">:</span> <span class="nc">Application</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">stage</span><span class="p">.</span><span class="n">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="nf">createContent</span><span class="p">(),</span> <span class="mf">400.0</span><span class="p">,</span> <span class="mf">350.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nc">Example1</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"example.css"</span><span class="p">)</span><span class="o">?.</span><span class="nf">toString</span><span class="p">()</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="n">stylesheets</span> <span class="p">+=</span> <span class="n">it</span> <span class="p">}</span>
        <span class="p">}</span>
        <span class="n">stage</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">createContent</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">BorderPane</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">center</span> <span class="p">=</span> <span class="nc">TableView</span><span class="p">&lt;</span><span class="nc">TableData1</span><span class="p">&gt;().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">columns</span> <span class="p">+=</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">TableData1</span><span class="p">,</span> <span class="nc">String</span><span class="p">&gt;(</span><span class="s">"Column A"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="nf">setCellValueFactory</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value1</span> <span class="p">}</span>
                <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"a-column"</span>
            <span class="p">}</span>
            <span class="n">columns</span> <span class="p">+=</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">TableData1</span><span class="p">,</span> <span class="nc">Double</span><span class="p">&gt;(</span><span class="s">"Temperature"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="nf">setCellValueFactory</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value4</span><span class="p">.</span><span class="nf">asObject</span><span class="p">()</span> <span class="p">}</span>
            <span class="p">}</span>
            <span class="n">columns</span> <span class="p">+=</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">TableData1</span><span class="p">,</span> <span class="nc">String</span><span class="p">&gt;(</span><span class="s">"Column C"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="nf">setCellValueFactory</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value2</span> <span class="p">}</span>
                <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"c-column"</span>
            <span class="p">}</span>
            <span class="k">for</span> <span class="p">(</span><span class="n">x</span> <span class="k">in</span> <span class="mi">1</span><span class="o">..</span><span class="mi">30</span><span class="p">)</span> <span class="n">items</span> <span class="p">+=</span> <span class="nc">TableData1</span><span class="p">(</span>
                <span class="s">"A"</span><span class="p">,</span>
                <span class="s">"B"</span><span class="p">,</span>
                <span class="s">"C"</span><span class="p">,</span>
                <span class="nc">Random</span><span class="p">.</span><span class="nf">nextDouble</span><span class="p">(</span><span class="mf">45.0</span><span class="p">)</span>
            <span class="p">)</span>
        <span class="p">}</span>
        <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">10.0</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Which looks like this:</p>

<p><img src="/assets/elements/TableColumn0.png" alt="Very Ugly Table" /></p>

<p>I 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 <code class="language-plaintext highlighter-rouge">ObservableValue.map{}</code>:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">columns</span> <span class="p">+=</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">TableData1</span><span class="p">,</span> <span class="nc">Double</span><span class="p">&gt;(</span><span class="s">"Temperature"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
    <span class="nf">setCellValueFactory</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value4</span><span class="p">.</span><span class="nf">asObject</span><span class="p">().</span><span class="nf">map</span> <span class="p">{</span> <span class="n">number</span> <span class="p">-&gt;</span> <span class="nc">Math</span><span class="p">.</span><span class="nf">round</span><span class="p">(</span><span class="n">number</span> <span class="p">*</span> <span class="mf">10.0</span><span class="p">)</span> <span class="p">/</span> <span class="mf">10.0</span> <span class="p">}</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><img src="/assets/elements/TableColumn1.png" alt="Less Ugly Table" /></p>

<p>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’s fix that by adding a style class selector <code class="language-plaintext highlighter-rouge">temperature-column</code> to the <code class="language-plaintext highlighter-rouge">TableColumn</code> and then fixing the alignment in the style sheet:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.table-row-cell</span> <span class="nc">.temperature-column</span>  <span class="p">{</span>
  <span class="py">-fx-alignment</span><span class="p">:</span> <span class="nb">center-right</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p><img src="/assets/elements/TableColumn2.png" alt="Even Less Ugly Table" /></p>

<p>One last problem is that the column is way too wide for the data because the heading, “Temperature” is a long word.  “Temp” would work but it’s also commonly used for “temporary” (at least to programmers) and doesn’t seem right.  Let’s just use a graphic instead:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">columns</span> <span class="p">+=</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">TableData1</span><span class="p">,</span> <span class="nc">String</span><span class="p">&gt;(</span><span class="s">""</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
    <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"temperature-column"</span>
    <span class="nf">setCellValueFactory</span> <span class="p">{</span>
        <span class="n">it</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value4</span><span class="p">.</span><span class="nf">asObject</span><span class="p">().</span><span class="nf">map</span> <span class="p">{</span> <span class="n">number</span> <span class="p">-&gt;</span> <span class="nc">Math</span><span class="p">.</span><span class="nf">round</span><span class="p">(</span><span class="n">number</span> <span class="p">*</span> <span class="mf">10.0</span><span class="p">)</span> <span class="p">/</span> <span class="mf">10.0</span> <span class="p">}</span>
            <span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">number</span> <span class="p">-&gt;</span> <span class="s">"%6.1f"</span><span class="p">.</span><span class="nf">format</span><span class="p">(</span><span class="n">number</span><span class="p">)</span> <span class="p">}</span>
    <span class="p">}</span>
    <span class="nc">Example1</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"thermometer24.png"</span><span class="p">)</span><span class="o">?.</span><span class="nf">toString</span><span class="p">()</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="n">graphic</span> <span class="p">=</span> <span class="nc">ImageView</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Which looks like this:</p>

<p><img src="/assets/elements/TableColumn3.png" alt="Table with Graphic Column Header" /></p>

<p>At this point, I’m not pretending that this is a great UI design.  This article is about programming techniques, so we’re looking for interesting things to do here.</p>

<h1 id="creating-a-custom-column">Creating a Custom Column</h1>

<p>For me, this small amount of code would be enough to trigger DRY the next time I created a “Temperature” column.  We can do it by creating a builder function or by creating a custom class extending <code class="language-plaintext highlighter-rouge">TableColumn</code>.  If we were going to stop here, then a builder would be the best option.  But we’re not going to stop here, so we’ll create a custom class, which will look like this to start:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">TemperatureColumn</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;(</span><span class="n">extractor</span><span class="p">:</span> <span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="nc">T</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">DoubleProperty</span><span class="p">)</span> <span class="p">:</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">,</span> <span class="nc">Double</span><span class="p">&gt;()</span> <span class="p">{</span>
    <span class="nf">init</span> <span class="p">{</span>
        <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"temperature-column"</span>
        <span class="nf">setCellValueFactory</span> <span class="p">{</span>
            <span class="n">extractor</span><span class="p">.</span><span class="nf">invoke</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">value</span><span class="p">).</span><span class="nf">asObject</span><span class="p">().</span><span class="nf">map</span> <span class="p">{</span> <span class="n">number</span> <span class="p">-&gt;</span> <span class="nc">Math</span><span class="p">.</span><span class="nf">round</span><span class="p">(</span><span class="n">number</span> <span class="p">*</span> <span class="mf">10.0</span><span class="p">)</span> <span class="p">/</span> <span class="mf">10.0</span> <span class="p">}</span>
        <span class="p">}</span>
        <span class="nc">TemperatureColumn</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"thermometer24.png"</span><span class="p">)</span><span class="o">?.</span><span class="nf">toString</span><span class="p">()</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="n">graphic</span> <span class="p">=</span> <span class="nc">ImageView</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>All of the <code class="language-plaintext highlighter-rouge">apply{}</code> code from before has been moved into <code class="language-plaintext highlighter-rouge">init{}</code> in our custom class.  We don’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.</p>

<p>In the <code class="language-plaintext highlighter-rouge">TableView</code> setup code we invoke our <code class="language-plaintext highlighter-rouge">TemperatureColumn</code> like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">columns</span> <span class="p">+=</span> <span class="nc">TemperatureColumn</span><span class="p">&lt;</span><span class="nc">TableData1</span><span class="p">&gt;</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value4</span> <span class="p">}</span>
</code></pre></div></div>
<p>Once we’ve done this, our attitude to how we approach this new <code class="language-plaintext highlighter-rouge">TableColumn</code> changes in two ways:</p>

<ul>
  <li>It’s now worth doing things that we wouldn’t have put in the effort for with a “one-off” implementation.</li>
  <li>It’s now worth doing things really, really technically correct and configurable.</li>
</ul>

<p>We’ll see this in the first enhancement that we make…</p>

<h2 id="styling-a-new-colour-for-temperatures-below-freezing">Styling a New Colour for Temperatures Below Freezing</h2>

<p>Let’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.</p>

<p>Let’s start with the style sheet:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.table-row-cell</span> <span class="nc">.temperature-column</span><span class="nd">:freezing</span>  <span class="p">{</span>
  <span class="py">-fx-text-fill</span><span class="p">:</span> <span class="no">cornflowerblue</span><span class="p">;</span>
  <span class="py">-fx-font-weight</span><span class="p">:</span> <span class="nb">bold</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>To do this, we’ll need to create the <code class="language-plaintext highlighter-rouge">PseudoClass</code> which will mean creating a custom <code class="language-plaintext highlighter-rouge">TableCell</code>:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">TemperatureTableCell</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;</span> <span class="p">:</span> <span class="nc">TableCell</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">,</span> <span class="nc">Double</span><span class="p">&gt;()</span> <span class="p">{</span>
    <span class="k">companion</span> <span class="k">object</span> <span class="nc">PseudoClasses</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">freezingPseudoClass</span><span class="p">:</span> <span class="nc">PseudoClass</span> <span class="p">=</span> <span class="nc">PseudoClass</span><span class="p">.</span><span class="nf">getPseudoClass</span><span class="p">(</span><span class="s">"freezing"</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="nf">itemProperty</span><span class="p">().</span><span class="nf">subscribe</span> <span class="p">{</span> <span class="n">newValue</span> <span class="p">-&gt;</span>
            <span class="nf">pseudoClassStateChanged</span><span class="p">(</span><span class="n">freezingPseudoClass</span><span class="p">,</span> <span class="p">(</span><span class="n">newValue</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="p">(</span><span class="n">it</span> <span class="p">&lt;=</span> <span class="mf">0.0</span><span class="p">)</span> <span class="p">}</span> <span class="o">?:</span> <span class="k">false</span><span class="p">))</span>
        <span class="p">}</span>
        <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="nf">itemProperty</span><span class="p">().</span><span class="nf">asString</span><span class="p">())</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Which looks like this:</p>

<p><img src="/assets/elements/TableColumn4.png" alt="Column With Blue Freezing Temperatures" /></p>

<p>Clearly, it is possible to just code in the colour change for freezing temperatures as part of the <code class="language-plaintext highlighter-rouge">TemperatureTableCell</code>, and that approach might be more expeditious.  But when you are building a custom <code class="language-plaintext highlighter-rouge">TableColumn</code> for re-use, you want to get it as close to “right” 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.</p>

<p>You may have guessed, given the formula, that this column is intended to display °C.  What if you need °F?</p>

<h2 id="styling-the-units">Styling the Units</h2>

<p>The first thing we need to do is to move the rounding feature out of the <code class="language-plaintext highlighter-rouge">TemperatureColumn</code> itself, and into the <code class="language-plaintext highlighter-rouge">TemperatureTableCell</code> because we don’t want to be doing any conversions on previously rounded numbers.  Then we can introduce an <code class="language-plaintext highlighter-rouge">Enum</code> called <code class="language-plaintext highlighter-rouge">TemperatureUnit</code> and an <code class="language-plaintext highlighter-rouge">ObjectBinding&lt;Double&gt;</code> to convert between Celsius input and Fahrenheit, Celsius and Kelvin output.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">TemperatureColumn</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;(</span><span class="n">extractor</span><span class="p">:</span> <span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="nc">T</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">DoubleProperty</span><span class="p">)</span> <span class="p">:</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">,</span> <span class="nc">Double</span><span class="p">&gt;()</span> <span class="p">{</span>

    <span class="kd">val</span> <span class="py">outputUnits</span><span class="p">:</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">TemperatureUnit</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">SimpleObjectProperty</span><span class="p">(</span><span class="nc">TemperatureUnit</span><span class="p">.</span><span class="nc">C</span><span class="p">)</span>

    <span class="k">infix</span> <span class="k">fun</span> <span class="nf">outputAs</span><span class="p">(</span><span class="n">newOutput</span><span class="p">:</span> <span class="nc">TemperatureUnit</span><span class="p">)</span> <span class="p">=</span> <span class="k">this</span><span class="p">.</span><span class="nf">apply</span> <span class="p">{</span> <span class="n">outputUnits</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">newOutput</span> <span class="p">}</span>

    <span class="k">companion</span> <span class="k">object</span> <span class="nc">PseudoClasses</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">freezingPseudoClass</span><span class="p">:</span> <span class="nc">PseudoClass</span> <span class="p">=</span> <span class="nc">PseudoClass</span><span class="p">.</span><span class="nf">getPseudoClass</span><span class="p">(</span><span class="s">"freezing"</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"temperature-column"</span>
        <span class="nf">setCellValueFactory</span> <span class="p">{</span>
            <span class="n">extractor</span><span class="p">.</span><span class="nf">invoke</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">value</span><span class="p">).</span><span class="nf">asObject</span><span class="p">()</span>
        <span class="p">}</span>
        <span class="nc">TemperatureColumn</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"thermometer24.png"</span><span class="p">)</span><span class="o">?.</span><span class="nf">toString</span><span class="p">()</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="n">graphic</span> <span class="p">=</span> <span class="nc">ImageView</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">}</span>
        <span class="nf">setCellFactory</span> <span class="p">{</span> <span class="nc">TemperatureTableCell</span><span class="p">()</span> <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">enum</span> <span class="kd">class</span> <span class="nc">TemperatureUnit</span> <span class="p">{</span> <span class="nc">C</span><span class="p">,</span> <span class="nc">F</span><span class="p">,</span> <span class="nc">K</span> <span class="p">}</span>

    <span class="k">inner</span> <span class="kd">class</span> <span class="nc">TemperatureTableCell</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;</span> <span class="p">:</span> <span class="nc">TableCell</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">,</span> <span class="nc">Double</span><span class="p">&gt;()</span> <span class="p">{</span>
        <span class="nf">init</span> <span class="p">{</span>
            <span class="nf">itemProperty</span><span class="p">().</span><span class="nf">subscribe</span> <span class="p">{</span> <span class="n">newValue</span> <span class="p">-&gt;</span>
                <span class="nf">pseudoClassStateChanged</span><span class="p">(</span><span class="n">freezingPseudoClass</span><span class="p">,</span> <span class="p">(</span><span class="n">newValue</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="p">(</span><span class="n">it</span> <span class="p">&lt;=</span> <span class="mf">0.0</span><span class="p">)</span> <span class="p">}</span> <span class="o">?:</span> <span class="k">false</span><span class="p">))</span>
            <span class="p">}</span>
            <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span>
                <span class="nc">ConversionBinding</span><span class="p">(</span><span class="nf">itemProperty</span><span class="p">(),</span> <span class="n">outputUnits</span><span class="p">)</span>
                    <span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">rawValue</span> <span class="p">-&gt;</span> <span class="n">rawValue</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="nc">Math</span><span class="p">.</span><span class="nf">round</span><span class="p">(</span><span class="n">rawValue</span> <span class="p">*</span> <span class="mf">10.0</span><span class="p">)</span> <span class="p">/</span> <span class="mf">10.0</span> <span class="p">}</span><span class="o">?.</span><span class="nf">toString</span><span class="p">()</span> <span class="p">})</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">class</span> <span class="nc">ConversionBinding</span><span class="p">(</span>
        <span class="kd">val</span> <span class="py">celciusValue</span><span class="p">:</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">Double</span><span class="p">&gt;,</span>
        <span class="kd">val</span> <span class="py">outputUnits</span><span class="p">:</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">TemperatureUnit</span><span class="p">&gt;</span>
    <span class="p">)</span> <span class="p">:</span> <span class="nc">ObjectBinding</span><span class="p">&lt;</span><span class="nc">Double</span><span class="p">?&gt;()</span> <span class="p">{</span>
        <span class="nf">init</span> <span class="p">{</span>
            <span class="k">super</span><span class="p">.</span><span class="nf">bind</span><span class="p">(</span><span class="n">celciusValue</span><span class="p">,</span> <span class="n">outputUnits</span><span class="p">)</span>
        <span class="p">}</span>

        <span class="k">override</span> <span class="k">fun</span> <span class="nf">computeValue</span><span class="p">():</span> <span class="nc">Double</span><span class="p">?</span> <span class="p">=</span> <span class="k">when</span> <span class="p">(</span><span class="n">outputUnits</span><span class="p">.</span><span class="n">value</span><span class="p">)</span> <span class="p">{</span>
            <span class="nc">TemperatureUnit</span><span class="p">.</span><span class="nc">K</span> <span class="p">-&gt;</span> <span class="n">celciusValue</span><span class="p">.</span><span class="n">value</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="n">it</span> <span class="p">+</span> <span class="mf">270.0</span> <span class="p">}</span>
            <span class="nc">TemperatureUnit</span><span class="p">.</span><span class="nc">F</span> <span class="p">-&gt;</span> <span class="n">celciusValue</span><span class="p">.</span><span class="n">value</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="p">(</span><span class="n">it</span> <span class="p">*</span> <span class="mi">9</span> <span class="p">/</span> <span class="mi">5</span><span class="p">)</span> <span class="p">+</span> <span class="mi">32</span> <span class="p">}</span>
            <span class="k">else</span> <span class="p">-&gt;</span> <span class="n">celciusValue</span><span class="p">.</span><span class="n">value</span>
        <span class="p">}</span>
    <span class="p">}</span>

<span class="p">}</span>
</code></pre></div></div>
<p>First off, everything has been moved inside the <code class="language-plaintext highlighter-rouge">TemperatureColumn</code> class, and <code class="language-plaintext highlighter-rouge">TemperatureTableCell</code> has been labled an “inner” class so that it can access fields from the outer <code class="language-plaintext highlighter-rouge">TemperatureColumn</code> class.  There is a <code class="language-plaintext highlighter-rouge">ConversionBinding</code> that is dependent on <code class="language-plaintext highlighter-rouge">Cell's item</code> and <code class="language-plaintext highlighter-rouge">TableColumn's outputUnits Properties</code> and which handles the conversion from Celsius to all three output formats.  The <code class="language-plaintext highlighter-rouge">text Property</code> of <code class="language-plaintext highlighter-rouge">TemperatureTableCell</code> is now bound to the <code class="language-plaintext highlighter-rouge">item Property</code> through this custom <code class="language-plaintext highlighter-rouge">Binding</code> and then further processed to limit it to one decimal place.</p>

<p>The <code class="language-plaintext highlighter-rouge">outputAs()</code> method of <code class="language-plaintext highlighter-rouge">TemperatureColumn</code> has been written as an <code class="language-plaintext highlighter-rouge">infix</code> decorator to make it easier to configure from the layout code.</p>

<p>In the layout, I’ve added a second <code class="language-plaintext highlighter-rouge">TemperatureColumn</code> so that we can have one in Celcius and one in Farenheit:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">columns</span> <span class="p">+=</span> <span class="nc">TemperatureColumn</span><span class="p">&lt;</span><span class="nc">TableData1</span><span class="p">&gt;</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value4</span> <span class="p">}</span> <span class="n">outputAs</span> <span class="nc">TemperatureColumn</span><span class="p">.</span><span class="nc">TemperatureUnit</span><span class="p">.</span><span class="nc">F</span>
<span class="n">columns</span> <span class="p">+=</span> <span class="nc">TemperatureColumn</span><span class="p">&lt;</span><span class="nc">TableData1</span><span class="p">&gt;</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value4</span> <span class="p">}</span>
</code></pre></div></div>
<p>And it looks like this:</p>

<p><img src="/assets/elements/TableColumn5.png" alt="Table with Farenheit and Celcius Colums" /></p>

<p>You can see here that the <code class="language-plaintext highlighter-rouge">freezing</code> Pseudo-class is still calculated properly as it is done on the Celsius value.  The conversions happens as <code class="language-plaintext highlighter-rouge">textProperty()</code> is updated, so the internal value of <code class="language-plaintext highlighter-rouge">item</code> always remains in Celsius.</p>

<h3 id="styling-the-units-via-css">Styling the Units Via CSS</h3>

<p>Ideally, all the aspects of how the output is displayed should be configurable via Style Sheets.  Let’s look at how to set up the <code class="language-plaintext highlighter-rouge">StyleableProperties</code> in our <code class="language-plaintext highlighter-rouge">TableColumn</code> so that we can configure the output units via CSS:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">TemperatureColumn</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;(</span><span class="n">extractor</span><span class="p">:</span> <span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="nc">T</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">DoubleProperty</span><span class="p">)</span> <span class="p">:</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">,</span> <span class="nc">Double</span><span class="p">&gt;()</span> <span class="p">{</span>

    <span class="k">private</span> <span class="kd">val</span> <span class="py">outputUnits</span><span class="p">:</span> <span class="nc">StyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">TemperatureUnit</span><span class="p">&gt;</span> <span class="p">=</span>
        <span class="nc">SimpleStyleableObjectProperty</span><span class="p">(</span><span class="nc">OUTPUT_UNITS_META_DATA</span><span class="p">,</span> <span class="nc">TemperatureUnit</span><span class="p">.</span><span class="nc">C</span><span class="p">)</span>

    <span class="k">infix</span> <span class="k">fun</span> <span class="nf">outputAs</span><span class="p">(</span><span class="n">newOutput</span><span class="p">:</span> <span class="nc">TemperatureUnit</span><span class="p">)</span> <span class="p">=</span> <span class="k">this</span><span class="p">.</span><span class="nf">apply</span> <span class="p">{</span> <span class="n">outputUnits</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">newOutput</span> <span class="p">}</span>

    <span class="k">companion</span> <span class="k">object</span> <span class="nc">StylingStuff</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">freezingPseudoClass</span><span class="p">:</span> <span class="nc">PseudoClass</span> <span class="p">=</span> <span class="nc">PseudoClass</span><span class="p">.</span><span class="nf">getPseudoClass</span><span class="p">(</span><span class="s">"freezing"</span><span class="p">)</span>
        <span class="kd">val</span> <span class="py">OUTPUT_UNITS_META_DATA</span><span class="p">:</span> <span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">TemperatureTableCell</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;,</span> <span class="nc">TemperatureUnit</span><span class="p">&gt;</span> <span class="p">=</span> <span class="kd">object</span> <span class="err">:
            </span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">TemperatureTableCell</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;,</span> <span class="nc">TemperatureUnit</span><span class="p">&gt;(</span>
                <span class="s">"-wfx-temperature-unit"</span><span class="p">,</span>
                <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getEnumConverter</span><span class="p">(</span><span class="nc">TemperatureUnit</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
            <span class="p">)</span> <span class="p">{</span>
            <span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">styleable</span><span class="p">:</span> <span class="nc">TemperatureTableCell</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;)</span> <span class="p">=</span>
                <span class="p">!(</span><span class="n">styleable</span><span class="p">.</span><span class="n">outputUnits</span><span class="p">.</span><span class="n">isBound</span><span class="p">)</span>

            <span class="k">override</span> <span class="k">fun</span> <span class="nf">getStyleableProperty</span><span class="p">(</span><span class="n">styleable</span><span class="p">:</span> <span class="nc">TemperatureTableCell</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;)</span> <span class="p">=</span> <span class="n">styleable</span><span class="p">.</span><span class="n">outputUnits</span>
        <span class="p">}</span>

        <span class="k">private</span> <span class="kd">val</span> <span class="py">cssMetaDataList</span> <span class="p">=</span>
            <span class="p">(</span><span class="nc">TableColumn</span><span class="p">.</span><span class="nf">getClassCssMetaData</span><span class="p">()</span> <span class="p">+</span> <span class="nc">OUTPUT_UNITS_META_DATA</span><span class="p">)</span> <span class="k">as</span> <span class="nc">MutableList</span>

        <span class="k">fun</span> <span class="nf">getClassCssMetaData</span><span class="p">()</span> <span class="p">=</span> <span class="n">cssMetaDataList</span>
    <span class="p">}</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"temperature-column"</span>
        <span class="nf">setCellValueFactory</span> <span class="p">{</span>
            <span class="n">extractor</span><span class="p">.</span><span class="nf">invoke</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">value</span><span class="p">).</span><span class="nf">asObject</span><span class="p">()</span>
        <span class="p">}</span>
        <span class="nc">TemperatureColumn</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"thermometer24.png"</span><span class="p">)</span><span class="o">?.</span><span class="nf">toString</span><span class="p">()</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="n">graphic</span> <span class="p">=</span> <span class="nc">ImageView</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">}</span>
        <span class="nf">setCellFactory</span> <span class="p">{</span> <span class="nc">TemperatureTableCell</span><span class="p">(</span><span class="n">outputUnits</span><span class="p">)</span> <span class="p">}</span>
    <span class="p">}</span>

<span class="p">}</span>

<span class="kd">class</span> <span class="nc">TemperatureTableCell</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;(</span><span class="kd">val</span> <span class="py">outputUnits</span><span class="p">:</span> <span class="nc">StyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">TemperatureUnit</span><span class="p">&gt;)</span> <span class="p">:</span>
    <span class="nc">TableCell</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">,</span> <span class="nc">Double</span><span class="p">&gt;()</span> <span class="p">{</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">getControlCssMetaData</span><span class="p">()</span> <span class="p">=</span>
        <span class="p">(</span><span class="k">super</span><span class="p">.</span><span class="nf">getControlCssMetaData</span><span class="p">()</span> <span class="p">+</span> <span class="nc">TemperatureColumn</span><span class="p">.</span><span class="nf">getClassCssMetaData</span><span class="p">())</span> <span class="k">as</span> <span class="nc">MutableList</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="nf">itemProperty</span><span class="p">().</span><span class="nf">subscribe</span> <span class="p">{</span> <span class="n">newValue</span> <span class="p">-&gt;</span>
            <span class="nf">pseudoClassStateChanged</span><span class="p">(</span><span class="n">freezingPseudoClass</span><span class="p">,</span> <span class="p">(</span><span class="n">newValue</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="p">(</span><span class="n">it</span> <span class="p">&lt;=</span> <span class="mf">0.0</span><span class="p">)</span> <span class="p">}</span> <span class="o">?:</span> <span class="k">false</span><span class="p">))</span>
        <span class="p">}</span>
        <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span>
            <span class="nc">ConversionBinding</span><span class="p">(</span><span class="nf">itemProperty</span><span class="p">(),</span> <span class="n">outputUnits</span><span class="p">)</span>
                <span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">rawValue</span> <span class="p">-&gt;</span> <span class="n">rawValue</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="nc">Math</span><span class="p">.</span><span class="nf">round</span><span class="p">(</span><span class="n">rawValue</span> <span class="p">*</span> <span class="mf">10.0</span><span class="p">)</span> <span class="p">/</span> <span class="mf">10.0</span> <span class="p">}</span><span class="o">?.</span><span class="nf">toString</span><span class="p">()</span> <span class="p">})</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now, this is a little bit complicated if you are new to creating custom <code class="language-plaintext highlighter-rouge">StyleableProperties</code>.  If you want to learn all about this you can read my article about it <a href="/javafx/elements/styleable-properties">here</a>.</p>

<p>We’ve turned <code class="language-plaintext highlighter-rouge">outputUnits</code> into a <code class="language-plaintext highlighter-rouge">StyleableObjectProperty</code> which will enable us to connect it to a style sheet.  This is done via something called <code class="language-plaintext highlighter-rouge">CssMetaData</code>, which really should be defined statically.  The <code class="language-plaintext highlighter-rouge">CssMetaData</code> is in the <code class="language-plaintext highlighter-rouge">companion object</code> - which is pretty much the Kotlin version of <code class="language-plaintext highlighter-rouge">static</code> - and is called <code class="language-plaintext highlighter-rouge">OUTPUT_UNITS_META_DATA</code>.  This is how you create an instance of a static <code class="language-plaintext highlighter-rouge">anonymous inner class</code> 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 <code class="language-plaintext highlighter-rouge">TableCell</code> and how to determine if it is settable by the style sheet.</p>

<p>Since it’s static, the <code class="language-plaintext highlighter-rouge">CssMetaData</code> doesn’t need to be defined in the class that uses it.  In this case, we are going to be using the same <code class="language-plaintext highlighter-rouge">Property</code> for each <code class="language-plaintext highlighter-rouge">TableCell</code> in the <code class="language-plaintext highlighter-rouge">TableColumn</code>, so we can define it in the custom <code class="language-plaintext highlighter-rouge">TableColumn</code> and then include it in the <code class="language-plaintext highlighter-rouge">CssMetaDataList</code> for the <code class="language-plaintext highlighter-rouge">TemperatureTableCell</code>.</p>

<p>One more twist on this is that <code class="language-plaintext highlighter-rouge">getCssMetaData()</code> is final in <code class="language-plaintext highlighter-rouge">TableCell</code>, but we have <code class="language-plaintext highlighter-rouge">getControlCssMetaData()</code> that we can use instead.</p>

<p>And now we can do this in the style sheet:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.temperature-column</span> <span class="p">{</span>
  <span class="py">-wfx-temperature-unit</span><span class="p">:</span> <span class="s1">"k"</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="adding-more-styleable-properties">Adding More Styleable Properties</h2>

<p>What if, instead of “freezing”, you wanted to define “cold” as a styleable element?  Even freezing is contextual, because the use case assumes the freezing point of water, but “cold” isn’t a generally understood value.  This means we’ll have to have some way to define “cold” in the style sheet, as well as provide all of the same elements that we did for freezing.</p>

<p>We’ve already performed most of the types of setup that we will need for the previous styling, so adding “cold” is just applying what we’ve already seen:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Example2</span> <span class="p">:</span> <span class="nc">Application</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">stage</span><span class="p">.</span><span class="n">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="nf">createContent</span><span class="p">(),</span> <span class="mf">400.0</span><span class="p">,</span> <span class="mf">350.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nc">Example2</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"Example0.css"</span><span class="p">)</span><span class="o">?.</span><span class="nf">toString</span><span class="p">()</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="n">stylesheets</span> <span class="p">+=</span> <span class="n">it</span> <span class="p">}</span>
        <span class="p">}</span>
        <span class="nc">Menu</span><span class="p">()</span>
        <span class="n">stage</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">createContent</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">BorderPane</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">center</span> <span class="p">=</span> <span class="nc">TableView</span><span class="p">&lt;</span><span class="nc">TableData1</span><span class="p">&gt;().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">columns</span> <span class="p">+=</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">TableData1</span><span class="p">,</span> <span class="nc">String</span><span class="p">&gt;(</span><span class="s">"Column A"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="nf">setCellValueFactory</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value1</span> <span class="p">}</span>
                <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"a-column"</span>
            <span class="p">}</span>
            <span class="n">columns</span> <span class="p">+=</span> <span class="nc">TemperatureColumn2</span><span class="p">&lt;</span><span class="nc">TableData1</span><span class="p">&gt;</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value4</span> <span class="p">}</span> <span class="n">outputAs</span> <span class="nc">TemperatureUnit</span><span class="p">.</span><span class="nc">F</span>
            <span class="n">columns</span> <span class="p">+=</span> <span class="nc">TemperatureColumn2</span><span class="p">&lt;</span><span class="nc">TableData1</span><span class="p">&gt;</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value4</span> <span class="p">}</span>
            <span class="n">columns</span> <span class="p">+=</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">TableData1</span><span class="p">,</span> <span class="nc">String</span><span class="p">&gt;(</span><span class="s">"Column C"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="nf">setCellValueFactory</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value3</span> <span class="p">}</span>
                <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"c-column"</span>
            <span class="p">}</span>
            <span class="nf">repeat</span><span class="p">(</span><span class="mi">30</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">items</span> <span class="p">+=</span> <span class="nc">TableData1</span><span class="p">(</span>
                    <span class="s">"A"</span><span class="p">,</span>
                    <span class="s">"B"</span><span class="p">,</span>
                    <span class="s">"C"</span><span class="p">,</span>
                    <span class="nc">Random</span><span class="p">.</span><span class="nf">nextDouble</span><span class="p">(-</span><span class="mf">30.0</span><span class="p">,</span> <span class="mf">55.0</span><span class="p">)</span>
                <span class="p">)</span>
            <span class="p">}</span>

        <span class="p">}</span>
        <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">10.0</span><span class="p">)</span>

    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="nc">TemperatureColumn2</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;(</span><span class="n">extractor</span><span class="p">:</span> <span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="nc">T</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">DoubleProperty</span><span class="p">)</span> <span class="p">:</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">,</span> <span class="nc">Double</span><span class="p">&gt;()</span> <span class="p">{</span>

    <span class="k">private</span> <span class="kd">val</span> <span class="py">outputUnits</span><span class="p">:</span> <span class="nc">StyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">TemperatureUnit</span><span class="p">&gt;</span> <span class="p">=</span>
        <span class="nc">SimpleStyleableObjectProperty</span><span class="p">(</span><span class="nc">OUTPUT_UNITS_META_DATA</span><span class="p">,</span> <span class="nc">TemperatureUnit</span><span class="p">.</span><span class="nc">C</span><span class="p">)</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">numDecimals</span><span class="p">:</span> <span class="nc">StyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">Number</span><span class="p">&gt;</span> <span class="p">=</span>
        <span class="nc">SimpleStyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">Number</span><span class="p">&gt;(</span><span class="nc">NUM_DECIMALS_META_DATA</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">coldPoint</span><span class="p">:</span> <span class="nc">StyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">Number</span><span class="p">&gt;</span> <span class="p">=</span>
        <span class="nc">SimpleStyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">Number</span><span class="p">&gt;(</span><span class="nc">COLD_POINT_META_DATA</span><span class="p">,</span> <span class="mi">1000</span><span class="p">)</span>

    <span class="k">infix</span> <span class="k">fun</span> <span class="nf">outputAs</span><span class="p">(</span><span class="n">newOutput</span><span class="p">:</span> <span class="nc">TemperatureUnit</span><span class="p">)</span> <span class="p">=</span> <span class="k">this</span><span class="p">.</span><span class="nf">apply</span> <span class="p">{</span> <span class="n">outputUnits</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">newOutput</span> <span class="p">}</span>

    <span class="k">companion</span> <span class="k">object</span> <span class="nc">StylingStuff</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">freezingPseudoClass</span><span class="p">:</span> <span class="nc">PseudoClass</span> <span class="p">=</span> <span class="nc">PseudoClass</span><span class="p">.</span><span class="nf">getPseudoClass</span><span class="p">(</span><span class="s">"freezing"</span><span class="p">)</span>
        <span class="kd">val</span> <span class="py">coldPseudoClass</span><span class="p">:</span> <span class="nc">PseudoClass</span> <span class="p">=</span> <span class="nc">PseudoClass</span><span class="p">.</span><span class="nf">getPseudoClass</span><span class="p">(</span><span class="s">"cold"</span><span class="p">)</span>
        <span class="kd">val</span> <span class="py">OUTPUT_UNITS_META_DATA</span><span class="p">:</span> <span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">TemperatureTableCell2</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;,</span> <span class="nc">TemperatureUnit</span><span class="p">&gt;</span> <span class="p">=</span> <span class="kd">object</span> <span class="err">:
            </span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">TemperatureTableCell2</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;,</span> <span class="nc">TemperatureUnit</span><span class="p">&gt;(</span>
                <span class="s">"-wfx-temperature-unit"</span><span class="p">,</span>
                <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getEnumConverter</span><span class="p">(</span><span class="nc">TemperatureUnit</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
            <span class="p">)</span> <span class="p">{</span>
            <span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">styleable</span><span class="p">:</span> <span class="nc">TemperatureTableCell2</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;)</span> <span class="p">=</span> <span class="p">!(</span><span class="n">styleable</span><span class="p">.</span><span class="n">outputUnits</span><span class="p">.</span><span class="n">isBound</span><span class="p">)</span>

            <span class="k">override</span> <span class="k">fun</span> <span class="nf">getStyleableProperty</span><span class="p">(</span><span class="n">styleable</span><span class="p">:</span> <span class="nc">TemperatureTableCell2</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;)</span> <span class="p">=</span> <span class="n">styleable</span><span class="p">.</span><span class="n">outputUnits</span>
        <span class="p">}</span>
        <span class="kd">val</span> <span class="py">NUM_DECIMALS_META_DATA</span><span class="p">:</span> <span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">TemperatureTableCell2</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;,</span> <span class="nc">Number</span><span class="p">&gt;</span> <span class="p">=</span> <span class="kd">object</span> <span class="err">:
            </span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">TemperatureTableCell2</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;,</span> <span class="nc">Number</span><span class="p">&gt;(</span>
                <span class="s">"-wfx-num-decimal"</span><span class="p">,</span>
                <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getSizeConverter</span><span class="p">()</span>
            <span class="p">)</span> <span class="p">{</span>
            <span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">styleable</span><span class="p">:</span> <span class="nc">TemperatureTableCell2</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;)</span> <span class="p">=</span>
                <span class="p">!(</span><span class="n">styleable</span><span class="p">.</span><span class="n">numDecimals</span><span class="p">.</span><span class="n">isBound</span><span class="p">)</span>

            <span class="k">override</span> <span class="k">fun</span> <span class="nf">getStyleableProperty</span><span class="p">(</span><span class="n">styleable</span><span class="p">:</span> <span class="nc">TemperatureTableCell2</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;)</span> <span class="p">=</span> <span class="n">styleable</span><span class="p">.</span><span class="n">numDecimals</span>
        <span class="p">}</span>
        <span class="kd">val</span> <span class="py">COLD_POINT_META_DATA</span><span class="p">:</span> <span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">TemperatureTableCell2</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;,</span> <span class="nc">Number</span><span class="p">&gt;</span> <span class="p">=</span> <span class="kd">object</span> <span class="err">:
            </span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">TemperatureTableCell2</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;,</span> <span class="nc">Number</span><span class="p">&gt;(</span>
                <span class="s">"-wfx-cold-point"</span><span class="p">,</span>
                <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getSizeConverter</span><span class="p">()</span>
            <span class="p">)</span> <span class="p">{</span>
            <span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">styleable</span><span class="p">:</span> <span class="nc">TemperatureTableCell2</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;)</span> <span class="p">=</span>
                <span class="p">!(</span><span class="n">styleable</span><span class="p">.</span><span class="n">coldPoint</span><span class="p">.</span><span class="n">isBound</span><span class="p">)</span>

            <span class="k">override</span> <span class="k">fun</span> <span class="nf">getStyleableProperty</span><span class="p">(</span><span class="n">styleable</span><span class="p">:</span> <span class="nc">TemperatureTableCell2</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;)</span> <span class="p">=</span> <span class="n">styleable</span><span class="p">.</span><span class="n">coldPoint</span>
        <span class="p">}</span>

        <span class="k">private</span> <span class="kd">val</span> <span class="py">cssMetaDataList</span> <span class="p">=</span>
            <span class="p">(</span><span class="nc">TableColumn</span><span class="p">.</span><span class="nf">getClassCssMetaData</span><span class="p">()</span> <span class="p">+</span>
                    <span class="nc">OUTPUT_UNITS_META_DATA</span> <span class="p">+</span>
                    <span class="nc">NUM_DECIMALS_META_DATA</span> <span class="p">+</span>
                    <span class="nc">COLD_POINT_META_DATA</span><span class="p">)</span> <span class="k">as</span> <span class="nc">MutableList</span>

        <span class="k">fun</span> <span class="nf">getClassCssMetaData</span><span class="p">()</span> <span class="p">=</span> <span class="n">cssMetaDataList</span>
    <span class="p">}</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"temperature-column"</span>
        <span class="nf">setCellValueFactory</span> <span class="p">{</span>
            <span class="n">extractor</span><span class="p">.</span><span class="nf">invoke</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">value</span><span class="p">).</span><span class="nf">asObject</span><span class="p">()</span>
        <span class="p">}</span>
        <span class="nc">TemperatureColumn2</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"thermometer24.png"</span><span class="p">)</span><span class="o">?.</span><span class="nf">toString</span><span class="p">()</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="n">graphic</span> <span class="p">=</span> <span class="nc">ImageView</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">}</span>
        <span class="nf">setCellFactory</span> <span class="p">{</span> <span class="nc">TemperatureTableCell2</span><span class="p">(</span><span class="n">outputUnits</span><span class="p">,</span> <span class="n">numDecimals</span><span class="p">,</span> <span class="n">coldPoint</span><span class="p">)</span> <span class="p">}</span>
    <span class="p">}</span>

<span class="p">}</span>

<span class="kd">class</span> <span class="nc">TemperatureTableCell2</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;(</span>
    <span class="kd">val</span> <span class="py">outputUnits</span><span class="p">:</span> <span class="nc">StyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">TemperatureUnit</span><span class="p">&gt;,</span>
    <span class="kd">val</span> <span class="py">numDecimals</span><span class="p">:</span> <span class="nc">StyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">Number</span><span class="p">&gt;,</span>
    <span class="kd">val</span> <span class="py">coldPoint</span><span class="p">:</span> <span class="nc">StyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">Number</span><span class="p">&gt;,</span>
<span class="p">)</span> <span class="p">:</span>
    <span class="nc">TableCell</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">,</span> <span class="nc">Double</span><span class="p">&gt;()</span> <span class="p">{</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">getControlCssMetaData</span><span class="p">()</span> <span class="p">=</span>
        <span class="p">(</span><span class="k">super</span><span class="p">.</span><span class="nf">getControlCssMetaData</span><span class="p">()</span> <span class="p">+</span> <span class="nc">TemperatureColumn2</span><span class="p">.</span><span class="nf">getClassCssMetaData</span><span class="p">())</span> <span class="k">as</span> <span class="nc">MutableList</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="nf">itemProperty</span><span class="p">().</span><span class="nf">subscribe</span> <span class="p">{</span> <span class="n">newValue</span> <span class="p">-&gt;</span>
            <span class="nf">pseudoClassStateChanged</span><span class="p">(</span><span class="nc">TemperatureColumn2</span><span class="p">.</span><span class="n">freezingPseudoClass</span><span class="p">,</span> <span class="p">(</span><span class="n">newValue</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="p">(</span><span class="n">it</span> <span class="p">&lt;=</span> <span class="mf">0.0</span><span class="p">)</span> <span class="p">}</span> <span class="o">?:</span> <span class="k">false</span><span class="p">))</span>
            <span class="nf">pseudoClassStateChanged</span><span class="p">(</span>
                <span class="nc">TemperatureColumn2</span><span class="p">.</span><span class="n">coldPseudoClass</span><span class="p">,</span>
                <span class="p">(</span><span class="n">newValue</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="p">(</span><span class="n">it</span> <span class="p">&lt;=</span> <span class="n">coldPoint</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="nf">toDouble</span><span class="p">())</span> <span class="p">}</span> <span class="o">?:</span> <span class="k">false</span><span class="p">))</span>
        <span class="p">}</span>
        <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span>
            <span class="nc">ConversionBinding</span><span class="p">(</span><span class="nf">itemProperty</span><span class="p">(),</span> <span class="n">outputUnits</span><span class="p">)</span>
                <span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">rawValue</span> <span class="p">-&gt;</span> <span class="n">rawValue</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="p">(</span><span class="n">rawValue</span> <span class="p">*</span> <span class="mf">10.0</span><span class="p">).</span><span class="nf">roundToInt</span><span class="p">()</span> <span class="p">/</span> <span class="mf">10.0</span> <span class="p">}</span><span class="o">?.</span><span class="nf">toString</span><span class="p">()</span> <span class="p">})</span>
    <span class="p">}</span>
<span class="p">}</span>


<span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Application</span><span class="p">.</span><span class="nf">launch</span><span class="p">(</span><span class="nc">Example2</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
</code></pre></div></div>
<p>With the following added to the CSS file:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.table-row-cell</span> <span class="nc">.temperature-column</span><span class="nd">:cold</span>  <span class="p">{</span>
  <span class="py">-fx-text-fill</span><span class="p">:</span> <span class="no">hotpink</span><span class="p">;</span>
  <span class="py">-fx-font-weight</span><span class="p">:</span> <span class="nb">bold</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.table-row-cell</span> <span class="nc">.temperature-column</span><span class="nd">:freezing</span>  <span class="p">{</span>
  <span class="py">-fx-text-fill</span><span class="p">:</span> <span class="no">cornflowerblue</span><span class="p">;</span>
  <span class="py">-fx-font-weight</span><span class="p">:</span> <span class="nb">bold</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Note that the <code class="language-plaintext highlighter-rouge">freezing</code> element goes after the <code class="language-plaintext highlighter-rouge">cold</code> element, because the freezing threshold (in this case) is below cold and will need to override <code class="language-plaintext highlighter-rouge">cold</code>.</p>

<p>There is more that we could do, but I’m sure that you get the idea at this point.</p>

<h1 id="more-utilitarian-tablecolumn-types">More Utilitarian TableColumn Types</h1>

<p>In my own projects I rarely use TableColumn directly.  I have custom columns for <code class="language-plaintext highlighter-rouge">Double</code>, <code class="language-plaintext highlighter-rouge">Integer</code> and even <code class="language-plaintext highlighter-rouge">String</code>.  One of the primitive types that really doesn’t work for me out-of-the-box is <code class="language-plaintext highlighter-rouge">Boolean</code>.  The default is a <code class="language-plaintext highlighter-rouge">CheckBox</code>, and it just doesn’t look right.  I much prefer to use an image of some sort.</p>

<p>Something like this:</p>

<p><img src="/assets/elements/TableColumn7.png" alt="Image Boolean Column" /></p>

<p>This can look a little busy, so it can be useful to only show the <code class="language-plaintext highlighter-rouge">true</code> images:</p>

<p><img src="/assets/elements/TableColumn8.png" alt="Image Boolean Column" /></p>

<p>It can be cleaner to just use red and green dots:</p>

<p><img src="/assets/elements/TableColumn9.png" alt="Image Boolean Column" /></p>

<p>This can be especially effective if you only show the dots for <code class="language-plaintext highlighter-rouge">true</code> values:</p>

<p><img src="/assets/elements/TableColumn10.png" alt="Image Boolean Column" /></p>

<p>Let’s look at the code for this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Example3</span> <span class="p">:</span> <span class="nc">Application</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">stage</span><span class="p">.</span><span class="n">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="nf">createContent</span><span class="p">(),</span> <span class="mf">400.0</span><span class="p">,</span> <span class="mf">350.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nc">Example3</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"Example0.css"</span><span class="p">)</span><span class="o">?.</span><span class="nf">toString</span><span class="p">()</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="n">stylesheets</span> <span class="p">+=</span> <span class="n">it</span> <span class="p">}</span>
        <span class="p">}</span>
        <span class="n">stage</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">createContent</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">BorderPane</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">center</span> <span class="p">=</span> <span class="nc">TableView</span><span class="p">&lt;</span><span class="nc">TableData3</span><span class="p">&gt;().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">columns</span> <span class="p">+=</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">TableData3</span><span class="p">,</span> <span class="nc">String</span><span class="p">&gt;(</span><span class="s">"Column A"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="nf">setCellValueFactory</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value1</span> <span class="p">}</span>
                <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"a-column"</span>
            <span class="p">}</span>
            <span class="n">columns</span> <span class="p">+=</span> <span class="nc">BooleanColumn</span><span class="p">&lt;</span><span class="nc">TableData3</span><span class="p">&gt;(</span><span class="s">"Column C"</span><span class="p">)</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value4</span> <span class="p">}.</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"c-column"</span>
            <span class="p">}</span> <span class="n">withFalseValues</span> <span class="k">false</span> <span class="n">withCircles</span> <span class="k">true</span> <span class="n">withCircleSize</span> <span class="mf">18.0</span>
            <span class="nf">repeat</span><span class="p">(</span><span class="mi">30</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">items</span> <span class="p">+=</span> <span class="nc">TableData3</span><span class="p">(</span>
                    <span class="s">"A"</span><span class="p">,</span>
                    <span class="s">"B"</span><span class="p">,</span>
                    <span class="s">"C"</span><span class="p">,</span>
                    <span class="nc">Random</span><span class="p">.</span><span class="nf">nextBoolean</span><span class="p">()</span>
                <span class="p">)</span>
            <span class="p">}</span>

        <span class="p">}</span>
        <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">10.0</span><span class="p">)</span>

    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="nc">BooleanColumn</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;(</span><span class="n">title</span><span class="p">:</span> <span class="nc">String</span><span class="p">,</span> <span class="n">extractor</span><span class="p">:</span> <span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="nc">T</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="nc">BooleanProperty</span><span class="p">)</span> <span class="p">:</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">,</span> <span class="nc">Boolean</span><span class="p">&gt;(</span><span class="n">title</span><span class="p">)</span> <span class="p">{</span>

    <span class="k">private</span> <span class="kd">val</span> <span class="py">showFalse</span><span class="p">:</span> <span class="nc">StyleableBooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleStyleableBooleanProperty</span><span class="p">(</span><span class="nc">SHOW_FALSE_META_DATA</span><span class="p">,</span> <span class="k">true</span><span class="p">)</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">useCircles</span><span class="p">:</span> <span class="nc">StyleableBooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleStyleableBooleanProperty</span><span class="p">(</span><span class="nc">USE_CIRCLES_META_DATA</span><span class="p">,</span> <span class="k">false</span><span class="p">)</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">circleSize</span><span class="p">:</span> <span class="nc">StyleableDoubleProperty</span> <span class="p">=</span> <span class="nc">SimpleStyleableDoubleProperty</span><span class="p">(</span><span class="nc">CIRCLE_SIZE_META_DATA</span><span class="p">,</span> <span class="mf">7.0</span><span class="p">)</span>

    <span class="k">infix</span> <span class="k">fun</span> <span class="nf">withCircles</span><span class="p">(</span><span class="n">withCircles</span><span class="p">:</span> <span class="nc">Boolean</span><span class="p">)</span> <span class="p">=</span> <span class="nf">apply</span> <span class="p">{</span>
        <span class="n">useCircles</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">withCircles</span>
    <span class="p">}</span>

    <span class="k">infix</span> <span class="k">fun</span> <span class="nf">withCircleSize</span><span class="p">(</span><span class="n">circleRadius</span><span class="p">:</span> <span class="nc">Double</span><span class="p">)</span> <span class="p">=</span> <span class="nf">apply</span> <span class="p">{</span>
        <span class="n">circleSize</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">circleRadius</span>
    <span class="p">}</span>

    <span class="k">infix</span> <span class="k">fun</span> <span class="nf">withFalseValues</span><span class="p">(</span><span class="n">falseValues</span><span class="p">:</span> <span class="nc">Boolean</span><span class="p">)</span> <span class="p">=</span> <span class="nf">apply</span> <span class="p">{</span>
        <span class="n">showFalse</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">falseValues</span>
    <span class="p">}</span>

    <span class="k">companion</span> <span class="k">object</span> <span class="nc">StylingStuff</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">SHOW_FALSE_META_DATA</span><span class="p">:</span> <span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">BooleanTableCell</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;,</span> <span class="nc">Boolean</span><span class="p">&gt;</span> <span class="p">=</span> <span class="kd">object</span> <span class="err">:
            </span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">BooleanTableCell</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;,</span> <span class="nc">Boolean</span><span class="p">&gt;(</span>
                <span class="s">"-wfx-show-false"</span><span class="p">,</span>
                <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getBooleanConverter</span><span class="p">()</span>
            <span class="p">)</span> <span class="p">{</span>
            <span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">styleable</span><span class="p">:</span> <span class="nc">BooleanTableCell</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;)</span> <span class="p">=</span> <span class="p">!(</span><span class="n">styleable</span><span class="p">.</span><span class="n">showFalse</span><span class="p">.</span><span class="n">isBound</span><span class="p">)</span>

            <span class="k">override</span> <span class="k">fun</span> <span class="nf">getStyleableProperty</span><span class="p">(</span><span class="n">styleable</span><span class="p">:</span> <span class="nc">BooleanTableCell</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;)</span> <span class="p">=</span> <span class="n">styleable</span><span class="p">.</span><span class="n">showFalse</span>
        <span class="p">}</span>
        <span class="kd">val</span> <span class="py">USE_CIRCLES_META_DATA</span><span class="p">:</span> <span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">BooleanTableCell</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;,</span> <span class="nc">Boolean</span><span class="p">&gt;</span> <span class="p">=</span> <span class="kd">object</span> <span class="err">:
            </span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">BooleanTableCell</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;,</span> <span class="nc">Boolean</span><span class="p">&gt;(</span>
                <span class="s">"-wfx-use-circles"</span><span class="p">,</span>
                <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getBooleanConverter</span><span class="p">()</span>
            <span class="p">)</span> <span class="p">{</span>
            <span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">styleable</span><span class="p">:</span> <span class="nc">BooleanTableCell</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;)</span> <span class="p">=</span> <span class="p">!(</span><span class="n">styleable</span><span class="p">.</span><span class="n">useCircles</span><span class="p">.</span><span class="n">isBound</span><span class="p">)</span>

            <span class="k">override</span> <span class="k">fun</span> <span class="nf">getStyleableProperty</span><span class="p">(</span><span class="n">styleable</span><span class="p">:</span> <span class="nc">BooleanTableCell</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;)</span> <span class="p">=</span> <span class="n">styleable</span><span class="p">.</span><span class="n">useCircles</span>
        <span class="p">}</span>
        <span class="kd">val</span> <span class="py">CIRCLE_SIZE_META_DATA</span><span class="p">:</span> <span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">BooleanTableCell</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;,</span> <span class="nc">Number</span><span class="p">&gt;</span> <span class="p">=</span> <span class="kd">object</span> <span class="err">:
            </span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">BooleanTableCell</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;,</span> <span class="nc">Number</span><span class="p">&gt;(</span>
                <span class="s">"-wfx-circle-size"</span><span class="p">,</span>
                <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getSizeConverter</span><span class="p">()</span>
            <span class="p">)</span> <span class="p">{</span>
            <span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">styleable</span><span class="p">:</span> <span class="nc">BooleanTableCell</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;)</span> <span class="p">=</span> <span class="p">!(</span><span class="n">styleable</span><span class="p">.</span><span class="n">circleSize</span><span class="p">.</span><span class="n">isBound</span><span class="p">)</span>

            <span class="k">override</span> <span class="k">fun</span> <span class="nf">getStyleableProperty</span><span class="p">(</span><span class="n">styleable</span><span class="p">:</span> <span class="nc">BooleanTableCell</span><span class="p">&lt;</span><span class="err">*</span><span class="p">&gt;)</span> <span class="p">=</span> <span class="n">styleable</span><span class="p">.</span><span class="n">circleSize</span>
        <span class="p">}</span>

        <span class="k">private</span> <span class="kd">val</span> <span class="py">cssMetaDataList</span> <span class="p">=</span>
            <span class="p">(</span><span class="nc">TableColumn</span><span class="p">.</span><span class="nf">getClassCssMetaData</span><span class="p">()</span> <span class="p">+</span> <span class="nc">SHOW_FALSE_META_DATA</span> <span class="p">+</span> <span class="nc">USE_CIRCLES_META_DATA</span> <span class="p">+</span> <span class="nc">CIRCLE_SIZE_META_DATA</span><span class="p">)</span> <span class="k">as</span> <span class="nc">MutableList</span>

        <span class="k">fun</span> <span class="nf">getClassCssMetaData</span><span class="p">()</span> <span class="p">=</span> <span class="n">cssMetaDataList</span>
    <span class="p">}</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"boolean-column"</span>
        <span class="nf">setCellValueFactory</span> <span class="p">{</span>
            <span class="n">extractor</span><span class="p">.</span><span class="nf">invoke</span><span class="p">(</span><span class="n">it</span><span class="p">.</span><span class="n">value</span><span class="p">).</span><span class="nf">asObject</span><span class="p">()</span>
        <span class="p">}</span>
        <span class="nf">setCellFactory</span> <span class="p">{</span> <span class="nc">BooleanTableCell</span><span class="p">(</span><span class="n">showFalse</span><span class="p">,</span> <span class="n">useCircles</span><span class="p">,</span> <span class="n">circleSize</span><span class="p">)</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="nc">BooleanTableCell</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;(</span>
    <span class="kd">val</span> <span class="py">showFalse</span><span class="p">:</span> <span class="nc">StyleableBooleanProperty</span><span class="p">,</span>
    <span class="kd">val</span> <span class="py">useCircles</span><span class="p">:</span> <span class="nc">StyleableBooleanProperty</span><span class="p">,</span>
    <span class="kd">val</span> <span class="py">circleSize</span><span class="p">:</span> <span class="nc">StyleableDoubleProperty</span>
<span class="p">)</span> <span class="p">:</span> <span class="nc">TableCell</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">,</span> <span class="nc">Boolean</span><span class="p">&gt;()</span> <span class="p">{</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">getControlCssMetaData</span><span class="p">()</span> <span class="p">=</span>
        <span class="p">(</span><span class="k">super</span><span class="p">.</span><span class="nf">getControlCssMetaData</span><span class="p">()</span> <span class="p">+</span> <span class="nc">BooleanColumn</span><span class="p">.</span><span class="nf">getClassCssMetaData</span><span class="p">())</span> <span class="k">as</span> <span class="nc">MutableList</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">booleanItem</span> <span class="p">=</span> <span class="nf">booleanProperty</span><span class="p">(</span><span class="nf">itemProperty</span><span class="p">())</span>
        <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"boolean-table-cell"</span>
        <span class="n">graphic</span> <span class="p">=</span> <span class="nc">StackPane</span><span class="p">(</span>
            <span class="nc">ImageView</span><span class="p">(</span><span class="nc">BooleanColumn</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"checkmark.png"</span><span class="p">)</span><span class="o">?.</span><span class="nf">toString</span><span class="p">()).</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="n">fitWidth</span> <span class="p">=</span> <span class="mf">14.0</span>
                <span class="n">fitHeight</span> <span class="p">=</span> <span class="mf">14.0</span>
                <span class="nf">visibleProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">booleanItem</span><span class="p">.</span><span class="nf">and</span><span class="p">(</span><span class="n">useCircles</span><span class="p">.</span><span class="nf">not</span><span class="p">()))</span>
                <span class="k">this</span><span class="p">.</span><span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"true-image"</span>
            <span class="p">},</span>
            <span class="nc">ImageView</span><span class="p">(</span><span class="nc">BooleanColumn</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"crossmark.png"</span><span class="p">)</span><span class="o">?.</span><span class="nf">toString</span><span class="p">()).</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="n">fitWidth</span> <span class="p">=</span> <span class="mf">14.0</span>
                <span class="n">fitHeight</span> <span class="p">=</span> <span class="mf">14.0</span>
                <span class="nf">visibleProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">booleanItem</span><span class="p">.</span><span class="nf">not</span><span class="p">().</span><span class="nf">and</span><span class="p">(</span><span class="n">showFalse</span><span class="p">).</span><span class="nf">and</span><span class="p">(</span><span class="n">useCircles</span><span class="p">.</span><span class="nf">not</span><span class="p">()))</span>
                <span class="k">this</span><span class="p">.</span><span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"false-image"</span>
            <span class="p">},</span>
            <span class="nc">Circle</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="nf">visibleProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">booleanItem</span><span class="p">.</span><span class="nf">and</span><span class="p">(</span><span class="n">useCircles</span><span class="p">))</span>
                <span class="nf">radiusProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">circleSize</span><span class="p">)</span>
                <span class="n">fill</span> <span class="p">=</span> <span class="nc">Color</span><span class="p">.</span><span class="nc">MEDIUMSEAGREEN</span>
                <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"true-circle"</span>
            <span class="p">},</span>
            <span class="nc">Circle</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="nf">visibleProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">booleanItem</span><span class="p">.</span><span class="nf">not</span><span class="p">().</span><span class="nf">and</span><span class="p">(</span><span class="n">useCircles</span><span class="p">).</span><span class="nf">and</span><span class="p">(</span><span class="n">showFalse</span><span class="p">))</span>
                <span class="nf">radiusProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">circleSize</span><span class="p">)</span>
                <span class="n">fill</span> <span class="p">=</span> <span class="nc">Color</span><span class="p">.</span><span class="nc">FIREBRICK</span>
                <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"false-circle"</span>
            <span class="p">}</span>
        <span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="nc">TableData3</span><span class="p">(</span><span class="n">initVal1</span><span class="p">:</span> <span class="nc">String</span><span class="p">,</span> <span class="n">initVal2</span><span class="p">:</span> <span class="nc">String</span><span class="p">,</span> <span class="n">initVal3</span><span class="p">:</span> <span class="nc">String</span><span class="p">,</span> <span class="n">initVal4</span><span class="p">:</span> <span class="nc">Boolean</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">value1</span><span class="p">:</span> <span class="nc">StringProperty</span> <span class="p">=</span> <span class="nc">SimpleStringProperty</span><span class="p">(</span><span class="n">initVal1</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">value2</span><span class="p">:</span> <span class="nc">StringProperty</span> <span class="p">=</span> <span class="nc">SimpleStringProperty</span><span class="p">(</span><span class="n">initVal2</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">value3</span><span class="p">:</span> <span class="nc">StringProperty</span> <span class="p">=</span> <span class="nc">SimpleStringProperty</span><span class="p">(</span><span class="n">initVal3</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">value4</span><span class="p">:</span> <span class="nc">BooleanProperty</span> <span class="p">=</span> <span class="nc">SimpleBooleanProperty</span><span class="p">(</span><span class="n">initVal4</span><span class="p">)</span>
<span class="p">}</span>

<span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Application</span><span class="p">.</span><span class="nf">launch</span><span class="p">(</span><span class="nc">Example3</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
</code></pre></div></div>
<p>and the CSS file:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.boolean-column</span> <span class="p">{</span>
  <span class="py">-wfx-show-false</span><span class="p">:</span> <span class="n">false</span><span class="p">;</span>
  <span class="py">-wfx-use-circles</span><span class="p">:</span> <span class="n">true</span><span class="p">;</span>
  <span class="py">-wfx-circle-size</span><span class="p">:</span> <span class="m">5.0</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.boolean-column</span> <span class="nc">.true-image</span> <span class="p">{</span>
  <span class="py">-fx-fit-width</span><span class="p">:</span> <span class="m">14px</span><span class="p">;</span>
  <span class="py">-fx-fit-height</span><span class="p">:</span> <span class="m">14px</span><span class="p">;</span>
  <span class="py">-fx-image</span><span class="p">:</span> <span class="sx">url(checkmark.png)</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.boolean-column</span> <span class="nc">.false-image</span> <span class="p">{</span>
  <span class="py">-fx-fit-width</span><span class="p">:</span> <span class="m">14px</span><span class="p">;</span>
  <span class="py">-fx-fit-height</span><span class="p">:</span> <span class="m">14px</span><span class="p">;</span>
  <span class="py">-fx-image</span><span class="p">:</span> <span class="sx">url(crossmark.png)</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.boolean-column</span> <span class="nc">.true-circle</span> <span class="p">{</span>
  <span class="py">-fx-fill</span><span class="p">:</span> <span class="no">green</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.boolean-column</span> <span class="nc">.false-circle</span> <span class="p">{</span>
  <span class="py">-fx-fill</span><span class="p">:</span> <span class="no">red</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>We have 3 <code class="language-plaintext highlighter-rouge">StyleableProperties</code> 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 <code class="language-plaintext highlighter-rouge">false</code>.  These <code class="language-plaintext highlighter-rouge">StyleableProperties</code> are all passed to the <code class="language-plaintext highlighter-rouge">BooleanTableCell</code> as constructor parameters.</p>

<p>The <code class="language-plaintext highlighter-rouge">BooleanTableCell</code> has a <code class="language-plaintext highlighter-rouge">graphic</code> that is just a <code class="language-plaintext highlighter-rouge">StackPane</code> with 2 <code class="language-plaintext highlighter-rouge">ImageViews</code> and 2 <code class="language-plaintext highlighter-rouge">Circles</code> in it. The visibility of each of those elements is bound to dependencies on <code class="language-plaintext highlighter-rouge">showFalse</code>, <code class="language-plaintext highlighter-rouge">useCircles</code> and the <code class="language-plaintext highlighter-rouge">item</code> value, such that only one of them (or maybe none of them) will ever be visible.  Note that the <code class="language-plaintext highlighter-rouge">item Property</code> is of type <code class="language-plaintext highlighter-rouge">ObjectProperty&lt;Boolean&gt;</code>, so you don’t have the Fluent API for <code class="language-plaintext highlighter-rouge">BooleanProperty</code>.  To get around this, the variable <code class="language-plaintext highlighter-rouge">booleanItem</code> is instantiated using <code class="language-plaintext highlighter-rouge">BooleanProperty.booleanProperty()</code> which is automatically bound to <code class="language-plaintext highlighter-rouge">item</code> but is of type <code class="language-plaintext highlighter-rouge">BooleanProperty</code>.  Then the Fluent API can be used in the visibility bindings.</p>

<p>There are default values for the <code class="language-plaintext highlighter-rouge">ImageView</code> images and the <code class="language-plaintext highlighter-rouge">Circle</code> colours.  However, no provision has been made to allow them to be modified from the layout.  This means that the <code class="language-plaintext highlighter-rouge">BooleanTableCell</code> will work without any Style Sheet configuration at all, but you cannot change the colours without using a style sheet.</p>

<h2 id="accessing-the-booleantablecell-programmatically">Accessing the BooleanTableCell Programmatically</h2>

<p>OK.  You can’t do this.  Even if you could find a way to do so, you’d be breaking the underlying paradigm for <code class="language-plaintext highlighter-rouge">TableView</code> and it would be a bad idea.</p>

<p>The whole idea behind <code class="language-plaintext highlighter-rouge">TableView</code> is that the <code class="language-plaintext highlighter-rouge">TableCells</code> at runtime are kept at “arms length” from your code.  You don’t know how many there are, you don’t have references to them, you don’t know which ones are currently in use and you don’t access them directly.  What you <em>can</em> do is define them, and tell the system how to create one.</p>

<p>This 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 <code class="language-plaintext highlighter-rouge">Callback</code> specified in the <code class="language-plaintext highlighter-rouge">TableColumn</code>.  What you can interact directly with, however, is the <code class="language-plaintext highlighter-rouge">TableColumn</code>.  That’s why the <code class="language-plaintext highlighter-rouge">StyleableProperties</code> are defined in the <code class="language-plaintext highlighter-rouge">BooleanColumn</code> and then passed to the <code class="language-plaintext highlighter-rouge">BooleanTableCell</code> in its constructor.  That’s also why the three <code class="language-plaintext highlighter-rouge">infix</code> decorator methods are defined in the <code class="language-plaintext highlighter-rouge">BooleanColumn</code>.</p>

<p>This means you can do this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">columns</span> <span class="p">+=</span> <span class="nc">BooleanColumn</span><span class="p">&lt;</span><span class="nc">TableData3</span><span class="p">&gt;(</span><span class="s">"Column C"</span><span class="p">)</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value4</span> <span class="p">}.</span><span class="nf">apply</span> <span class="p">{</span>
    <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"c-column"</span>
<span class="p">}</span> <span class="n">withFalseValues</span> <span class="k">false</span> <span class="n">withCircles</span> <span class="k">true</span> <span class="n">withCircleSize</span> <span class="mf">18.0</span>
</code></pre></div></div>

<h2 id="accessing-the-imageviews-and-circles">Accessing the ImageViews and Circles</h2>

<p>Because the <code class="language-plaintext highlighter-rouge">ImageViews</code> and the <code class="language-plaintext highlighter-rouge">Circles</code> are defined in the <code class="language-plaintext highlighter-rouge">BooleanTableCell</code>, you cannot access them programatically from your layout code at all.</p>

<p>This 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.</p>

<p>Once again, if you do want to programatically configure them, you’ll have to do it through the <code class="language-plaintext highlighter-rouge">BooleanColumn</code> and then pass the values to the <code class="language-plaintext highlighter-rouge">BooleanTableCell</code> in it’s constructor.</p>

<h1 id="javadocs">JavaDocs</h1>

<p>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, <strong>anything</strong> that you can do to reduce and simplify your layout code is going to be a big win.</p>

<p>If you can define a <code class="language-plaintext highlighter-rouge">TableView</code> with 10 columns with 10-15 lines of code, and if 10 of those lines are just simple constructor calls for custom <code class="language-plaintext highlighter-rouge">TableColumns</code> you can go a long way towards simplifying to layout code.  This is especially true if you use good, descriptive names for your custom <code class="language-plaintext highlighter-rouge">TableColumns</code>.</p>

<p>Look at our <code class="language-plaintext highlighter-rouge">BooleanColumn</code>, it can be instantiated like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">columns</span> <span class="p">+=</span> <span class="nc">BooleanColumn</span><span class="p">(</span><span class="s">"Some Heading"</span><span class="p">){</span><span class="n">item</span> <span class="p">-&gt;</span> <span class="n">item</span><span class="p">.</span><span class="nf">someBooleanProperty</span><span class="p">()}</span>
</code></pre></div></div>
<p>But, if the maintenance programmer has to click through into <code class="language-plaintext highlighter-rouge">BooleanColumn</code> to see how it works and how to configure some aspect of its display, then it gets harder, not easier, for them.</p>

<p>It’s really important to make that information readily available without forcing maintenance programmers to read the source code.  JavaDocs can be crucial here.  Let’s take a look at some JavaDocs (KDocs actually, because it’s Kotlin) for <code class="language-plaintext highlighter-rouge">BooleanColumn</code></p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/**
 * 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.
 */</span>
</code></pre></div></div>
<p>We have the standard stuff that explains the constructor parameters, of course.  But equally important is all the information that they’d need to do configuration in the layout, or to tailor the StyleSheet.</p>

<p>It looks like this in my IDE:</p>

<p><img src="/assets/elements/TableColumn11.png" alt="KDocs in Action" /></p>

<p>There’s probably more that I should add in, but you get the idea.</p>

<h1 id="conclusion">Conclusion</h1>

<p>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 <em>they</em> want.  Even more, <code class="language-plaintext highlighter-rouge">TableColumns</code> and <code class="language-plaintext highlighter-rouge">TableCells</code> are extremely generic and almost impossible to use out-of-the-box without doing some kind of customization.  So why not do that customization “right”, do it <em>completely</em>, and then just re-use it over and over again?</p>

<p>A key concept here is recognizing that <code class="language-plaintext highlighter-rouge">TableCells</code> that go with a certain kind of data go with <code class="language-plaintext highlighter-rouge">TableColumns</code> of the same kind of data.  The two are a matched pair.  Once you think of it this way, it’s fairly clear how to design a <code class="language-plaintext highlighter-rouge">TableColumn</code> such that it supports a matched <code class="language-plaintext highlighter-rouge">TableCell</code>.  And then, in your layout code, you simply don’t have to think about the <code class="language-plaintext highlighter-rouge">TableCells</code> at all because they just work.</p>

<p>The difference in your layout code between using a pre-built, custom, <code class="language-plaintext highlighter-rouge">TableColumn</code> and doing the configuration on the fly is huge.  I don’t think you can overestimate how much baggage that in-layout configuration brings with it.  Using a custom <code class="language-plaintext highlighter-rouge">TableColumn</code> eliminates all of that baggage and can make your <code class="language-plaintext highlighter-rouge">TableView</code> configuration absolutely trivial.</p>]]></content><author><name>Dave Barrett</name><email>pragmaticcoding8@gmail.com</email></author><category term="javafx" /><summary type="html"><![CDATA[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.]]></summary></entry><entry><title type="html">Should You Use FXML?</title><link href="https://www.pragmaticcoding.ca/javafx/fxml-or-not" rel="alternate" type="text/html" title="Should You Use FXML?" /><published>2025-03-10T17:00:00+00:00</published><updated>2025-03-10T17:00:00+00:00</updated><id>https://www.pragmaticcoding.ca/javafx/fxml-or-not</id><content type="html" xml:base="https://www.pragmaticcoding.ca/javafx/fxml-or-not"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>From time to time the question about, “To FXML or not to FXML” comes up on Reddit and other social media, and I’m know as being firmly in the “Not FXML” camp.  I’ve had a few people message me about this, so I thought it might be a good time to examine this topic in depth.</p>

<dl>
  <dt>Full disclosure:</dt>
  <dd>I haven’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.  While I’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’ve also seen tons of FXML when looking at other programmers’ projects on GitHub, and helping people with issues on-line.  I wouldn’t hesitate to say that I know more about FXML and SceneBuilder than the average JavaFX beginner or intermediate programmer.  I just don’t use it.</dd>
</dl>

<h1 id="true-or-false---claims-about-fxml">True or False - Claims About FXML</h1>

<p>There are a lot of claims about all of the wonderous benefits of using FXML.  Are they true?  Let’s take a look at them and see…</p>

<h2 id="separation-of-concerns">Separation of Concerns</h2>

<p>This is the claim you hear the most, from the Oracle tutorial for FXML we have this:</p>

<blockquote>
  <p>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</p>
</blockquote>

<p>There’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 “separation” has to speak to the relationship between these two elements.  So let’s concentrate on that…</p>

<p>When we talk about “Separation of Concerns”, we are really talking about “coupling”, 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?</p>

<p>Specifically, 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 “know” about each other?</p>

<p>Most FXML implementation that I see follow a basic pattern:</p>

<ul>
  <li>EventHandler subroutines in the FXML Controller are exposed to the FXML file.</li>
  <li>Named Nodes are exposed to the FXML Controller through @FXML annotations.</li>
</ul>

<p>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’t make critical changes to those subroutines which would impact the FXML file.</p>

<p>The other direction is tighter.  If you want to get data in or out of any Node on the screen, you’ll need a <em>typed</em> reference to it declared as a field in your FXML Controller.  This means that everything that isn’t purely static content in your GUI will need to be exposed to the FXML Controller.  You <em>have</em> to have each one of those <code class="language-plaintext highlighter-rouge">Nodes</code> in your FXML file, and they have to be the correct type of <code class="language-plaintext highlighter-rouge">Node</code> 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 <code class="language-plaintext highlighter-rouge">Nodes</code>.</p>

<p>Is there some way to remove all this coupling?  Obviously, no… But can we do anything to make it “one way” coupling?</p>

<p>It turns out that we can.  If we expose all of the elements that might generate <code class="language-plaintext highlighter-rouge">Events</code> that we want to handle in the FXML Controller to that Controller by giving them id’s in the FXML file, then we can configure them, including attaching <code class="language-plaintext highlighter-rouge">EventHandlers</code> to them, in the FXML Controller.  If you do this then you don’t have any references from the FXML file to the FXML Controller.</p>

<p>I haven’t looked at enough FXML systems to say definitively that this technique is <em>never</em> used, but… I’ve never seen it used.</p>

<p>What about in the other direction? Can we create an FXML Controller that doesn’t need any references to <code class="language-plaintext highlighter-rouge">Nodes</code> defined in the FXML file?</p>

<p>This turns out to be more complicated.  It is possible to define data elements as <code class="language-plaintext highlighter-rouge">Properties</code> 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.</p>

<p>Except that this won’t work.</p>

<p>FXML does not support bi-directional binding.  This is something that apparently has been a “top priority” for over a decade, and it’s still not been implemented.  Without bi-directional binding, you cannot update those <code class="language-plaintext highlighter-rouge">Properties</code> in the FXML Controller and you cannot get data out of your GUI.  So this is a dead end.</p>

<p>Finally, we haven’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.</p>

<p>Now, let’s go back to “Separation of Concerns”.  What separation do we have?</p>

<p>In some cases, we have separation of <em>most</em> 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 <code class="language-plaintext highlighter-rouge">Labels</code> are displayed in a <code class="language-plaintext highlighter-rouge">VBox</code> or an <code class="language-plaintext highlighter-rouge">HBox</code>, but if those <code class="language-plaintext highlighter-rouge">Labels</code> have dynamic content, then the FXML Controller needs to know that they are <code class="language-plaintext highlighter-rouge">Labels</code> and what data needs to be stuffed into them.</p>

<p class="notice--info">I highly doubt that this is the level of “separation” that programmers are looking for when they expect “Separation of Concerns”.</p>

<p>But that Oracle tutorial doesn’t even seem to be talking about this, it says:</p>

<blockquote>
  <p>provides the structure for building a user interface separate from the application logic of your code</p>
</blockquote>

<p>Application logic?  This leads to the next claim about the benefits of FXML…</p>

<h2 id="fxml-supports-mvc">FXML Supports MVC</h2>

<p>Also from that Oracle tutorial for FXML:</p>

<blockquote>
  <p>From a Model View Controller (MVC) perspective, the FXML file that contains the description of the user interface is the view.</p>
</blockquote>

<p>In MVC, the View isn’t just a layout, it’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.</p>

<p>You should understand that there are <code class="language-plaintext highlighter-rouge">Events</code> and there are “Actions”.  <code class="language-plaintext highlighter-rouge">Events</code> 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).  “Actions” are a much more generic concept and aren’t necessarily GUI elements at all.</p>

<p>I know that this sounds like nit-picking, but it’s an important practical consideration when talking about MVC.  Let’s look at what typically happens when you click a <code class="language-plaintext highlighter-rouge">Button</code> that requires some business logic to be run…</p>

<ol>
  <li>The first thing that happens is that the <code class="language-plaintext highlighter-rouge">Button</code> is disabled because you don’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 <code class="language-plaintext highlighter-rouge">Nodes</code>, starting up a <code class="language-plaintext highlighter-rouge">ProgressBar</code> or putting up a “Please Wait” message.</li>
  <li>The next thing that happens is the action is run, often in a background thread.</li>
  <li>Once the action completes, the Presentation Model is updated as required.</li>
  <li>Finally, the <code class="language-plaintext highlighter-rouge">Button</code> is re-enabled, the <code class="language-plaintext highlighter-rouge">ProgressBar</code> is removed, and the “Please Wait” message is cleared.</li>
</ol>

<p>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’ll have to write code in the FXML Controller!</p>

<p>What does this really mean?</p>

<p>As 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.</p>

<p>Once you look at it this way, everything else makes much more sense and you don’t have the cognitive dissonance that results from treating the FXML and FXML Controller as View and MVC Controller.</p>

<p>But you don’t automatically get an MVC Controller, you’ll have to deliberately create one.  We’ll look at this later.</p>

<p class="notice--warning">This is the most dangerous of all of the false claims about FXML.  That’s because, if you buy into this then you’ll actually architect your system incorrectly</p>

<h2 id="multiple-layouts-with-one-controller">Multiple Layouts With One Controller</h2>

<p>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’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 “create”, “update”, “read” and “delete”.</p>

<p>In practice, I’m not sure that anyone does this, or that it would be a good idea.  Essentially, you’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.</p>

<p>My sense is that trying to do this makes your application more complicated, not simpler.</p>

<h3 id="but-the-layout-is-separate-from-the-logic">But the Layout is Separate From the Logic!</h3>

<p>It’s important to recognize that “physically separate” and “uncoupled” are <strong>NOT</strong> 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.</p>

<p>So what is the advantage to having the layout separate from the logic when both are still tightly coupled?</p>

<p>I’m not sure that there is one.</p>

<p>I 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’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’s going a lot further than just building the GUI logic as I go.</p>

<h2 id="fxmls-declarative-syntax">FXML`s Declarative Syntax</h2>

<p>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.</p>

<p>I <em>think</em> that this means that it is supposed to be easy to look at an FXML file and understand what it’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.</p>

<p>Let’s see if this is true…</p>

<p>While I would prefer not to include a huge FXML file, I think we need to look at one to understand this issue.  So here’s the FXML file from <a href="https://github.com/trinity-xai/Trinity/blob/v2024.12.13/src/main/resources/edu/jhuapl/trinity/fxml/ManifoldControl.fxml">Trinity on GitHub</a>:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?import javafx.geometry.Insets?&gt;</span>
<span class="cp">&lt;?import javafx.scene.control.*?&gt;</span>
<span class="cp">&lt;?import javafx.scene.layout.*?&gt;</span>

<span class="nt">&lt;AnchorPane</span> <span class="na">fx:id=</span><span class="s">"root"</span> <span class="na">style=</span><span class="s">"-fx-background-color: #00000000;"</span> <span class="na">xmlns=</span><span class="s">"http://javafx.com/javafx"</span> <span class="na">xmlns:fx=</span><span class="s">"http://javafx.com/fxml"</span>
            <span class="na">fx:controller=</span><span class="s">"edu.jhuapl.trinity.javafx.controllers.ManifoldControlController"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;children&gt;</span>
        <span class="nt">&lt;TabPane</span> <span class="na">fx:id=</span><span class="s">"tabPane"</span> <span class="na">tabClosingPolicy=</span><span class="s">"UNAVAILABLE"</span><span class="nt">&gt;</span>
            <span class="nt">&lt;tabs&gt;</span>
                <span class="nt">&lt;Tab</span> <span class="na">closable=</span><span class="s">"false"</span> <span class="na">text=</span><span class="s">"UMAP"</span><span class="nt">&gt;</span>
                    <span class="nt">&lt;content&gt;</span>
                        <span class="nt">&lt;BorderPane</span> <span class="na">fx:id=</span><span class="s">"majorPane"</span> <span class="na">minHeight=</span><span class="s">"200.0"</span> <span class="na">minWidth=</span><span class="s">"400.0"</span><span class="nt">&gt;</span>
                            <span class="nt">&lt;children&gt;</span>
                            <span class="nt">&lt;/children&gt;</span>
                            <span class="nt">&lt;top&gt;</span>
                            <span class="nt">&lt;/top&gt;</span>
                            <span class="nt">&lt;center&gt;</span>
                                <span class="nt">&lt;GridPane</span> <span class="na">hgap=</span><span class="s">"10.0"</span> <span class="na">vgap=</span><span class="s">"5.0"</span> <span class="na">BorderPane.alignment=</span><span class="s">"CENTER"</span><span class="nt">&gt;</span>
                                    <span class="nt">&lt;columnConstraints&gt;</span>
                                        <span class="nt">&lt;ColumnConstraints</span> <span class="na">hgrow=</span><span class="s">"SOMETIMES"</span> <span class="na">maxWidth=</span><span class="s">"288.0"</span> <span class="na">minWidth=</span><span class="s">"10.0"</span> <span class="na">prefWidth=</span><span class="s">"206.0"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;ColumnConstraints</span> <span class="na">hgrow=</span><span class="s">"SOMETIMES"</span> <span class="na">maxWidth=</span><span class="s">"380.0"</span> <span class="na">minWidth=</span><span class="s">"10.0"</span> <span class="na">prefWidth=</span><span class="s">"380.0"</span><span class="nt">/&gt;</span>
                                    <span class="nt">&lt;/columnConstraints&gt;</span>
                                    <span class="nt">&lt;rowConstraints&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                    <span class="nt">&lt;/rowConstraints&gt;</span>
                                    <span class="nt">&lt;children&gt;</span>
                                        <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Number of Components"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Spinner</span> <span class="na">fx:id=</span><span class="s">"numComponentsSpinner"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefWidth=</span><span class="s">"100.0"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"1"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Number of Epochs"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"2"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Spinner</span> <span class="na">fx:id=</span><span class="s">"numEpochsSpinner"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefWidth=</span><span class="s">"100.0"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"3"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Nearest Neighbors"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"4"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Negative Sample Rate"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"6"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Local Connectivity"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"8"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Spinner</span> <span class="na">fx:id=</span><span class="s">"nearestNeighborsSpinner"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefWidth=</span><span class="s">"100.0"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"5"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Spinner</span> <span class="na">fx:id=</span><span class="s">"negativeSampleRateSpinner"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefWidth=</span><span class="s">"100.0"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"7"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Spinner</span> <span class="na">fx:id=</span><span class="s">"localConnectivitySpinner"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefWidth=</span><span class="s">"100.0"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"9"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;HBox</span> <span class="na">spacing=</span><span class="s">"10.0"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"10"</span> <span class="na">GridPane.rowSpan=</span><span class="s">"2"</span><span class="nt">&gt;</span>
                                            <span class="nt">&lt;children&gt;</span>
                                                <span class="nt">&lt;VBox</span> <span class="na">alignment=</span><span class="s">"CENTER_LEFT"</span> <span class="na">spacing=</span><span class="s">"10.0"</span><span class="nt">&gt;</span>
                                                    <span class="nt">&lt;children&gt;</span>
                                                        <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Distance Metric"</span><span class="nt">/&gt;</span>
                                                        <span class="nt">&lt;ChoiceBox</span> <span class="na">fx:id=</span><span class="s">"metricChoiceBox"</span> <span class="na">prefWidth=</span><span class="s">"200.0"</span><span class="nt">/&gt;</span>
                                                    <span class="nt">&lt;/children&gt;</span>
                                                <span class="nt">&lt;/VBox&gt;</span>
                                                <span class="nt">&lt;VBox</span> <span class="na">alignment=</span><span class="s">"CENTER_LEFT"</span> <span class="na">layoutX=</span><span class="s">"10.0"</span> <span class="na">layoutY=</span><span class="s">"10.0"</span> <span class="na">spacing=</span><span class="s">"10.0"</span><span class="nt">&gt;</span>
                                                    <span class="nt">&lt;children&gt;</span>
                                                        <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Threshold (if applicable)"</span><span class="nt">/&gt;</span>
                                                        <span class="nt">&lt;Spinner</span> <span class="na">fx:id=</span><span class="s">"thresholdSpinner"</span> <span class="na">prefWidth=</span><span class="s">"150.0"</span><span class="nt">&gt;</span>
                                                            <span class="nt">&lt;editable&gt;</span>true<span class="nt">&lt;/editable&gt;</span>
                                                            <span class="nt">&lt;valueFactory&gt;</span>
                                                                <span class="nt">&lt;SpinnerValueFactory.DoubleSpinnerValueFactory</span> <span class="na">min=</span><span class="s">"0.01"</span> <span class="na">max=</span><span class="s">"1.0"</span> <span class="na">initialValue=</span><span class="s">"0.1"</span>
                                                                                                               <span class="na">amountToStepBy=</span><span class="s">"0.01"</span><span class="nt">/&gt;</span>
                                                            <span class="nt">&lt;/valueFactory&gt;</span>
                                                        <span class="nt">&lt;/Spinner&gt;</span>
                                                    <span class="nt">&lt;/children&gt;</span>
                                                <span class="nt">&lt;/VBox&gt;</span>
                                            <span class="nt">&lt;/children&gt;</span>
                                            <span class="nt">&lt;padding&gt;</span>
                                                <span class="nt">&lt;Insets</span> <span class="na">top=</span><span class="s">"5.0"</span><span class="nt">/&gt;</span>
                                            <span class="nt">&lt;/padding&gt;</span>
                                        <span class="nt">&lt;/HBox&gt;</span>
                                        <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Repulsion Strength"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Minimum Distance"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"2"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Spread"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"4"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Op Mix Ratio"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"6"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Slider</span> <span class="na">fx:id=</span><span class="s">"repulsionSlider"</span> <span class="na">blockIncrement=</span><span class="s">"0.1"</span> <span class="na">majorTickUnit=</span><span class="s">"0.1"</span> <span class="na">max=</span><span class="s">"2.0"</span> <span class="na">showTickLabels=</span><span class="s">"true"</span>
                                                <span class="na">showTickMarks=</span><span class="s">"true"</span> <span class="na">snapToTicks=</span><span class="s">"true"</span> <span class="na">value=</span><span class="s">"1.0"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"1"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Slider</span> <span class="na">fx:id=</span><span class="s">"minDistanceSlider"</span> <span class="na">blockIncrement=</span><span class="s">"0.1"</span> <span class="na">majorTickUnit=</span><span class="s">"0.1"</span> <span class="na">max=</span><span class="s">"0.6"</span> <span class="na">showTickLabels=</span><span class="s">"true"</span>
                                                <span class="na">showTickMarks=</span><span class="s">"true"</span> <span class="na">snapToTicks=</span><span class="s">"true"</span> <span class="na">value=</span><span class="s">"0.1"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"3"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Slider</span> <span class="na">fx:id=</span><span class="s">"spreadSlider"</span> <span class="na">blockIncrement=</span><span class="s">"0.1"</span> <span class="na">majorTickUnit=</span><span class="s">"0.1"</span> <span class="na">max=</span><span class="s">"1.5"</span> <span class="na">min=</span><span class="s">"0.5"</span> <span class="na">showTickLabels=</span><span class="s">"true"</span>
                                                <span class="na">showTickMarks=</span><span class="s">"true"</span> <span class="na">snapToTicks=</span><span class="s">"true"</span> <span class="na">value=</span><span class="s">"1.0"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"5"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Slider</span> <span class="na">fx:id=</span><span class="s">"opMixSlider"</span> <span class="na">blockIncrement=</span><span class="s">"0.1"</span> <span class="na">majorTickUnit=</span><span class="s">"0.1"</span> <span class="na">max=</span><span class="s">"1.0"</span> <span class="na">showTickLabels=</span><span class="s">"true"</span>
                                                <span class="na">showTickMarks=</span><span class="s">"true"</span> <span class="na">snapToTicks=</span><span class="s">"true"</span> <span class="na">value=</span><span class="s">"0.5"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"7"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;HBox</span> <span class="na">alignment=</span><span class="s">"CENTER"</span> <span class="na">spacing=</span><span class="s">"15.0"</span> <span class="na">GridPane.columnSpan=</span><span class="s">"2147483647"</span> <span class="na">GridPane.halignment=</span><span class="s">"CENTER"</span>
                                              <span class="na">GridPane.rowIndex=</span><span class="s">"12"</span> <span class="na">GridPane.rowSpan=</span><span class="s">"2"</span> <span class="na">GridPane.valignment=</span><span class="s">"CENTER"</span><span class="nt">&gt;</span>
                                            <span class="nt">&lt;children&gt;</span>
                                                <span class="nt">&lt;VBox</span> <span class="na">alignment=</span><span class="s">"CENTER"</span> <span class="na">spacing=</span><span class="s">"10.0"</span><span class="nt">&gt;</span>
                                                    <span class="nt">&lt;children&gt;</span>
                                                        <span class="nt">&lt;Button</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">onAction=</span><span class="s">"#loadUmapConfig"</span> <span class="na">prefWidth=</span><span class="s">"200.0"</span> <span class="na">text=</span><span class="s">"Load New Config"</span><span class="nt">/&gt;</span>
                                                        <span class="nt">&lt;Button</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">onAction=</span><span class="s">"#saveUmapConfig"</span> <span class="na">prefWidth=</span><span class="s">"200.0"</span>
                                                                <span class="na">text=</span><span class="s">"Save Current Config"</span><span class="nt">/&gt;</span>
                                                    <span class="nt">&lt;/children&gt;</span>
                                                <span class="nt">&lt;/VBox&gt;</span>
                                                <span class="nt">&lt;VBox</span> <span class="na">alignment=</span><span class="s">"CENTER_LEFT"</span> <span class="na">spacing=</span><span class="s">"10.0"</span><span class="nt">&gt;</span>
                                                    <span class="nt">&lt;children&gt;</span>
                                                        <span class="nt">&lt;RadioButton</span> <span class="na">fx:id=</span><span class="s">"useHypersurfaceButton"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">text=</span><span class="s">"Use Hypersurface"</span><span class="nt">/&gt;</span>
                                                        <span class="nt">&lt;RadioButton</span> <span class="na">fx:id=</span><span class="s">"useHyperspaceButton"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">selected=</span><span class="s">"true"</span> <span class="na">text=</span><span class="s">"Use Hyperspace"</span><span class="nt">/&gt;</span>
                                                        <span class="nt">&lt;CheckBox</span> <span class="na">fx:id=</span><span class="s">"verboseCheckBox"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">selected=</span><span class="s">"true"</span> <span class="na">text=</span><span class="s">"Progress Output"</span><span class="nt">/&gt;</span>
                                                    <span class="nt">&lt;/children&gt;</span>
                                                    <span class="nt">&lt;padding&gt;</span>
                                                        <span class="nt">&lt;Insets</span> <span class="na">bottom=</span><span class="s">"5.0"</span> <span class="na">left=</span><span class="s">"5.0"</span> <span class="na">right=</span><span class="s">"5.0"</span> <span class="na">top=</span><span class="s">"5.0"</span><span class="nt">/&gt;</span>
                                                    <span class="nt">&lt;/padding&gt;</span>
                                                <span class="nt">&lt;/VBox&gt;</span>
                                                <span class="nt">&lt;VBox</span> <span class="na">alignment=</span><span class="s">"CENTER"</span> <span class="na">spacing=</span><span class="s">"10.0"</span><span class="nt">&gt;</span>
                                                    <span class="nt">&lt;children&gt;</span>
                                                        <span class="nt">&lt;Button</span> <span class="na">defaultButton=</span><span class="s">"true"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">onAction=</span><span class="s">"#project"</span> <span class="na">prefWidth=</span><span class="s">"200.0"</span>
                                                                <span class="na">text=</span><span class="s">"Run UMAP"</span><span class="nt">/&gt;</span>
                                                        <span class="nt">&lt;Button</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">onAction=</span><span class="s">"#exportMatrix"</span> <span class="na">prefWidth=</span><span class="s">"200.0"</span> <span class="na">text=</span><span class="s">"Export TMatrix"</span><span class="nt">/&gt;</span>
                                                        <span class="nt">&lt;Button</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">onAction=</span><span class="s">"#saveProjections"</span> <span class="na">prefWidth=</span><span class="s">"200.0"</span>
                                                                <span class="na">text=</span><span class="s">"Export Projections"</span><span class="nt">/&gt;</span>
                                                    <span class="nt">&lt;/children&gt;</span>
                                                <span class="nt">&lt;/VBox&gt;</span>
                                            <span class="nt">&lt;/children&gt;</span>
                                            <span class="nt">&lt;padding&gt;</span>
                                                <span class="nt">&lt;Insets</span> <span class="na">bottom=</span><span class="s">"2.0"</span> <span class="na">left=</span><span class="s">"2.0"</span> <span class="na">right=</span><span class="s">"2.0"</span> <span class="na">top=</span><span class="s">"2.0"</span><span class="nt">/&gt;</span>
                                            <span class="nt">&lt;/padding&gt;</span>
                                        <span class="nt">&lt;/HBox&gt;</span>
                                        <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Target Weight"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"8"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Slider</span> <span class="na">fx:id=</span><span class="s">"targetWeightSlider"</span> <span class="na">blockIncrement=</span><span class="s">"0.1"</span> <span class="na">majorTickUnit=</span><span class="s">"0.1"</span> <span class="na">max=</span><span class="s">"1.0"</span> <span class="na">showTickLabels=</span><span class="s">"true"</span>
                                                <span class="na">showTickMarks=</span><span class="s">"true"</span> <span class="na">snapToTicks=</span><span class="s">"true"</span> <span class="na">value=</span><span class="s">"0.5"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"9"</span><span class="nt">/&gt;</span>
                                    <span class="nt">&lt;/children&gt;</span>
                                    <span class="nt">&lt;padding&gt;</span>
                                        <span class="nt">&lt;Insets</span> <span class="na">bottom=</span><span class="s">"25.0"</span> <span class="na">left=</span><span class="s">"25.0"</span> <span class="na">right=</span><span class="s">"25.0"</span> <span class="na">top=</span><span class="s">"25.0"</span><span class="nt">/&gt;</span>
                                    <span class="nt">&lt;/padding&gt;</span>
                                <span class="nt">&lt;/GridPane&gt;</span>
                            <span class="nt">&lt;/center&gt;</span>
                        <span class="nt">&lt;/BorderPane&gt;</span>
                    <span class="nt">&lt;/content&gt;</span>
                <span class="nt">&lt;/Tab&gt;</span>
                <span class="nt">&lt;Tab</span> <span class="na">closable=</span><span class="s">"false"</span> <span class="na">text=</span><span class="s">"PCA"</span><span class="nt">&gt;</span>
                    <span class="nt">&lt;content&gt;</span>
                        <span class="nt">&lt;BorderPane</span> <span class="na">fx:id=</span><span class="s">"majorPane"</span> <span class="na">minHeight=</span><span class="s">"200.0"</span> <span class="na">minWidth=</span><span class="s">"400.0"</span><span class="nt">&gt;</span>
                            <span class="nt">&lt;children&gt;</span>
                            <span class="nt">&lt;/children&gt;</span>
                            <span class="nt">&lt;top&gt;</span>
                            <span class="nt">&lt;/top&gt;</span>
                            <span class="nt">&lt;center&gt;</span>
                                <span class="nt">&lt;GridPane</span> <span class="na">hgap=</span><span class="s">"10.0"</span> <span class="na">vgap=</span><span class="s">"5.0"</span> <span class="na">BorderPane.alignment=</span><span class="s">"CENTER"</span><span class="nt">&gt;</span>
                                    <span class="nt">&lt;columnConstraints&gt;</span>
                                        <span class="nt">&lt;ColumnConstraints</span> <span class="na">hgrow=</span><span class="s">"SOMETIMES"</span> <span class="na">maxWidth=</span><span class="s">"288.0"</span> <span class="na">minWidth=</span><span class="s">"10.0"</span> <span class="na">prefWidth=</span><span class="s">"200.0"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;ColumnConstraints</span> <span class="na">hgrow=</span><span class="s">"SOMETIMES"</span> <span class="na">maxWidth=</span><span class="s">"380.0"</span> <span class="na">minWidth=</span><span class="s">"10.0"</span> <span class="na">prefWidth=</span><span class="s">"200.0"</span><span class="nt">/&gt;</span>
                                    <span class="nt">&lt;/columnConstraints&gt;</span>
                                    <span class="nt">&lt;rowConstraints&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                    <span class="nt">&lt;/rowConstraints&gt;</span>
                                    <span class="nt">&lt;children&gt;</span>
                                        <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Number of Components"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Spinner</span> <span class="na">fx:id=</span><span class="s">"numPcaComponentsSpinner"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefWidth=</span><span class="s">"100.0"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"1"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;HBox</span> <span class="na">spacing=</span><span class="s">"20.0"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"3"</span> <span class="na">GridPane.rowSpan=</span><span class="s">"2"</span><span class="nt">&gt;</span>
                                            <span class="nt">&lt;children&gt;</span>
                                                <span class="nt">&lt;VBox</span> <span class="na">spacing=</span><span class="s">"15.0"</span><span class="nt">&gt;</span>
                                                    <span class="nt">&lt;children&gt;</span>
                                                        <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Fit Start Index"</span><span class="nt">/&gt;</span>
                                                        <span class="nt">&lt;Spinner</span> <span class="na">fx:id=</span><span class="s">"fitStartIndexSpinner"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefWidth=</span><span class="s">"100.0"</span><span class="nt">/&gt;</span>
                                                    <span class="nt">&lt;/children&gt;</span>
                                                <span class="nt">&lt;/VBox&gt;</span>
                                                <span class="nt">&lt;VBox</span> <span class="na">spacing=</span><span class="s">"15.0"</span><span class="nt">&gt;</span>
                                                    <span class="nt">&lt;children&gt;</span>
                                                        <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Fit End Index"</span><span class="nt">/&gt;</span>
                                                        <span class="nt">&lt;Spinner</span> <span class="na">fx:id=</span><span class="s">"fitEndIndexSpinner"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefWidth=</span><span class="s">"100.0"</span><span class="nt">/&gt;</span>
                                                    <span class="nt">&lt;/children&gt;</span>
                                                <span class="nt">&lt;/VBox&gt;</span>
                                            <span class="nt">&lt;/children&gt;</span>
                                        <span class="nt">&lt;/HBox&gt;</span>
                                        <span class="nt">&lt;RadioButton</span> <span class="na">fx:id=</span><span class="s">"pcaRadioButton"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">selected=</span><span class="s">"true"</span> <span class="na">text=</span><span class="s">"PCA (EigenValue)"</span>
                                                     <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"1"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Component Analysis Type"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RadioButton</span> <span class="na">fx:id=</span><span class="s">"svdRadioButton"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">text=</span><span class="s">"Singular Value Decomposition"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span>
                                                     <span class="na">GridPane.rowIndex=</span><span class="s">"2"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RadioButton</span> <span class="na">fx:id=</span><span class="s">"pcaUseHypersurfaceButton"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">text=</span><span class="s">"Use Hypersurface"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span>
                                                     <span class="na">GridPane.rowIndex=</span><span class="s">"4"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RadioButton</span> <span class="na">fx:id=</span><span class="s">"pcaUseHyperspaceButton"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">selected=</span><span class="s">"true"</span> <span class="na">text=</span><span class="s">"Use Hyperspace"</span>
                                                     <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"5"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Output Scaling Factor"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"5"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;Spinner</span> <span class="na">fx:id=</span><span class="s">"pcaScalingSpinner"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefWidth=</span><span class="s">"100.0"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"6"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;HBox</span> <span class="na">alignment=</span><span class="s">"CENTER"</span> <span class="na">spacing=</span><span class="s">"10.0"</span> <span class="na">GridPane.columnSpan=</span><span class="s">"2147483647"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"8"</span><span class="nt">&gt;</span>
                                            <span class="nt">&lt;children&gt;</span>
                                                <span class="nt">&lt;Button</span> <span class="na">defaultButton=</span><span class="s">"true"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">onAction=</span><span class="s">"#runPCA"</span> <span class="na">prefWidth=</span><span class="s">"175.0"</span> <span class="na">text=</span><span class="s">"Project Data"</span><span class="nt">/&gt;</span>
                                                <span class="nt">&lt;Button</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">onAction=</span><span class="s">"#saveProjections"</span> <span class="na">prefWidth=</span><span class="s">"175.0"</span> <span class="na">text=</span><span class="s">"Export Projections"</span><span class="nt">/&gt;</span>
                                            <span class="nt">&lt;/children&gt;</span>
                                            <span class="nt">&lt;padding&gt;</span>
                                                <span class="nt">&lt;Insets</span> <span class="na">bottom=</span><span class="s">"5.0"</span> <span class="na">left=</span><span class="s">"5.0"</span> <span class="na">right=</span><span class="s">"5.0"</span> <span class="na">top=</span><span class="s">"5.0"</span><span class="nt">/&gt;</span>
                                            <span class="nt">&lt;/padding&gt;</span>
                                        <span class="nt">&lt;/HBox&gt;</span>
                                        <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Input Data Source"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"3"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;CheckBox</span> <span class="na">fx:id=</span><span class="s">"rangedFittingCheckBox"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">text=</span><span class="s">"Enabled Ranged Fitting (Experimental)"</span>
                                                  <span class="na">GridPane.rowIndex=</span><span class="s">"2"</span><span class="nt">/&gt;</span>
                                    <span class="nt">&lt;/children&gt;</span>
                                    <span class="nt">&lt;padding&gt;</span>
                                        <span class="nt">&lt;Insets</span> <span class="na">bottom=</span><span class="s">"15.0"</span> <span class="na">left=</span><span class="s">"15.0"</span> <span class="na">right=</span><span class="s">"15.0"</span> <span class="na">top=</span><span class="s">"15.0"</span><span class="nt">/&gt;</span>
                                    <span class="nt">&lt;/padding&gt;</span>
                                <span class="nt">&lt;/GridPane&gt;</span>
                            <span class="nt">&lt;/center&gt;</span>
                        <span class="nt">&lt;/BorderPane&gt;</span>
                    <span class="nt">&lt;/content&gt;</span>
                <span class="nt">&lt;/Tab&gt;</span>
                <span class="nt">&lt;Tab</span> <span class="na">closable=</span><span class="s">"false"</span> <span class="na">text=</span><span class="s">"Distances"</span><span class="nt">&gt;</span>
                    <span class="nt">&lt;content&gt;</span>
                        <span class="nt">&lt;BorderPane</span> <span class="na">fx:id=</span><span class="s">"majorPane"</span> <span class="na">minHeight=</span><span class="s">"200.0"</span> <span class="na">minWidth=</span><span class="s">"400.0"</span><span class="nt">&gt;</span>
                            <span class="nt">&lt;children&gt;</span>
                            <span class="nt">&lt;/children&gt;</span>
                            <span class="nt">&lt;top&gt;</span>
                            <span class="nt">&lt;/top&gt;</span>
                            <span class="nt">&lt;center&gt;</span>
                                <span class="nt">&lt;GridPane</span> <span class="na">hgap=</span><span class="s">"10.0"</span> <span class="na">vgap=</span><span class="s">"5.0"</span> <span class="na">BorderPane.alignment=</span><span class="s">"CENTER"</span><span class="nt">&gt;</span>
                                    <span class="nt">&lt;columnConstraints&gt;</span>
                                        <span class="nt">&lt;ColumnConstraints</span> <span class="na">hgrow=</span><span class="s">"SOMETIMES"</span> <span class="na">maxWidth=</span><span class="s">"288.0"</span> <span class="na">minWidth=</span><span class="s">"10.0"</span> <span class="na">prefWidth=</span><span class="s">"206.0"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;ColumnConstraints</span> <span class="na">fillWidth=</span><span class="s">"false"</span> <span class="na">hgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;ColumnConstraints</span> <span class="na">hgrow=</span><span class="s">"SOMETIMES"</span> <span class="na">maxWidth=</span><span class="s">"380.0"</span> <span class="na">minWidth=</span><span class="s">"10.0"</span> <span class="na">prefWidth=</span><span class="s">"380.0"</span><span class="nt">/&gt;</span>
                                    <span class="nt">&lt;/columnConstraints&gt;</span>
                                    <span class="nt">&lt;rowConstraints&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
                                    <span class="nt">&lt;/rowConstraints&gt;</span>
                                    <span class="nt">&lt;children&gt;</span>
                                        <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Connector Thickness"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"5"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;VBox</span> <span class="na">alignment=</span><span class="s">"CENTER_LEFT"</span> <span class="na">spacing=</span><span class="s">"5.0"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"7"</span> <span class="na">GridPane.rowSpan=</span><span class="s">"2"</span><span class="nt">&gt;</span>
                                            <span class="nt">&lt;children&gt;</span>
                                                <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Connector Color"</span><span class="nt">/&gt;</span>
                                                <span class="nt">&lt;ColorPicker</span> <span class="na">fx:id=</span><span class="s">"connectorColorPicker"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">minHeight=</span><span class="s">"40.0"</span> <span class="na">prefWidth=</span><span class="s">"200.0"</span>
                                                             <span class="na">promptText=</span><span class="s">"Change the color of the 3D connector"</span><span class="nt">/&gt;</span>
                                            <span class="nt">&lt;/children&gt;</span>
                                            <span class="nt">&lt;padding&gt;</span>
                                                <span class="nt">&lt;Insets</span> <span class="na">bottom=</span><span class="s">"5.0"</span> <span class="na">left=</span><span class="s">"5.0"</span> <span class="na">right=</span><span class="s">"5.0"</span> <span class="na">top=</span><span class="s">"5.0"</span><span class="nt">/&gt;</span>
                                            <span class="nt">&lt;/padding&gt;</span>
                                        <span class="nt">&lt;/VBox&gt;</span>
                                        <span class="nt">&lt;Spinner</span> <span class="na">fx:id=</span><span class="s">"connectorThicknessSpinner"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefWidth=</span><span class="s">"100.0"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"6"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;HBox</span> <span class="na">alignment=</span><span class="s">"CENTER"</span> <span class="na">spacing=</span><span class="s">"20.0"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"2"</span><span class="nt">&gt;</span>
                                            <span class="nt">&lt;children&gt;</span>
                                                <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Collected Distances"</span><span class="nt">/&gt;</span>
                                                <span class="nt">&lt;Button</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">onAction=</span><span class="s">"#clearAllDistances"</span> <span class="na">prefWidth=</span><span class="s">"150.0"</span> <span class="na">text=</span><span class="s">"Clear All"</span><span class="nt">/&gt;</span>
                                            <span class="nt">&lt;/children&gt;</span>
                                        <span class="nt">&lt;/HBox&gt;</span>
                                        <span class="nt">&lt;VBox</span> <span class="na">spacing=</span><span class="s">"5.0"</span> <span class="na">GridPane.rowSpan=</span><span class="s">"2"</span><span class="nt">&gt;</span>
                                            <span class="nt">&lt;children&gt;</span>
                                                <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Distance Metric"</span><span class="nt">/&gt;</span>
                                                <span class="nt">&lt;TextField</span> <span class="na">fx:id=</span><span class="s">"distanceMetricTextField"</span> <span class="na">minHeight=</span><span class="s">"40.0"</span> <span class="na">promptText=</span><span class="s">"Select Distance From ListView"</span><span class="nt">/&gt;</span>
                                            <span class="nt">&lt;/children&gt;</span>
                                            <span class="nt">&lt;padding&gt;</span>
                                                <span class="nt">&lt;Insets</span> <span class="na">bottom=</span><span class="s">"5.0"</span> <span class="na">left=</span><span class="s">"5.0"</span> <span class="na">right=</span><span class="s">"5.0"</span> <span class="na">top=</span><span class="s">"5.0"</span><span class="nt">/&gt;</span>
                                            <span class="nt">&lt;/padding&gt;</span>
                                        <span class="nt">&lt;/VBox&gt;</span>
                                        <span class="nt">&lt;ScrollPane</span> <span class="na">fitToHeight=</span><span class="s">"true"</span> <span class="na">fitToWidth=</span><span class="s">"true"</span> <span class="na">pannable=</span><span class="s">"true"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"2"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"1"</span>
                                                    <span class="na">GridPane.rowSpan=</span><span class="s">"2147483647"</span><span class="nt">&gt;</span>
                                            <span class="nt">&lt;content&gt;</span>
                                                <span class="nt">&lt;ListView</span> <span class="na">fx:id=</span><span class="s">"distancesListView"</span> <span class="na">prefHeight=</span><span class="s">"200.0"</span> <span class="na">prefWidth=</span><span class="s">"200.0"</span><span class="nt">&gt;</span>
                                                    <span class="nt">&lt;padding&gt;</span>
                                                        <span class="nt">&lt;Insets</span> <span class="na">bottom=</span><span class="s">"5.0"</span> <span class="na">left=</span><span class="s">"5.0"</span> <span class="na">right=</span><span class="s">"5.0"</span> <span class="na">top=</span><span class="s">"5.0"</span><span class="nt">/&gt;</span>
                                                    <span class="nt">&lt;/padding&gt;</span>
                                                <span class="nt">&lt;/ListView&gt;</span>
                                            <span class="nt">&lt;/content&gt;</span>
                                        <span class="nt">&lt;/ScrollPane&gt;</span>
                                        <span class="nt">&lt;Separator</span> <span class="na">orientation=</span><span class="s">"VERTICAL"</span> <span class="na">prefHeight=</span><span class="s">"200.0"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span> <span class="na">GridPane.rowSpan=</span><span class="s">"2147483647"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RadioButton</span> <span class="na">fx:id=</span><span class="s">"pointToGroupRadioButton"</span> <span class="na">disable=</span><span class="s">"true"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">text=</span><span class="s">"Point to Group"</span>
                                                     <span class="na">GridPane.rowIndex=</span><span class="s">"3"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;RadioButton</span> <span class="na">fx:id=</span><span class="s">"pointToPointRadioButton"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">selected=</span><span class="s">"true"</span> <span class="na">text=</span><span class="s">"Point to Point"</span>
                                                     <span class="na">GridPane.rowIndex=</span><span class="s">"2"</span><span class="nt">/&gt;</span>
                                    <span class="nt">&lt;/children&gt;</span>
                                    <span class="nt">&lt;padding&gt;</span>
                                        <span class="nt">&lt;Insets</span> <span class="na">bottom=</span><span class="s">"10.0"</span> <span class="na">left=</span><span class="s">"10.0"</span> <span class="na">right=</span><span class="s">"10.0"</span> <span class="na">top=</span><span class="s">"10.0"</span><span class="nt">/&gt;</span>
                                    <span class="nt">&lt;/padding&gt;</span>
                                <span class="nt">&lt;/GridPane&gt;</span>
                            <span class="nt">&lt;/center&gt;</span>
                        <span class="nt">&lt;/BorderPane&gt;</span>
                    <span class="nt">&lt;/content&gt;</span>
                <span class="nt">&lt;/Tab&gt;</span>
                <span class="nt">&lt;Tab</span> <span class="na">closable=</span><span class="s">"false"</span> <span class="na">text=</span><span class="s">"Hull Geometry"</span><span class="nt">&gt;</span>
                    <span class="nt">&lt;content&gt;</span>
                        <span class="nt">&lt;BorderPane&gt;</span>
                            <span class="nt">&lt;center&gt;</span>
                                <span class="nt">&lt;VBox</span> <span class="na">spacing=</span><span class="s">"5.0"</span> <span class="na">BorderPane.alignment=</span><span class="s">"CENTER"</span><span class="nt">&gt;</span>
                                    <span class="nt">&lt;children&gt;</span>
                                        <span class="nt">&lt;HBox</span> <span class="na">alignment=</span><span class="s">"CENTER_LEFT"</span> <span class="na">spacing=</span><span class="s">"20.0"</span><span class="nt">&gt;</span>
                                            <span class="nt">&lt;children&gt;</span>
                                                <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Generated Manifolds"</span><span class="nt">/&gt;</span>
                                            <span class="nt">&lt;/children&gt;</span>
                                        <span class="nt">&lt;/HBox&gt;</span>
                                        <span class="nt">&lt;HBox</span> <span class="na">alignment=</span><span class="s">"CENTER"</span> <span class="na">layoutX=</span><span class="s">"15.0"</span> <span class="na">layoutY=</span><span class="s">"15.0"</span> <span class="na">spacing=</span><span class="s">"20.0"</span><span class="nt">&gt;</span>
                                            <span class="nt">&lt;children&gt;</span>
                                                <span class="nt">&lt;Button</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">onAction=</span><span class="s">"#clearAll"</span> <span class="na">prefWidth=</span><span class="s">"125.0"</span> <span class="na">text=</span><span class="s">"Clear All"</span><span class="nt">/&gt;</span>
                                                <span class="nt">&lt;Button</span> <span class="na">layoutX=</span><span class="s">"100.0"</span> <span class="na">layoutY=</span><span class="s">"10.0"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">onAction=</span><span class="s">"#exportAll"</span> <span class="na">prefWidth=</span><span class="s">"125.0"</span>
                                                        <span class="na">text=</span><span class="s">"Export All"</span><span class="nt">/&gt;</span>
                                            <span class="nt">&lt;/children&gt;</span>
                                        <span class="nt">&lt;/HBox&gt;</span>
                                        <span class="nt">&lt;ScrollPane</span> <span class="na">fitToHeight=</span><span class="s">"true"</span> <span class="na">fitToWidth=</span><span class="s">"true"</span> <span class="na">pannable=</span><span class="s">"true"</span><span class="nt">&gt;</span>
                                            <span class="nt">&lt;content&gt;</span>
                                                <span class="nt">&lt;ListView</span> <span class="na">fx:id=</span><span class="s">"manifoldsListView"</span><span class="nt">&gt;</span>
                                                    <span class="nt">&lt;padding&gt;</span>
                                                        <span class="nt">&lt;Insets</span> <span class="na">bottom=</span><span class="s">"5.0"</span> <span class="na">left=</span><span class="s">"5.0"</span> <span class="na">right=</span><span class="s">"5.0"</span> <span class="na">top=</span><span class="s">"5.0"</span><span class="nt">/&gt;</span>
                                                    <span class="nt">&lt;/padding&gt;</span>
                                                <span class="nt">&lt;/ListView&gt;</span>
                                            <span class="nt">&lt;/content&gt;</span>
                                        <span class="nt">&lt;/ScrollPane&gt;</span>
                                    <span class="nt">&lt;/children&gt;</span>
                                    <span class="nt">&lt;padding&gt;</span>
                                        <span class="nt">&lt;Insets</span> <span class="na">bottom=</span><span class="s">"5.0"</span> <span class="na">left=</span><span class="s">"5.0"</span> <span class="na">right=</span><span class="s">"5.0"</span> <span class="na">top=</span><span class="s">"5.0"</span><span class="nt">/&gt;</span>
                                    <span class="nt">&lt;/padding&gt;</span>
                                <span class="nt">&lt;/VBox&gt;</span>
                            <span class="nt">&lt;/center&gt;</span>
                            <span class="nt">&lt;top&gt;</span>
                                <span class="nt">&lt;HBox</span> <span class="na">alignment=</span><span class="s">"CENTER"</span> <span class="na">BorderPane.alignment=</span><span class="s">"CENTER"</span><span class="nt">&gt;</span>
                                    <span class="nt">&lt;children&gt;</span>
                                        <span class="nt">&lt;GridPane</span> <span class="na">maxHeight=</span><span class="s">"1.7976931348623157E308"</span> <span class="na">maxWidth=</span><span class="s">"1.7976931348623157E308"</span><span class="nt">&gt;</span>
                                            <span class="nt">&lt;columnConstraints&gt;</span>
                                                <span class="nt">&lt;ColumnConstraints</span> <span class="na">halignment=</span><span class="s">"CENTER"</span> <span class="na">hgrow=</span><span class="s">"ALWAYS"</span> <span class="na">maxWidth=</span><span class="s">"-Infinity"</span> <span class="na">minWidth=</span><span class="s">"10.0"</span> <span class="na">prefWidth=</span><span class="s">"150.0"</span><span class="nt">/&gt;</span>
                                                <span class="nt">&lt;ColumnConstraints</span> <span class="na">halignment=</span><span class="s">"CENTER"</span> <span class="na">hgrow=</span><span class="s">"ALWAYS"</span> <span class="na">maxWidth=</span><span class="s">"216.0"</span> <span class="na">minWidth=</span><span class="s">"10.0"</span> <span class="na">prefWidth=</span><span class="s">"214.0"</span><span class="nt">/&gt;</span>
                                                <span class="nt">&lt;ColumnConstraints</span> <span class="na">halignment=</span><span class="s">"CENTER"</span> <span class="na">hgrow=</span><span class="s">"ALWAYS"</span> <span class="na">maxWidth=</span><span class="s">"145.0"</span> <span class="na">minWidth=</span><span class="s">"10.0"</span> <span class="na">prefWidth=</span><span class="s">"118.0"</span><span class="nt">/&gt;</span>
                                                <span class="nt">&lt;ColumnConstraints</span> <span class="na">halignment=</span><span class="s">"CENTER"</span> <span class="na">hgrow=</span><span class="s">"ALWAYS"</span> <span class="na">maxWidth=</span><span class="s">"145.0"</span> <span class="na">minWidth=</span><span class="s">"10.0"</span> <span class="na">prefWidth=</span><span class="s">"118.0"</span><span class="nt">/&gt;</span>
                                            <span class="nt">&lt;/columnConstraints&gt;</span>
                                            <span class="nt">&lt;rowConstraints&gt;</span>
                                                <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"ALWAYS"</span><span class="nt">/&gt;</span>
                                                <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"40.0"</span> <span class="na">vgrow=</span><span class="s">"ALWAYS"</span><span class="nt">/&gt;</span>
                                                <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"40.0"</span> <span class="na">vgrow=</span><span class="s">"ALWAYS"</span><span class="nt">/&gt;</span>
                                                <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">vgrow=</span><span class="s">"ALWAYS"</span><span class="nt">/&gt;</span>
                                            <span class="nt">&lt;/rowConstraints&gt;</span>
                                            <span class="nt">&lt;children&gt;</span>
                                                <span class="nt">&lt;Label</span> <span class="na">layoutX=</span><span class="s">"15.0"</span> <span class="na">layoutY=</span><span class="s">"62.0"</span> <span class="na">text=</span><span class="s">"Point Set"</span><span class="nt">/&gt;</span>
                                                <span class="nt">&lt;HBox</span> <span class="na">alignment=</span><span class="s">"CENTER"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span> <span class="na">GridPane.valignment=</span><span class="s">"CENTER"</span><span class="nt">&gt;</span>
                                                    <span class="nt">&lt;children&gt;</span>
                                                        <span class="nt">&lt;RadioButton</span> <span class="na">fx:id=</span><span class="s">"useVisibleRadioButton"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">prefWidth=</span><span class="s">"75.0"</span> <span class="na">selected=</span><span class="s">"true"</span>
                                                                     <span class="na">text=</span><span class="s">"Visible"</span><span class="nt">/&gt;</span>
                                                        <span class="nt">&lt;RadioButton</span> <span class="na">fx:id=</span><span class="s">"useAllRadioButton"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">prefWidth=</span><span class="s">"75.0"</span> <span class="na">text=</span><span class="s">"All"</span><span class="nt">/&gt;</span>
                                                    <span class="nt">&lt;/children&gt;</span>
                                                <span class="nt">&lt;/HBox&gt;</span>
                                                <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Distance Tolerance"</span> <span class="na">textAlignment=</span><span class="s">"CENTER"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"1"</span><span class="nt">/&gt;</span>
                                                <span class="nt">&lt;HBox</span> <span class="na">alignment=</span><span class="s">"CENTER"</span> <span class="na">spacing=</span><span class="s">"10.0"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"1"</span><span class="nt">&gt;</span>
                                                    <span class="nt">&lt;children&gt;</span>
                                                        <span class="nt">&lt;CheckBox</span> <span class="na">fx:id=</span><span class="s">"automaticCheckBox"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">selected=</span><span class="s">"true"</span> <span class="na">text=</span><span class="s">"Auto"</span><span class="nt">/&gt;</span>
                                                        <span class="nt">&lt;Spinner</span> <span class="na">fx:id=</span><span class="s">"manualSpinner"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefWidth=</span><span class="s">"75.0"</span><span class="nt">/&gt;</span>
                                                    <span class="nt">&lt;/children&gt;</span>
                                                <span class="nt">&lt;/HBox&gt;</span>
                                                <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Find by Label"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"2"</span><span class="nt">/&gt;</span>
                                                <span class="nt">&lt;ChoiceBox</span> <span class="na">fx:id=</span><span class="s">"labelChoiceBox"</span> <span class="na">maxWidth=</span><span class="s">"200.0"</span> <span class="na">prefWidth=</span><span class="s">"150.0"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"1"</span>
                                                           <span class="na">GridPane.rowIndex=</span><span class="s">"2"</span><span class="nt">/&gt;</span>
                                                <span class="nt">&lt;Separator</span> <span class="na">prefWidth=</span><span class="s">"200.0"</span> <span class="na">GridPane.columnSpan=</span><span class="s">"2147483647"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"3"</span><span class="nt">/&gt;</span>
                                                <span class="nt">&lt;VBox</span> <span class="na">alignment=</span><span class="s">"CENTER"</span> <span class="na">spacing=</span><span class="s">"20.0"</span> <span class="na">GridPane.columnIndex=</span><span class="s">"2"</span> <span class="na">GridPane.columnSpan=</span><span class="s">"2"</span>
                                                      <span class="na">GridPane.halignment=</span><span class="s">"CENTER"</span> <span class="na">GridPane.rowSpan=</span><span class="s">"3"</span> <span class="na">GridPane.valignment=</span><span class="s">"CENTER"</span><span class="nt">&gt;</span>
                                                    <span class="nt">&lt;children&gt;</span>
                                                        <span class="nt">&lt;Button</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">onAction=</span><span class="s">"#generate"</span> <span class="na">prefWidth=</span><span class="s">"175.0"</span> <span class="na">text=</span><span class="s">"Generate"</span><span class="nt">/&gt;</span>
                                                        <span class="nt">&lt;Button</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">onAction=</span><span class="s">"#clusterBuilder"</span> <span class="na">prefWidth=</span><span class="s">"175.0"</span> <span class="na">text=</span><span class="s">"Cluster Tools"</span><span class="nt">/&gt;</span>
                                                    <span class="nt">&lt;/children&gt;</span>
                                                    <span class="nt">&lt;padding&gt;</span>
                                                        <span class="nt">&lt;Insets</span> <span class="na">bottom=</span><span class="s">"10.0"</span> <span class="na">left=</span><span class="s">"10.0"</span> <span class="na">right=</span><span class="s">"10.0"</span> <span class="na">top=</span><span class="s">"10.0"</span><span class="nt">/&gt;</span>
                                                    <span class="nt">&lt;/padding&gt;</span>
                                                <span class="nt">&lt;/VBox&gt;</span>
                                            <span class="nt">&lt;/children&gt;</span>
                                        <span class="nt">&lt;/GridPane&gt;</span>
                                    <span class="nt">&lt;/children&gt;</span>
                                <span class="nt">&lt;/HBox&gt;</span>
                            <span class="nt">&lt;/top&gt;</span>
                            <span class="nt">&lt;left&gt;</span>
                                <span class="nt">&lt;VBox</span> <span class="na">spacing=</span><span class="s">"10.0"</span> <span class="na">BorderPane.alignment=</span><span class="s">"CENTER"</span><span class="nt">&gt;</span>
                                    <span class="nt">&lt;children&gt;</span>
                                        <span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Selected Manifold Properties"</span><span class="nt">/&gt;</span>
                                        <span class="nt">&lt;TitledPane</span> <span class="na">collapsible=</span><span class="s">"false"</span> <span class="na">text=</span><span class="s">"Material"</span> <span class="na">VBox.vgrow=</span><span class="s">"ALWAYS"</span><span class="nt">&gt;</span>
                                            <span class="nt">&lt;content&gt;</span>
                                                <span class="nt">&lt;VBox</span> <span class="na">spacing=</span><span class="s">"5.0"</span><span class="nt">&gt;</span>
                                                    <span class="nt">&lt;children&gt;</span>
                                                        <span class="nt">&lt;HBox</span> <span class="na">alignment=</span><span class="s">"CENTER_LEFT"</span> <span class="na">spacing=</span><span class="s">"10.0"</span><span class="nt">&gt;</span>
                                                            <span class="nt">&lt;children&gt;</span>
                                                                <span class="nt">&lt;Label</span> <span class="na">prefWidth=</span><span class="s">"125.0"</span> <span class="na">text=</span><span class="s">"Diffuse Color"</span><span class="nt">/&gt;</span>
                                                                <span class="nt">&lt;ColorPicker</span> <span class="na">fx:id=</span><span class="s">"manifoldDiffuseColorPicker"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefHeight=</span><span class="s">"50.0"</span>
                                                                             <span class="na">prefWidth=</span><span class="s">"150.0"</span><span class="nt">/&gt;</span>
                                                            <span class="nt">&lt;/children&gt;</span>
                                                        <span class="nt">&lt;/HBox&gt;</span>
                                                        <span class="nt">&lt;HBox</span> <span class="na">alignment=</span><span class="s">"CENTER_LEFT"</span> <span class="na">spacing=</span><span class="s">"10.0"</span><span class="nt">&gt;</span>
                                                            <span class="nt">&lt;children&gt;</span>
                                                                <span class="nt">&lt;Label</span> <span class="na">prefWidth=</span><span class="s">"125.0"</span> <span class="na">text=</span><span class="s">"Wire Mesh Color"</span><span class="nt">/&gt;</span>
                                                                <span class="nt">&lt;ColorPicker</span> <span class="na">fx:id=</span><span class="s">"manifoldWireMeshColorPicker"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefHeight=</span><span class="s">"50.0"</span>
                                                                             <span class="na">prefWidth=</span><span class="s">"150.0"</span><span class="nt">/&gt;</span>
                                                            <span class="nt">&lt;/children&gt;</span>
                                                        <span class="nt">&lt;/HBox&gt;</span>
                                                        <span class="nt">&lt;HBox</span> <span class="na">alignment=</span><span class="s">"CENTER_LEFT"</span> <span class="na">spacing=</span><span class="s">"10.0"</span><span class="nt">&gt;</span>
                                                            <span class="nt">&lt;children&gt;</span>
                                                                <span class="nt">&lt;Label</span> <span class="na">prefWidth=</span><span class="s">"125.0"</span> <span class="na">text=</span><span class="s">"Specular Color"</span><span class="nt">/&gt;</span>
                                                                <span class="nt">&lt;ColorPicker</span> <span class="na">fx:id=</span><span class="s">"manifoldSpecularColorPicker"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefHeight=</span><span class="s">"50.0"</span>
                                                                             <span class="na">prefWidth=</span><span class="s">"150.0"</span><span class="nt">/&gt;</span>
                                                            <span class="nt">&lt;/children&gt;</span>
                                                        <span class="nt">&lt;/HBox&gt;</span>
                                                    <span class="nt">&lt;/children&gt;</span>
                                                <span class="nt">&lt;/VBox&gt;</span>
                                            <span class="nt">&lt;/content&gt;</span>
                                        <span class="nt">&lt;/TitledPane&gt;</span>
                                        <span class="nt">&lt;TitledPane</span> <span class="na">collapsible=</span><span class="s">"false"</span> <span class="na">layoutX=</span><span class="s">"10.0"</span> <span class="na">layoutY=</span><span class="s">"396.0"</span> <span class="na">text=</span><span class="s">"MeshView"</span> <span class="na">VBox.vgrow=</span><span class="s">"ALWAYS"</span><span class="nt">&gt;</span>
                                            <span class="nt">&lt;content&gt;</span>
                                                <span class="nt">&lt;GridPane&gt;</span>
                                                    <span class="nt">&lt;columnConstraints&gt;</span>
                                                        <span class="nt">&lt;ColumnConstraints</span> <span class="na">hgrow=</span><span class="s">"SOMETIMES"</span> <span class="na">maxWidth=</span><span class="s">"-Infinity"</span> <span class="na">minWidth=</span><span class="s">"10.0"</span> <span class="na">prefWidth=</span><span class="s">"300.0"</span><span class="nt">/&gt;</span>
                                                    <span class="nt">&lt;/columnConstraints&gt;</span>
                                                    <span class="nt">&lt;rowConstraints&gt;</span>
                                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"SOMETIMES"</span><span class="nt">/&gt;</span>
                                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"SOMETIMES"</span><span class="nt">/&gt;</span>
                                                        <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"SOMETIMES"</span><span class="nt">/&gt;</span>
                                                    <span class="nt">&lt;/rowConstraints&gt;</span>
                                                    <span class="nt">&lt;children&gt;</span>
                                                        <span class="nt">&lt;HBox</span> <span class="na">alignment=</span><span class="s">"CENTER_LEFT"</span> <span class="na">spacing=</span><span class="s">"15.0"</span> <span class="na">GridPane.halignment=</span><span class="s">"CENTER"</span> <span class="na">GridPane.valignment=</span><span class="s">"CENTER"</span><span class="nt">&gt;</span>
                                                            <span class="nt">&lt;children&gt;</span>
                                                                <span class="nt">&lt;Label</span> <span class="na">prefWidth=</span><span class="s">"75.0"</span> <span class="na">text=</span><span class="s">"Cull Face"</span><span class="nt">/&gt;</span>
                                                                <span class="nt">&lt;RadioButton</span> <span class="na">fx:id=</span><span class="s">"frontCullFaceRadioButton"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">prefWidth=</span><span class="s">"70.0"</span>
                                                                             <span class="na">selected=</span><span class="s">"true"</span> <span class="na">text=</span><span class="s">"Front"</span><span class="nt">/&gt;</span>
                                                                <span class="nt">&lt;RadioButton</span> <span class="na">fx:id=</span><span class="s">"backCullFaceRadioButton"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">text=</span><span class="s">"Back"</span><span class="nt">/&gt;</span>
                                                                <span class="nt">&lt;RadioButton</span> <span class="na">fx:id=</span><span class="s">"noneCullFaceRadioButton"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">text=</span><span class="s">"None"</span><span class="nt">/&gt;</span>
                                                            <span class="nt">&lt;/children&gt;</span>
                                                        <span class="nt">&lt;/HBox&gt;</span>
                                                        <span class="nt">&lt;HBox</span> <span class="na">alignment=</span><span class="s">"CENTER_LEFT"</span> <span class="na">spacing=</span><span class="s">"15.0"</span> <span class="na">GridPane.halignment=</span><span class="s">"CENTER"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"1"</span>
                                                              <span class="na">GridPane.valignment=</span><span class="s">"CENTER"</span><span class="nt">&gt;</span>
                                                            <span class="nt">&lt;children&gt;</span>
                                                                <span class="nt">&lt;Label</span> <span class="na">prefWidth=</span><span class="s">"75.0"</span> <span class="na">text=</span><span class="s">"Draw Mode"</span><span class="nt">/&gt;</span>
                                                                <span class="nt">&lt;RadioButton</span> <span class="na">fx:id=</span><span class="s">"fillDrawModeRadioButton"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">prefWidth=</span><span class="s">"70.0"</span>
                                                                             <span class="na">selected=</span><span class="s">"true"</span> <span class="na">text=</span><span class="s">"Fill"</span><span class="nt">/&gt;</span>
                                                                <span class="nt">&lt;RadioButton</span> <span class="na">fx:id=</span><span class="s">"linesDrawModeRadioButton"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">text=</span><span class="s">"Lines"</span><span class="nt">/&gt;</span>
                                                            <span class="nt">&lt;/children&gt;</span>
                                                        <span class="nt">&lt;/HBox&gt;</span>
                                                        <span class="nt">&lt;HBox</span> <span class="na">alignment=</span><span class="s">"CENTER"</span> <span class="na">spacing=</span><span class="s">"10.0"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"2"</span><span class="nt">&gt;</span>
                                                            <span class="nt">&lt;children&gt;</span>
                                                                <span class="nt">&lt;CheckBox</span> <span class="na">fx:id=</span><span class="s">"showWireframeCheckBox"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">selected=</span><span class="s">"true"</span>
                                                                          <span class="na">text=</span><span class="s">"Show Wire Frame"</span><span class="nt">/&gt;</span>
                                                                <span class="nt">&lt;CheckBox</span> <span class="na">fx:id=</span><span class="s">"showControlPointsCheckBox"</span> <span class="na">mnemonicParsing=</span><span class="s">"false"</span> <span class="na">selected=</span><span class="s">"true"</span>
                                                                          <span class="na">text=</span><span class="s">"Show Control Points"</span><span class="nt">/&gt;</span>
                                                            <span class="nt">&lt;/children&gt;</span>
                                                        <span class="nt">&lt;/HBox&gt;</span>
                                                    <span class="nt">&lt;/children&gt;</span>
                                                <span class="nt">&lt;/GridPane&gt;</span>
                                            <span class="nt">&lt;/content&gt;</span>
                                        <span class="nt">&lt;/TitledPane&gt;</span>
                                    <span class="nt">&lt;/children&gt;</span>
                                    <span class="nt">&lt;padding&gt;</span>
                                        <span class="nt">&lt;Insets</span> <span class="na">bottom=</span><span class="s">"5.0"</span> <span class="na">left=</span><span class="s">"5.0"</span> <span class="na">right=</span><span class="s">"5.0"</span> <span class="na">top=</span><span class="s">"5.0"</span><span class="nt">/&gt;</span>
                                    <span class="nt">&lt;/padding&gt;</span>
                                <span class="nt">&lt;/VBox&gt;</span>
                            <span class="nt">&lt;/left&gt;</span>
                        <span class="nt">&lt;/BorderPane&gt;</span>
                    <span class="nt">&lt;/content&gt;</span>
                <span class="nt">&lt;/Tab&gt;</span>
            <span class="nt">&lt;/tabs&gt;</span>
        <span class="nt">&lt;/TabPane&gt;</span>

    <span class="nt">&lt;/children&gt;</span>
<span class="nt">&lt;/AnchorPane&gt;</span>
</code></pre></div></div>
<p>All right, maybe if you’re an FXML expert user you can look at these 462 lines of FXML and make some sense out of it at a glance…but I can’t.</p>

<p>I don’t really know anything about the project this came from.  I don’t know if it would be considered “good” FXML or not.  I don’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.</p>

<p>What I can say for sure is that it’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’s rarely done.</p>

<p>This layout has <strong>everything</strong> encased in an <code class="language-plaintext highlighter-rouge">AnchorPane</code> that has only one child, a <code class="language-plaintext highlighter-rouge">TabPane</code>, but it has no anchoring specified.  Once again, this is something the SceneBuilder seems to encourage because you see it a lot.  Like, really…a lot.  The <code class="language-plaintext highlighter-rouge">AnchorPane</code> appears to have no purpose in the layout, but you have to scroll through the entire file to see that it only has the <code class="language-plaintext highlighter-rouge">TabPane</code> in it.  You also have to scroll through the whole file to see that the <code class="language-plaintext highlighter-rouge">TabPane</code> has 4 <code class="language-plaintext highlighter-rouge">Tabs</code> defined.</p>

<p>Let’s look at the first <code class="language-plaintext highlighter-rouge">GridPane</code> defined.  It has this block of <code class="language-plaintext highlighter-rouge">RowConstraints</code></p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;rowConstraints&gt;</span>
    <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;RowConstraints</span> <span class="na">minHeight=</span><span class="s">"10.0"</span> <span class="na">prefHeight=</span><span class="s">"30.0"</span> <span class="na">vgrow=</span><span class="s">"NEVER"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/rowConstraints&gt;</span>
</code></pre></div></div>
<p>Yeah, you have to count them.  There’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’t do anything this crude in code.  And there are 26 more <code class="language-plaintext highlighter-rouge">GridPane</code> rows scattered throughout this file that use the same parameters!</p>

<p>Moving on to the content of the <code class="language-plaintext highlighter-rouge">GridPane</code>, we’ll just look at a small section:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Number of Components"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;Spinner</span> <span class="na">fx:id=</span><span class="s">"numComponentsSpinner"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefWidth=</span><span class="s">"100.0"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"1"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Number of Epochs"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"2"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;Spinner</span> <span class="na">fx:id=</span><span class="s">"numEpochsSpinner"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefWidth=</span><span class="s">"100.0"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"3"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Nearest Neighbors"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"4"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Negative Sample Rate"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"6"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;Label</span> <span class="na">text=</span><span class="s">"Local Connectivity"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"8"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;Spinner</span> <span class="na">fx:id=</span><span class="s">"nearestNeighborsSpinner"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefWidth=</span><span class="s">"100.0"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"5"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;Spinner</span> <span class="na">fx:id=</span><span class="s">"negativeSampleRateSpinner"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefWidth=</span><span class="s">"100.0"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"7"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;Spinner</span> <span class="na">fx:id=</span><span class="s">"localConnectivitySpinner"</span> <span class="na">editable=</span><span class="s">"true"</span> <span class="na">prefWidth=</span><span class="s">"100.0"</span> <span class="na">GridPane.rowIndex=</span><span class="s">"9"</span><span class="nt">/&gt;</span>
</code></pre></div></div>
<p>First off, I guess we have to assume that the first <code class="language-plaintext highlighter-rouge">Label</code> goes in row/colum 0/0, because it has nothing specified.  I’m guessing this is what SceneBuilder does by default???</p>

<p>The <code class="language-plaintext highlighter-rouge">Spinner</code> that goes with that first <code class="language-plaintext highlighter-rouge">Label</code> is actually declared in the next line, as well as the next <code class="language-plaintext highlighter-rouge">Label</code>/<code class="language-plaintext highlighter-rouge">Spinner</code> combination.  After that we get the next three <code class="language-plaintext highlighter-rouge">Labels</code> and then we find that the <code class="language-plaintext highlighter-rouge">Spinners</code> that go along with them follow after the last <code class="language-plaintext highlighter-rouge">Label</code>.  The rest of the <code class="language-plaintext highlighter-rouge">GridPane</code> seems to go more like that.  Perhaps this is simply the order in which the contents were added in SceneBuilder??</p>

<p>I wonder what’s in all these <code class="language-plaintext highlighter-rouge">Spinners</code>?  Integers?  Decimals?  What are there ranges?  How do they increment, and what are the default values?  Do they change dynamically?  You can’t tell any of this without looking at the FXML Controller, because clearly that’s where this stuff is defined.  I don’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.</p>

<p>What can see here from these 462 lines of FXML is that the assertion that the “declarative, hierarchical syntax” of FXML is somehow easy to read, understand and maintain is just not true.</p>

<p>I think it’s clear that well structured and organized layout code is infinitely easier to read, understand and maintain than the equivalent FXML.</p>

<h1 id="what-are-the-actual-benefits-of-fxml">What Are the Actual Benefits of FXML?</h1>

<p>As far as I can tell, there is only one:  You can use SceneBuilder.</p>

<p>It’s up to you to decide if SceneBuilder is something that you find value in.  It’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.</p>

<p>A 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’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’t want to spend time waiting.</p>

<p>I 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’t develop a screen layout “in situ”, I’d whip up a mini-application for testing the layout (and the Model, Controller and Interactor for that screen).</p>

<p>The 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 <code class="language-plaintext highlighter-rouge">Nodes</code> in the layout and the <code class="language-plaintext highlighter-rouge">Properties</code> in the Presentation Model as I build the layout itself.  In this way, I don’t just create the layout, I create a good part of the entire MVCI construct that it’s part of at the same time.</p>

<h2 id="scenebuilder-as-a-beginners-tool">SceneBuilder as a Beginners’ Tool</h2>

<p>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.</p>

<p>There 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.</p>

<p>But for a beginner, starting up SceneBuilder is probably like sitting in the cockpit of a modern jet fighter when you’ve never even flown a kite before.  There are a dizzying array of <code class="language-plaintext highlighter-rouge">Controls</code> you can use, and a head-spinning number of options that you can fiddle with for each one.  And there’s no explanations for any of it (at least there weren’t years ago).</p>

<p>My main rememberance of SceneBuilder is the countless hours that I frittered away trying to make something…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’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.</p>

<p>I often see comments from beginners that indicate that they are having the same experience.  I’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 <code class="language-plaintext highlighter-rouge">AnchorPane</code> with a single child and no anchors.</p>

<h3 id="dealing-with-the-complexity-of-fxml">Dealing With the Complexity of FXML</h3>

<p>Beginners, by definition, don’t understand what they are doing and there’s always a very big element of “paint by numbers” in their work.  This is especially true because the SceneBuilder -&gt; FXMLLoader -&gt; FXML Controller process seems to add some layer of inpenetrable magic that they cannot get past.  It’s clear that most beginners don’t even understand that the output of <code class="language-plaintext highlighter-rouge">FXMLLoader.load()</code> is just a <code class="language-plaintext highlighter-rouge">Node</code> subclass of some sort.  They only ever see it referred to as <code class="language-plaintext highlighter-rouge">root</code> and dropped immediately into a <code class="language-plaintext highlighter-rouge">Scene</code>, so they think that’s all you can do with it.</p>

<p>Once you realize that FXML itself doesn’t actually give you anything of value, you can easily see it instead as the <em>cost</em> of using SceneBuilder.</p>

<p>Over the decades, I’ve used all kinds of different screen generators in different systems, I’ve even written one myself for a “green screen” system.  They all have to include some method to allow programmers to handle the stuff that doesn’t neatly fall into values and parameters that can be easily included in the screen building tool.  Some use “hooks”.  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.</p>

<p>SceneBuilder has FXML and the FXML Controller, along with the FXMLLoader that ties them together.  That’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.</p>

<p>Let’s face it.  Complexity is a huge issue with JavaFX, even without FXML.</p>

<p>Think about some beginner who scratches his head for hours trying to figure out why that <code class="language-plaintext highlighter-rouge">Node</code> where he’s set <code class="language-plaintext highlighter-rouge">visible</code> to <code class="language-plaintext highlighter-rouge">false</code> is still taking up space in his layout.  Why would they think they would need to set <code class="language-plaintext highlighter-rouge">managed</code> to <code class="language-plaintext highlighter-rouge">false</code> as well?  How would they even know what <code class="language-plaintext highlighter-rouge">managed</code> meant?  How are they supposed to cope with the magic that happens inside <code class="language-plaintext highlighter-rouge">FXMLLoader</code>?</p>

<p>It’s clear that beginners have difficulty understanding how elements inside an FXML Controller relate to the associated layout.  It’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.</p>

<h1 id="the-cost-of-fxml">The Cost of FXML</h1>

<p>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.</p>

<p>But 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 <em>you</em> approach it.</p>

<p>Programmer complaints about JavaFX seem to revolve around one of two things:  Too much boilerplate, or the lack of some particular <code class="language-plaintext highlighter-rouge">Node</code> or <code class="language-plaintext highlighter-rouge">Control</code> class.</p>

<p>What do people mean by “boilerplate” with regards to JavaFX?</p>

<p>Usually, it centres around the idea that you have to instantiate a <code class="language-plaintext highlighter-rouge">Node</code> as a variable, and then call several of its methods to configure it.  Then put the <code class="language-plaintext highlighter-rouge">Node</code> into the layout.  Something like this:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Label</span> <span class="n">label</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Label</span><span class="o">();</span>
<span class="n">label</span><span class="o">.</span><span class="na">getStyleClass</span><span class="o">().</span><span class="na">add</span><span class="o">(</span><span class="s">"some-selector"</span><span class="o">);</span>
<span class="n">label</span><span class="o">.</span><span class="na">textProperty</span><span class="o">().</span><span class="na">bind</span><span class="o">(</span><span class="n">model</span><span class="o">.</span><span class="na">someProperty</span><span class="o">());</span>
<span class="nc">HBox</span> <span class="n">hBox</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">HBox</span><span class="o">(</span><span class="mi">10</span><span class="o">,</span> <span class="n">label</span><span class="o">,</span> <span class="n">otherNode</span><span class="o">);</span>
</code></pre></div></div>
<p>If you have ten <code class="language-plaintext highlighter-rouge">Labels</code> in your layout, and they are all configured in a similar way, then you’ve got 30 lines of boilerplate, which bloats your layout code and makes it difficult to read.</p>

<p>But you don’t have to live with this.  You can create a static builder method that will do this for you:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Labels</span> <span class="o">{</span>

   <span class="kd">public</span> <span class="kd">static</span> <span class="nc">Label</span> <span class="nf">promptLabel</span><span class="o">(</span><span class="nc">ObseravbleStringProperty</span> <span class="n">boundProperty</span><span class="o">)</span> <span class="o">{</span>
      <span class="nc">Label</span> <span class="n">label</span> <span class="o">=</span> <span class="nc">Label</span><span class="o">();</span>
      <span class="n">label</span><span class="o">.</span><span class="na">textProperty</span><span class="o">().</span><span class="na">bind</span><span class="o">(</span><span class="n">boundProperty</span><span class="o">);</span>
      <span class="n">label</span><span class="o">.</span><span class="na">getStyleClass</span><span class="o">().</span><span class="na">add</span><span class="o">(</span><span class="s">"some-selector"</span><span class="o">);</span>
      <span class="k">return</span> <span class="n">label</span><span class="o">;</span>
   <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>And you can call it like this:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">HBox</span> <span class="n">hBox</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">HBox</span><span class="o">(</span><span class="mi">10</span><span class="o">,</span> <span class="nc">Labels</span><span class="o">.</span><span class="na">promptLabel</span><span class="o">(</span><span class="n">model</span><span class="o">.</span><span class="na">someProperty</span><span class="o">()),.....);</span>
</code></pre></div></div>
<p>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.</p>

<p>You <em>can</em> 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.</p>

<p>Which leads to the second point, missing <code class="language-plaintext highlighter-rouge">Node</code> classes…</p>

<p>There are many instances in JavaFX where the only tools that we are given are very generic.  Take text entry as an example.  We have <code class="language-plaintext highlighter-rouge">TextField</code>.</p>

<p>Now, <code class="language-plaintext highlighter-rouge">TextField</code> by itself is just a box that you can type anything into.  But <code class="language-plaintext highlighter-rouge">TextField</code> supports the inclusion of a <code class="language-plaintext highlighter-rouge">TextFormatter</code> which allows you to take complete control over the input into a <code class="language-plaintext highlighter-rouge">TextField</code>.  You can create <code class="language-plaintext highlighter-rouge">TextFormatters</code> for money amounts, integer amounts, postal codes, or phone numbers, or anything else you can think of.  Personally, I wouldn’t use <code class="language-plaintext highlighter-rouge">TextField</code> without a <code class="language-plaintext highlighter-rouge">TextFormatter</code> for anything other than truly free-form text entry.</p>

<p>Let’s look at phone numbers which have different formats all over the world.  In Canada and the US we use something like “#(###)###-####”.  In France they use “”## # ## ## ## ##”, while in Ukraine they use “### ## ### ####”, and in China it’s, “## ### #### ####”.  And in many of these places, if not all, the format is different for calling in-country.</p>

<p>One possible implementation is to have a different <code class="language-plaintext highlighter-rouge">TextFormatter</code> for every phone number layout.  <code class="language-plaintext highlighter-rouge">TextFormatter</code> is a <code class="language-plaintext highlighter-rouge">Property</code> of <code class="language-plaintext highlighter-rouge">TextField</code>, so it is possible to change the <code class="language-plaintext highlighter-rouge">TextFormatter</code> 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</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">,</span> <span class="nf">promptLabel</span><span class="p">(</span><span class="s">"Phone Number:"</span><span class="p">),</span> <span class="nf">frenchPhoneNumberTF</span><span class="p">(</span><span class="n">phoneNumber</span><span class="p">)))</span>
</code></pre></div></div>
<p>And now none of the mechanics of setting up the <code class="language-plaintext highlighter-rouge">TextField</code> are exposed to the layout code at all.  Alternatively,</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">,</span> <span class="nf">promptLabel</span><span class="p">(</span><span class="s">"Phone Number:"</span><span class="p">),</span> <span class="nf">phoneNumberTF</span><span class="p">(</span><span class="n">countryCode</span><span class="p">,</span> <span class="n">phoneNumber</span><span class="p">)))</span>
</code></pre></div></div>
<p>works too.</p>

<p>If you were in a domain where you really did need a range of different formats based on the country code, you’d probably be tempted to have a <code class="language-plaintext highlighter-rouge">ComboBox</code> for the country selection, and then a <code class="language-plaintext highlighter-rouge">TextField</code> for the phone number.  You see this on websites all the time.  In this case, I’d probably create a custom control that had both the <code class="language-plaintext highlighter-rouge">ComboBox</code> and the <code class="language-plaintext highlighter-rouge">TextField</code> in it, along with their associated prompt Labels, and the connections between the country code and the <code class="language-plaintext highlighter-rouge">TextFormatter</code> could be established internally.  You could include a <code class="language-plaintext highlighter-rouge">Property&lt;Orientation&gt;</code> which would determine if the elements were layed out horizontally or vertically.</p>

<p>You’d use it like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">children</span> <span class="p">+=</span> <span class="nc">PhoneNumberEntryBox</span><span class="p">(</span><span class="n">countryCode</span><span class="p">,</span> <span class="n">phoneNumber</span><span class="p">,</span> <span class="n">orientation</span><span class="p">)))</span>
</code></pre></div></div>
<p>Can you do this with FXML?</p>

<p>Somewhat.  You cannot access the builder approach, but you could create <code class="language-plaintext highlighter-rouge">FrenchPhoneNumberTF</code> class and use it.  You can reference <code class="language-plaintext highlighter-rouge">PhoneNumberEntryBox</code>, but you cannot connect the <code class="language-plaintext highlighter-rouge">countryCode</code> or <code class="language-plaintext highlighter-rouge">phoneNumber</code> parameters via FXML.  Nor can you access the <code class="language-plaintext highlighter-rouge">orientation</code> parameter in FXML.  In truth, you could make <code class="language-plaintext highlighter-rouge">orientation</code> a <code class="language-plaintext highlighter-rouge">StlyeableProperty</code> and have a custom attribute for it in a CSS file, which might be just as easy.</p>

<p>But here’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 <code class="language-plaintext highlighter-rouge">Controls</code>, over and over again.  Integrating them into your layouts is trivial - trivial to the point where they might as well be native <code class="language-plaintext highlighter-rouge">Nodes</code> in the JavaFX library - when you code your layouts by hand.  It’s much more difficult to do the same with FXML.</p>

<h2 id="the-importance-of-builders-and-custom-controls">The Importance of Builders and Custom Controls</h2>

<p>I simply cannot over-emphasize the importance and value of this.</p>

<p>The core concept is that there is layout, and there is configuration, and there is logic.</p>

<p>Configuration is where the boilerplate and repeated code lives.  Simply refuse to repeat yourself - apply DRY contstantly and consistently - and you’ll find yourself extracting the configuration out of your layouts.  This leaves the layouts to be…layout.</p>

<p>This 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.</p>

<p>And 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.</p>

<p>In the real layout code that I write, you’ll almost never see me use something like, <code class="language-plaintext highlighter-rouge">new Label()</code>.  That’s because I’m almost always going to configure that <code class="language-plaintext highlighter-rouge">Label</code> in one of four or five ways, and I have builders for each of them.  I do the same thing for many other <code class="language-plaintext highlighter-rouge">Controls</code>, too.  I have custom <code class="language-plaintext highlighter-rouge">TableColumns</code> that have <code class="language-plaintext highlighter-rouge">TableCells</code> for a number of data types like dates, money, integers and booleans.</p>

<p>When you look at layout code, you don’t care about the details of how a <code class="language-plaintext highlighter-rouge">Label</code> created through a call to <code class="language-plaintext highlighter-rouge">Labels.dataLabel(model.someProperty)</code> is configured.  You know it’s a <code class="language-plaintext highlighter-rouge">Label</code> and it’s configured to display data.  You also know that <code class="language-plaintext highlighter-rouge">Labels.dataLabel()</code> has been used hundreds of times, is thoroughly tested, and works properly.  So it’s not likely to be a source of any problem you’re having.</p>

<p>Because of this, it’s not just code that’s not cluttering up your layouts, it’s not cluttering up your mind either.  Do you worry about how <code class="language-plaintext highlighter-rouge">new Label("Something")</code> works?  No, of course not.  Nor do you worry about <code class="language-plaintext highlighter-rouge">Labels.dataLabel()</code>.</p>

<h1 id="fxml-with-a-framework">FXML With a Framework</h1>

<p>If you are going to develop real applications that actually do something, and you want to do it right, you’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 “separation of concerns” that make systems easier to maintain and enhance.</p>

<p>We now know that FXML doesn’t give you a head start on MVC, because the FXML Controller is not an MVC Controller.  So here we’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…</p>

<div class="notice--kotlin">
 <img src="/assets/logos/Kotlin.png" alt="Kotlin" style="float:left;margin-right: 10px;margin-top: 8px;" />
 <p style="overflow:auto; float:none">
   While code is this article is written in Kotlin, all of the JavaFX concepts are exactly the same.
   Most of the Kotlin should be intuitively obvious to Java programmers,
   but if you need help understanding it, refer to this <a href="/kotlin/kotlin-examples" title="Read the article" target="_blank">page</a>.
 </p>
</div>

<p>If I go into Intellij Idea and use the “New -&gt; Project…” wizard to create a JavaFX project, I’ll get a “Hello World” application that uses FXML.  So let’s do that and see what it looks like:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">HelloApplication</span> <span class="p">:</span> <span class="nc">Application</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">fxmlLoader</span> <span class="p">=</span> <span class="nc">FXMLLoader</span><span class="p">(</span><span class="nc">HelloApplication</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"hello-view.fxml"</span><span class="p">))</span>
        <span class="kd">val</span> <span class="py">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="n">fxmlLoader</span><span class="p">.</span><span class="nf">load</span><span class="p">(),</span> <span class="mf">320.0</span><span class="p">,</span> <span class="mf">240.0</span><span class="p">)</span>
        <span class="n">stage</span><span class="p">.</span><span class="n">title</span> <span class="p">=</span> <span class="s">"Hello!"</span>
        <span class="n">stage</span><span class="p">.</span><span class="n">scene</span> <span class="p">=</span> <span class="n">scene</span>
        <span class="n">stage</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="nc">Application</span><span class="p">.</span><span class="nf">launch</span><span class="p">(</span><span class="nc">HelloApplication</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>and the FXML Controller looks like this:</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">HelloController</span> <span class="p">{</span>
    <span class="nd">@FXML</span>
    <span class="k">private</span> <span class="k">lateinit</span> <span class="kd">var</span> <span class="py">welcomeText</span><span class="p">:</span> <span class="nc">Label</span>

    <span class="nd">@FXML</span>
    <span class="k">private</span> <span class="k">fun</span> <span class="nf">onHelloButtonClick</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">welcomeText</span><span class="p">.</span><span class="n">text</span> <span class="p">=</span> <span class="s">"Welcome to JavaFX Application!"</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>while the FXML file looks like this:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>

<span class="cp">&lt;?import javafx.geometry.Insets?&gt;</span>
<span class="cp">&lt;?import javafx.scene.control.Label?&gt;</span>
<span class="cp">&lt;?import javafx.scene.layout.VBox?&gt;</span>

<span class="cp">&lt;?import javafx.scene.control.Button?&gt;</span>
<span class="nt">&lt;VBox</span> <span class="na">alignment=</span><span class="s">"CENTER"</span> <span class="na">spacing=</span><span class="s">"20.0"</span> <span class="na">xmlns:fx=</span><span class="s">"http://javafx.com/fxml"</span>
      <span class="na">fx:controller=</span><span class="s">"ca.pragmaticcoding.fxmlstuff.HelloController"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;padding&gt;</span>
        <span class="nt">&lt;Insets</span> <span class="na">bottom=</span><span class="s">"20.0"</span> <span class="na">left=</span><span class="s">"20.0"</span> <span class="na">right=</span><span class="s">"20.0"</span> <span class="na">top=</span><span class="s">"20.0"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;/padding&gt;</span>

    <span class="nt">&lt;Label</span> <span class="na">fx:id=</span><span class="s">"welcomeText"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;Button</span> <span class="na">text=</span><span class="s">"Hello!"</span> <span class="na">onAction=</span><span class="s">"#onHelloButtonClick"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/VBox&gt;</span>
</code></pre></div></div>
<p>This is good enough for an example.</p>

<p>Now, let’s look at what the exact same application would look like without FXML:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">HelloNoFXML</span> <span class="p">:</span> <span class="nc">Application</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
        <span class="nf">with</span><span class="p">(</span><span class="n">stage</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">title</span> <span class="p">=</span> <span class="s">"Hello!"</span>
            <span class="n">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="nf">createContent</span><span class="p">(),</span> <span class="mf">320.0</span><span class="p">,</span> <span class="mf">240.0</span><span class="p">)</span>
            <span class="nf">show</span><span class="p">()</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">createContent</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">20.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">20.0</span><span class="p">)</span>
        <span class="n">alignment</span> <span class="p">=</span> <span class="nc">Pos</span><span class="p">.</span><span class="nc">CENTER</span>
        <span class="kd">val</span> <span class="py">welcomeText</span> <span class="p">=</span> <span class="nc">Label</span><span class="p">()</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="n">welcomeText</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">Button</span><span class="p">(</span><span class="s">"Hello "</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">onAction</span> <span class="p">=</span> <span class="nc">EventHandler</span> <span class="p">{</span> <span class="n">welcomeText</span><span class="p">.</span><span class="n">text</span> <span class="p">=</span> <span class="s">"Welcome to JavaFX Application!"</span> <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="nc">Application</span><span class="p">.</span><span class="nf">launch</span><span class="p">(</span><span class="nc">HelloNoFXML</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>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.</p>

<p>In my opinion, the non-FXML version has the advantage that it’s incredibly staight-forward.  You can look at it and you can see that <code class="language-plaintext highlighter-rouge">createContent()</code> is a builder, and that everything in it is just plain, vanilla Kotlin code.  You can see instantly how the action of the <code class="language-plaintext highlighter-rouge">Button</code> relates directly to the <code class="language-plaintext highlighter-rouge">Label</code> and how they are both in a <code class="language-plaintext highlighter-rouge">VBox</code> with a specific alignment, padding and spacing.</p>

<p>But now, let’s imagine that we are going to something more involved with this.  We’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’ll just do this via the message in the Label.  This means that we’ll need to implement a framework, in this case I’m going to use my own MVCI (Model-View-Controller-Interactor).</p>

<p>In the non-FXML version, this is pretty simple.  The first thing I’ll do is create a Model:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">HelloModel</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">welcomeMessage</span><span class="p">:</span> <span class="nc">StringProperty</span> <span class="p">=</span> <span class="nc">SimpleStringProperty</span><span class="p">(</span><span class="s">""</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I’ll split that <code class="language-plaintext highlighter-rouge">createContent()</code> method out to be the <code class="language-plaintext highlighter-rouge">build()</code> method in a <code class="language-plaintext highlighter-rouge">Builder</code>:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">HelloViewBuilder</span><span class="p">(</span><span class="k">private</span> <span class="kd">val</span> <span class="py">model</span><span class="p">:</span> <span class="nc">HelloModel</span><span class="p">)</span> <span class="p">:</span> <span class="nc">Builder</span><span class="p">&lt;</span><span class="nc">Region</span><span class="p">&gt;</span> <span class="p">{</span>
    <span class="k">override</span> <span class="k">fun</span> <span class="nf">build</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">20.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">20.0</span><span class="p">)</span>
        <span class="n">alignment</span> <span class="p">=</span> <span class="nc">Pos</span><span class="p">.</span><span class="nc">CENTER</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">model</span><span class="p">.</span><span class="n">welcomeMessage</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">Button</span><span class="p">(</span><span class="s">"Hello "</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">onAction</span> <span class="p">=</span> <span class="nc">EventHandler</span> <span class="p">{</span> <span class="n">model</span><span class="p">.</span><span class="n">welcomeMessage</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="s">"Welcome to JavaFX Application!"</span> <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Then we’ll introduce a Controller:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">HelloMvciController</span> <span class="p">{</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">model</span><span class="p">:</span> <span class="nc">HelloModel</span> <span class="p">=</span> <span class="nc">HelloModel</span><span class="p">()</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">viewBuilder</span> <span class="p">=</span> <span class="nc">HelloViewBuilder</span><span class="p">(</span><span class="n">model</span><span class="p">)</span>

    <span class="k">fun</span> <span class="nf">getView</span><span class="p">()</span> <span class="p">=</span> <span class="n">viewBuilder</span><span class="p">.</span><span class="nf">build</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>And we’ll link it back into the Application:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">HelloNoFXML</span> <span class="p">:</span> <span class="nc">Application</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
        <span class="nf">with</span><span class="p">(</span><span class="n">stage</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">title</span> <span class="p">=</span> <span class="s">"Hello!"</span>
            <span class="n">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="nc">HelloMvciController</span><span class="p">().</span><span class="nf">getView</span><span class="p">(),</span> <span class="mf">320.0</span><span class="p">,</span> <span class="mf">240.0</span><span class="p">)</span>
            <span class="nf">show</span><span class="p">()</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>You can see that this is still fundamentally the same thing.  We’ve split the contents of the <code class="language-plaintext highlighter-rouge">Label</code> off into the Model, but this actually improves on the original code because now the <code class="language-plaintext highlighter-rouge">Button</code> refers to the Model, and has no reference to the <code class="language-plaintext highlighter-rouge">Label</code>, and we no longer need a variable for it.  This removes internal coupling between the layout <code class="language-plaintext highlighter-rouge">Nodes</code>.</p>

<p>But 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:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">HelloInteractor</span><span class="p">(</span><span class="k">private</span> <span class="kd">val</span> <span class="py">model</span><span class="p">:</span> <span class="nc">HelloModel</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">fun</span> <span class="nf">getWelcome</span><span class="p">():</span> <span class="nc">Unit</span> <span class="p">{</span>
        <span class="n">model</span><span class="p">.</span><span class="n">welcomeMessage</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="s">"Hello from the Interactor"</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This doesn’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.</p>

<p>To use this, we’ll need to update the Controller and the View to handle an “action” triggered by the View (internally from the <code class="language-plaintext highlighter-rouge">Button</code>).  First the ViewBuilder:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">HelloViewBuilder</span><span class="p">(</span><span class="k">private</span> <span class="kd">val</span> <span class="py">model</span><span class="p">:</span> <span class="nc">HelloModel</span><span class="p">,</span> <span class="k">private</span> <span class="kd">val</span> <span class="py">actionHandler</span> <span class="p">:</span> <span class="p">()</span> <span class="p">-&gt;</span> <span class="nc">Unit</span><span class="p">)</span> <span class="p">:</span> <span class="nc">Builder</span><span class="p">&lt;</span><span class="nc">Region</span><span class="p">&gt;</span> <span class="p">{</span>
    <span class="k">override</span> <span class="k">fun</span> <span class="nf">build</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">20.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">20.0</span><span class="p">)</span>
        <span class="n">alignment</span> <span class="p">=</span> <span class="nc">Pos</span><span class="p">.</span><span class="nc">CENTER</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">model</span><span class="p">.</span><span class="n">welcomeMessage</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">Button</span><span class="p">(</span><span class="s">"Hello "</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">onAction</span> <span class="p">=</span> <span class="nc">EventHandler</span> <span class="p">{</span> <span class="n">actionHandler</span><span class="p">.</span><span class="nf">invoke</span><span class="p">()</span> <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The ViewBuilder now as an additional constructor parameter that takes the Kotlin equivalent of <code class="language-plaintext highlighter-rouge">Runnable</code>.  It now just invokes the <code class="language-plaintext highlighter-rouge">Runnable</code> when the <code class="language-plaintext highlighter-rouge">Button</code> is activated.</p>

<p>And then the Controller:</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">HelloMvciController</span> <span class="p">{</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">model</span><span class="p">:</span> <span class="nc">HelloModel</span> <span class="p">=</span> <span class="nc">HelloModel</span><span class="p">()</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">interactor</span> <span class="p">=</span> <span class="nc">HelloInteractor</span><span class="p">(</span><span class="n">model</span><span class="p">)</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">viewBuilder</span> <span class="p">=</span> <span class="nc">HelloViewBuilder</span><span class="p">(</span><span class="n">model</span><span class="p">)</span> <span class="p">{</span><span class="n">interactor</span><span class="p">.</span><span class="nf">getWelcome</span><span class="p">()}</span>

    <span class="k">fun</span> <span class="nf">getView</span><span class="p">()</span> <span class="p">=</span> <span class="n">viewBuilder</span><span class="p">.</span><span class="nf">build</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now, when the <code class="language-plaintext highlighter-rouge">Button</code> is activated, the Interactor’s <code class="language-plaintext highlighter-rouge">getWelcome()</code> method is invoked by the Controller and the Model is updated.  It’s important to understand that the <code class="language-plaintext highlighter-rouge">EventHandler</code> itself is very much a GUI component - it responds to JavaFX events - while the “action” is actually GUI agnostic.  The JavaFX nature of the <code class="language-plaintext highlighter-rouge">EventHandler</code> has been stripped away, and we’re just communicating via a generic Kotlin\Java functional interface.</p>

<p>If we needed to disable and the later re-enable the <code class="language-plaintext highlighter-rouge">Button</code>, we’d define that in the View, and pass the re-enabling code to the Controller as a <code class="language-plaintext highlighter-rouge">Runnable</code>.  The original <code class="language-plaintext highlighter-rouge">Runnable</code> would be transformed to a <code class="language-plaintext highlighter-rouge">Consumer&lt;Runnable&gt;</code>.  All of this still remains just generic Kotlin\Java function elements.</p>

<p>This 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’s to do whatever it needed.</p>

<p>But regardless of what that was, the View wouldn’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.</p>

<p>As for the FXML…</p>

<p>Theoretically, we should be able to treat the FXML file, the FXMLController and the FXMLLoader as a View in MVCI.  Let’s see how that would work.</p>

<p>The <code class="language-plaintext highlighter-rouge">getView()</code> method in the Controller would now call the FXMLLoader to create the view:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">HelloMvciControllerFxml</span> <span class="p">{</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">model</span><span class="p">:</span> <span class="nc">HelloModel</span> <span class="p">=</span> <span class="nc">HelloModel</span><span class="p">()</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">interactor</span> <span class="p">=</span> <span class="nc">HelloInteractor</span><span class="p">(</span><span class="n">model</span><span class="p">)</span>

    <span class="k">fun</span> <span class="nf">getView</span><span class="p">()</span> <span class="p">:</span> <span class="nc">Region</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">fxmlLoader</span> <span class="p">=</span> <span class="nc">FXMLLoader</span><span class="p">(</span><span class="nc">HelloApplication</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"hello-view.fxml"</span><span class="p">))</span>
        <span class="k">return</span> <span class="n">fxmlLoader</span><span class="p">.</span><span class="nf">load</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Then we can update the Application to work the same way that the non-FXML version worked:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">HelloApplication</span> <span class="p">:</span> <span class="nc">Application</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">stage</span><span class="p">.</span><span class="n">title</span> <span class="p">=</span> <span class="s">"Hello!"</span>
        <span class="n">stage</span><span class="p">.</span><span class="n">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="nc">HelloMvciControllerFxml</span><span class="p">().</span><span class="nf">getView</span><span class="p">(),</span> <span class="mf">320.0</span><span class="p">,</span> <span class="mf">240.0</span><span class="p">)</span>
        <span class="n">stage</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>And it runs just like it did before.</p>

<p>But 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:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">HelloMvciControllerFxml</span> <span class="p">{</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">model</span><span class="p">:</span> <span class="nc">HelloModel</span> <span class="p">=</span> <span class="nc">HelloModel</span><span class="p">()</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">interactor</span> <span class="p">=</span> <span class="nc">HelloInteractor</span><span class="p">(</span><span class="n">model</span><span class="p">)</span>

    <span class="k">fun</span> <span class="nf">getView</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">fxmlLoader</span> <span class="p">=</span> <span class="nc">FXMLLoader</span><span class="p">(</span><span class="nc">HelloApplication</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"hello-view.fxml"</span><span class="p">))</span>
        <span class="kd">val</span> <span class="py">view</span><span class="p">:</span> <span class="nc">Region</span> <span class="p">=</span> <span class="n">fxmlLoader</span><span class="p">.</span><span class="nf">load</span><span class="p">()</span>
        <span class="kd">val</span> <span class="py">fxmlController</span> <span class="p">=</span> <span class="n">fxmlLoader</span><span class="p">.</span><span class="nf">getController</span><span class="p">()</span> <span class="k">as</span> <span class="nc">HelloController</span>
        <span class="n">fxmlController</span><span class="p">.</span><span class="nf">setModel</span><span class="p">(</span><span class="n">model</span><span class="p">)</span>
        <span class="n">fxmlController</span><span class="p">.</span><span class="nf">setAction</span> <span class="p">{</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">getWelcome</span><span class="p">()</span> <span class="p">}</span>
        <span class="k">return</span> <span class="n">view</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Let’s look at the FXML Controller:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">HelloController</span> <span class="p">{</span>
    <span class="nd">@FXML</span>
    <span class="k">private</span> <span class="k">lateinit</span> <span class="kd">var</span> <span class="py">welcomeText</span><span class="p">:</span> <span class="nc">Label</span>
    <span class="k">private</span> <span class="kd">var</span> <span class="py">actionHandler</span><span class="p">:</span> <span class="p">(()</span> <span class="p">-&gt;</span> <span class="nc">Unit</span><span class="p">)?</span> <span class="p">=</span> <span class="k">null</span>

    <span class="k">fun</span> <span class="nf">setModel</span><span class="p">(</span><span class="n">model</span><span class="p">:</span> <span class="nc">HelloModel</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">welcomeText</span><span class="p">.</span><span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">model</span><span class="p">.</span><span class="n">welcomeMessage</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">fun</span> <span class="nf">setAction</span><span class="p">(</span><span class="n">newActionHandler</span><span class="p">:</span> <span class="p">()</span> <span class="p">-&gt;</span> <span class="nc">Unit</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">actionHandler</span> <span class="p">=</span> <span class="n">newActionHandler</span>
    <span class="p">}</span>

    <span class="nd">@FXML</span>
    <span class="k">private</span> <span class="k">fun</span> <span class="nf">onHelloButtonClick</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">actionHandler</span><span class="o">?.</span><span class="nf">invoke</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>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 <code class="language-plaintext highlighter-rouge">FXMLLoader</code>.  This means that you cannot perform setup operations in <code class="language-plaintext highlighter-rouge">initialize()</code> as you ordinarily would.  In this example, I’ve loaded all that stuff (well, it’s just one thing but you get the idea) into <code class="language-plaintext highlighter-rouge">setModel()</code>, but that might not be the best approach.</p>

<p>For the action handler, I’ve taken a different approach and created it as a field in the FXML Controller which is initially <code class="language-plaintext highlighter-rouge">null</code>.  Then it’s set later by the MVCI Controller after the <code class="language-plaintext highlighter-rouge">FXMLLoader.load()</code> has been invoked.  The downside to this is that every reference to <code class="language-plaintext highlighter-rouge">actionHandler</code> has to deal with the possible <code class="language-plaintext highlighter-rouge">null</code> value.  On the upside, the FXML file still implements the <code class="language-plaintext highlighter-rouge">EventHandler</code> in the standard FXML way and doesn’t need to be modified to accommodate the changes to the FXML Controller.</p>

<p>There is a better way.  First, modify the FXML Controller so that it has constructor parameters :</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">HelloController</span><span class="p">(</span><span class="k">private</span> <span class="kd">val</span> <span class="py">model</span><span class="p">:</span> <span class="nc">HelloModel</span><span class="p">,</span> <span class="k">private</span> <span class="kd">val</span> <span class="py">actionHandler</span><span class="p">:</span> <span class="p">()</span> <span class="p">-&gt;</span> <span class="nc">Unit</span><span class="p">)</span> <span class="p">:</span> <span class="nc">Initializable</span> <span class="p">{</span>
    <span class="nd">@FXML</span>
    <span class="k">private</span> <span class="k">lateinit</span> <span class="kd">var</span> <span class="py">welcomeText</span><span class="p">:</span> <span class="nc">Label</span>

    <span class="nd">@FXML</span>
    <span class="k">private</span> <span class="k">fun</span> <span class="nf">onHelloButtonClick</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">actionHandler</span><span class="p">.</span><span class="nf">invoke</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">location</span><span class="p">:</span> <span class="nc">URL</span><span class="p">?,</span> <span class="n">resources</span><span class="p">:</span> <span class="nc">ResourceBundle</span><span class="p">?)</span> <span class="p">{</span>
        <span class="n">welcomeText</span><span class="p">.</span><span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">model</span><span class="p">.</span><span class="n">welcomeMessage</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>You can see that now we can set the binding in <code class="language-plaintext highlighter-rouge">initialize()</code> and we don’t have to worry about <code class="language-plaintext highlighter-rouge">null</code> in the <code class="language-plaintext highlighter-rouge">actionHandler</code> for the <code class="language-plaintext highlighter-rouge">Button</code>.</p>

<p>Then we have to tell the <code class="language-plaintext highlighter-rouge">FXMLLoader</code> to use a “Controller Factory” to instantiate the FXML Controller.  This is just a <code class="language-plaintext highlighter-rouge">Callback</code> that will return an instantiation of our FXML Controller:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">HelloMvciControllerFxml</span> <span class="p">{</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">model</span><span class="p">:</span> <span class="nc">HelloModel</span> <span class="p">=</span> <span class="nc">HelloModel</span><span class="p">()</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">interactor</span> <span class="p">=</span> <span class="nc">HelloInteractor</span><span class="p">(</span><span class="n">model</span><span class="p">)</span>

    <span class="k">fun</span> <span class="nf">getView</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">fxmlLoader</span> <span class="p">=</span> <span class="nc">FXMLLoader</span><span class="p">(</span><span class="nc">HelloApplication</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"hello-view.fxml"</span><span class="p">))</span>
        <span class="n">fxmlLoader</span><span class="p">.</span><span class="nf">setControllerFactory</span> <span class="p">{</span> <span class="nc">HelloController</span><span class="p">(</span><span class="n">model</span><span class="p">)</span> <span class="p">{</span> <span class="n">interactor</span><span class="p">.</span><span class="nf">getWelcome</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span>
        <span class="k">return</span> <span class="n">fxmlLoader</span><span class="p">.</span><span class="nf">load</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This works, and functions exactly the same as the non-FXML version.</p>

<p>When you look at the code, it’s really not <em>that</em> complicated.  Just create an FXML Controller with constructor parameters (just like the ViewBuilder in the non-FXML version) and use a <code class="language-plaintext highlighter-rouge">ControllerFactory</code> to instantiate the FXML Controller inside the <code class="language-plaintext highlighter-rouge">FXMLLoader</code>.  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.</p>

<p>You 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.</p>

<p>You should also note that you’re probably not going to see this approach outlined anywhere else.  I spent a bit of time specifically searching for variants on  “JavaFX FXML MVC” and didn’t see anything like this anywhere.</p>

<p>Why not?</p>

<p>I 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 “Model” that they propose is just a POJO of <code class="language-plaintext highlighter-rouge">Observable</code> classes, and doesn’t have any domain/application logic in it.  All the application logic goes into the FXML Controller, which becomes essentially a “God” class.</p>

<h1 id="conclusion">Conclusion</h1>

<p>As an experienced JavaFX developer I can say that if I’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’d guess something approaching 10% of the total code in the application.</p>

<p>This probably sounds fantastical to most beginners.  Impossible, really.  That’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 <code class="language-plaintext highlighter-rouge">EventHandlers</code> and <code class="language-plaintext highlighter-rouge">ChangeListeners</code> when <code class="language-plaintext highlighter-rouge">Binding</code> is the better approach.  Or, things like using <code class="language-plaintext highlighter-rouge">ComboBox.getSelectionModel().getSelectedItem()</code> instead of just <code class="language-plaintext highlighter-rouge">ComboBox.getValue()</code>.</p>

<p>None of these “wrong” 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.</p>

<p>So when we talk about “To FXML or not to FXML”, we’re not talking about some profound decision that dictates the entire structure of your application.  We’re talking about how you architect that 10% which comprises the View.</p>

<p>That is, however, as long as you don’t buy into the idea that the FXML Controller acts as the MVC Controller.  Because it’s not, and if you treat it like it is then your application structure is going to suffer.</p>

<p>At the end of the day, the answer to, “Should you use FXML?”, 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 <a href="/beginners/intro">Absolute Beginners Guide to JavaFX</a>.</p>]]></content><author><name>Dave Barrett</name><email>pragmaticcoding8@gmail.com</email></author><category term="javafx" /><summary type="html"><![CDATA[FXML. There are all kinds of claims about its value, but are they right?]]></summary></entry><entry><title type="html">Custom Styleable Properties</title><link href="https://www.pragmaticcoding.ca/javafx/elements/styleable-properties" rel="alternate" type="text/html" title="Custom Styleable Properties" /><published>2024-12-15T17:00:00+00:00</published><updated>2024-12-15T17:00:00+00:00</updated><id>https://www.pragmaticcoding.ca/javafx/elements/styleable-properties</id><content type="html" xml:base="https://www.pragmaticcoding.ca/javafx/elements/styleable-properties"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>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 <code class="language-plaintext highlighter-rouge">Node</code> classes when they make sense.  This includes padding, background parameters, border parameters, min/max/pref sizes and a host of other things.</p>

<p>It could be argued that anything that can be styled via CSS <em>should</em> 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 <code class="language-plaintext highlighter-rouge">HBox</code> and <code class="language-plaintext highlighter-rouge">VBox</code>.</p>

<p>But what about aspects that aren’t covered by the standard JavaFX attributes?</p>

<p>It 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’ll find that they don’t do anything that you can’t do yourself in your applications.</p>

<p>However, the JavaDocs for the classes that you’ll need to use are somewhat less than clear, and seem to be very complicated.  In this article, we’ll take a step by step approach to implementing some custom styleable attributes, understand what’s happening, and then look at how you can deviate from the “best practices” described in the JavaDocs to make it easier to implement custom styling in your layouts.</p>

<p>Finally, we’ll look at how you should go about implementing custom styling in a custom <code class="language-plaintext highlighter-rouge">Node</code> subclass that you create for general purpose use.</p>

<div class="notice--kotlin">
 <img src="/assets/logos/Kotlin.png" alt="Kotlin" style="float:left;margin-right: 10px;margin-top: 8px;" />
 <p style="overflow:auto; float:none">
   While code is this article is written in Kotlin, all of the JavaFX concepts are exactly the same.
   Most of the Kotlin should be intuitively obvious to Java programmers,
   but if you need help understanding it, refer to this <a href="/kotlin/kotlin-examples" title="Read the article" target="_blank">page</a>.
 </p>
</div>

<h1 id="custom-styling-in-a-layout">Custom Styling in a Layout</h1>

<p>Let’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…</p>

<h2 id="using-traditional-styling">Using Traditional Styling</h2>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Styleable0</span> <span class="p">:</span> <span class="nc">Application</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">stage</span><span class="p">.</span><span class="n">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="nf">createContent</span><span class="p">()).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nc">Styleable0</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"styleable0.css"</span><span class="p">)</span><span class="o">?.</span><span class="nf">toString</span><span class="p">()</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="n">stylesheets</span> <span class="p">+=</span> <span class="n">it</span> <span class="p">}</span>
        <span class="p">}</span>
        <span class="n">stage</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">createContent</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">BorderPane</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">center</span> <span class="p">=</span> <span class="nc">CustomBox</span><span class="p">()</span>
        <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">10.0</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="nc">CustomBox</span> <span class="p">:</span> <span class="nc">HBox</span><span class="p">(</span><span class="mf">20.0</span><span class="p">)</span> <span class="p">{</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"custom-widget"</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">Circle</span><span class="p">(</span><span class="mf">100.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"circle"</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">Rectangle</span><span class="p">(</span><span class="mf">200.0</span><span class="p">,</span> <span class="mf">200.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"square"</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Application</span><span class="p">.</span><span class="nf">launch</span><span class="p">(</span><span class="nc">Styleable0</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
</code></pre></div></div>

<p>Here’s the style sheet:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.custom-widget</span> <span class="p">{</span>
  <span class="py">-fx-padding</span><span class="p">:</span> <span class="m">10px</span><span class="p">;</span>
  <span class="py">-fx-border-width</span><span class="p">:</span> <span class="m">3px</span><span class="p">;</span>
  <span class="py">-fx-border-color</span><span class="p">:</span> <span class="no">green</span><span class="p">;</span>
  <span class="py">-fx-background-color</span><span class="p">:</span> <span class="no">beige</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.custom-widget</span> <span class="nc">.circle</span> <span class="p">{</span>
  <span class="py">-fx-fill</span><span class="p">:</span> <span class="no">indianred</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.custom-widget</span> <span class="nc">.square</span> <span class="p">{</span>
  <span class="py">-fx-fill</span><span class="p">:</span> <span class="no">darkseagreen</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And it looks like this:</p>

<p><img src="/assets/elements/StyleableProperties0.png" alt="Initial Example" /></p>

<p>I’ve implemented this as a custom class extending HBox to emphasize a few points:</p>

<ul>
  <li>You’d do this when you want to reuse this component over and over.</li>
  <li>You’d probably want to limit the client code’s ability to mess with the internal workings of it.</li>
  <li>It doesn’t stop client code from messing with its internal workings.</li>
</ul>

<p>The first thing is that, as an extension of <code class="language-plaintext highlighter-rouge">HBox</code>, it exposes quite a few methods that client code can use to mess around with it.  Most importantly, it exposes <code class="language-plaintext highlighter-rouge">Pane.getChildren()</code> 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 <strong>any</strong> styleable aspect of those components, not just the colour.  There’s not even an implied understanding that only the colours should be customized.</p>

<p>How do we fix this?</p>

<h2 id="locking-down-the-structure">Locking Down the Structure</h2>

<p>The first thing we can do is to change the base class of our custom component from <code class="language-plaintext highlighter-rouge">HBox</code> to <code class="language-plaintext highlighter-rouge">Region</code>.  This is because <code class="language-plaintext highlighter-rouge">Region</code> exposes a lot less public methods.  Most importantly, <code class="language-plaintext highlighter-rouge">Region.getChildren()</code> is <code class="language-plaintext highlighter-rouge">protected</code>, unlike <code class="language-plaintext highlighter-rouge">Pane.getChildren()</code>, which is <code class="language-plaintext highlighter-rouge">public</code>.  This means that our custom class’s code can call <code class="language-plaintext highlighter-rouge">getChildren()</code> but client code cannot.  So now our custom class looks like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">CustomBox1</span> <span class="p">:</span> <span class="nc">Region</span><span class="p">()</span> <span class="p">{</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">(</span><span class="mf">20.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"custom-widget"</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Circle</span><span class="p">(</span><span class="mf">100.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"circle"</span>
            <span class="p">}</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Rectangle</span><span class="p">(</span><span class="mf">200.0</span><span class="p">,</span> <span class="mf">200.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"square"</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>And, of course, it still looks like this:</p>

<p><img src="/assets/elements/StyleableProperties0.png" alt="Initial Example" /></p>

<p>This 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.</p>

<p>We do this through custom <code class="language-plaintext highlighter-rouge">StyleableProperties</code>.</p>

<p>We’ll start with just the circle, and when that’s working, then we’ll duplicate it for the square.</p>

<p>The first thing we need is a <code class="language-plaintext highlighter-rouge">Property</code> to store the colour.  JavaFX gives us a special set of <code class="language-plaintext highlighter-rouge">Properties</code> that we can use to link back to a style sheet.  We will use <code class="language-plaintext highlighter-rouge">StyleableObjectProperty</code> here:</p>

<p><img src="/assets/elements/StyleableProperties1.png" alt="Styleable Properety Declaration" /></p>

<p>As you can see, the parameter <code class="language-plaintext highlighter-rouge">CIRCLE_COLOUR_META_DATA</code> is red, meaning that it hasn’t been defined yet.  It’s also in all caps with underscores, hinting that it might be a static constant - which it is.</p>

<p>This is where the JavaDocs just dump a whole pantload of details on you without any explanations and quickly get confusing.  So we’ll go through this one piece at a time, and explain what’s happening.</p>

<p>A <code class="language-plaintext highlighter-rouge">StyleableProperty</code> needs a way to link back to the CSS file, and this is done with something called <code class="language-plaintext highlighter-rouge">CssMetaData</code>, which is what <code class="language-plaintext highlighter-rouge">CIRCLE_COLOUR_META_DATA</code> is.  Well, actually it will be an anonymous inner class extending <code class="language-plaintext highlighter-rouge">CssMetaData</code>.</p>

<p>First off, <code class="language-plaintext highlighter-rouge">CssMetaData</code> is a generic class.  It has two types, one is the type of the <code class="language-plaintext highlighter-rouge">Node</code> that contains the <code class="language-plaintext highlighter-rouge">StyleableProperty</code> (actually it’s any class that implements the <code class="language-plaintext highlighter-rouge">Styleable</code> interface) and the other is type of the data contained in the <code class="language-plaintext highlighter-rouge">StyleableProperty</code>.  In our case, this is <code class="language-plaintext highlighter-rouge">CustomBox2</code> and <code class="language-plaintext highlighter-rouge">Color</code>.</p>

<p>Let’s look at the standard constructor for <code class="language-plaintext highlighter-rouge">CssMetaData</code>.  It has two parameters:</p>

<ul>
  <li>A <code class="language-plaintext highlighter-rouge">String</code> which is the attribute tag in the CSS file.</li>
  <li>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 <code class="language-plaintext highlighter-rouge">StyleableProperty</code>.</li>
</ul>

<p>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 <code class="language-plaintext highlighter-rouge">StyleConverter</code> class.  There is one for each of the kind of values that you are likely to need, including standard JavaFX types like <code class="language-plaintext highlighter-rouge">Color</code>, <code class="language-plaintext highlighter-rouge">Font</code> and <code class="language-plaintext highlighter-rouge">Duration</code>.  In our case we’ll use:</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="nc">CssMetaData</span><span class="p">(</span><span class="s">"-wfx-circle-colour"</span><span class="p">,</span> <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getColorConverter</span><span class="p">())</span><span class="err">`</span>
</code></pre></div></div>

<p>This constructor provides the essential connection to the CSS file.</p>

<p>Moving on, <code class="language-plaintext highlighter-rouge">CssMetaData</code> has two abstract methods that need to be supplied by any extending class:</p>

<ul>
  <li>A method that returns whether or not the value of the property can be set from the style sheet.</li>
  <li>A method that returns the associated <code class="language-plaintext highlighter-rouge">StyleableProperty</code>.</li>
</ul>

<p>Remember that <code class="language-plaintext highlighter-rouge">CssMetaData</code> is defined statically, but the <code class="language-plaintext highlighter-rouge">StyleableProperty</code> is an instance variable, or field, of the class.  This means that we cannot just reference the <code class="language-plaintext highlighter-rouge">StyleableProperty</code> directly.  But both of these methods take an instance of the class as a parameter, so we can reference the field through that parameter.</p>

<p>The official documentation treats these methods like they are just standard boilerplate that you can just copy and paste without thinking about them.  But we’ll think about them here…</p>

<p>You cannot set a <code class="language-plaintext highlighter-rouge">Property</code> that has been bound.  That would generate a runtime error and would be bad.  So the standard answer to whether a <code class="language-plaintext highlighter-rouge">StyleableProperty</code> 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’ll have to decide if just checking if the <code class="language-plaintext highlighter-rouge">StyleableProperty</code> is bound is enough for your situation.</p>

<p>The standard method for returning the <code class="language-plaintext highlighter-rouge">StyleableProperty</code> 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’s no reason you have to stick to this.  Let’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 <code class="language-plaintext highlighter-rouge">List</code> of <code class="language-plaintext highlighter-rouge">StyleableProperties</code> and you could use a DOW calculation to pick one.</p>

<p>For now, at least, we are going to stick with the standard implementation:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">circleColour</span><span class="p">:</span> <span class="nc">StyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span>
    <span class="nc">SimpleStyleableObjectProperty</span><span class="p">(</span><span class="nc">CIRCLE_COLOUR_META_DATA</span><span class="p">,</span> <span class="nc">Color</span><span class="p">.</span><span class="nc">INDIANRED</span><span class="p">)</span>

<span class="k">companion</span> <span class="k">object</span> <span class="nc">CssStuff</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">CIRCLE_COLOUR_META_DATA</span><span class="p">:</span> <span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">CustomBox2</span><span class="p">,</span> <span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span>
        <span class="kd">object</span> <span class="err">: </span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">CustomBox2</span><span class="p">,</span> <span class="nc">Color</span><span class="p">&gt;(</span><span class="s">"-wfx-circle-colour"</span><span class="p">,</span> <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getColorConverter</span><span class="p">())</span> <span class="p">{</span>
            <span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">CustomBox2</span><span class="p">):</span> <span class="nc">Boolean</span> <span class="p">=</span> <span class="p">!</span><span class="n">p0</span><span class="p">.</span><span class="n">circleColour</span><span class="p">.</span><span class="n">isBound</span>

            <span class="k">override</span> <span class="k">fun</span> <span class="nf">getStyleableProperty</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">CustomBox2</span><span class="p">):</span> <span class="nc">StyleableProperty</span><span class="p">&lt;</span><span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span> <span class="n">p0</span><span class="p">.</span><span class="n">circleColour</span>
        <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>In Kotlin, a <code class="language-plaintext highlighter-rouge">companion object</code> is a way of encapsulating static elements.  They aren’t really “static”, 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 <code class="language-plaintext highlighter-rouge">companion object</code> structure because it segregates the static elements from the instance members of the class.</p>

<p>In 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 <code class="language-plaintext highlighter-rouge">CssMetaData</code> to be heritable and you need to set everything up properly.  But in our case, where we are just building a “one-off”, and all that stuff would just be “dotting i’s and crossing t’s” for the sake of formality.  So we can forgo it here.</p>

<p>We need a way for the underlying JavaFX code to see our new <code class="language-plaintext highlighter-rouge">CssMetaData</code> and incorporate it into its styling information.  The <code class="language-plaintext highlighter-rouge">Styleable</code> interface has a method called <code class="language-plaintext highlighter-rouge">getCssMetaData()</code> that we can override for this purpose.  We need to integrate our new <code class="language-plaintext highlighter-rouge">CssMetaData</code> into the list that the parent class, in this case <code class="language-plaintext highlighter-rouge">Region</code>, already has:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">override</span> <span class="k">fun</span> <span class="nf">getCssMetaData</span><span class="p">():</span> <span class="nc">MutableList</span><span class="p">&lt;</span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="k">out</span> <span class="nc">Styleable</span><span class="p">,</span> <span class="err">*</span><span class="p">&gt;&gt;</span> <span class="p">=</span>
         <span class="p">(</span><span class="k">super</span><span class="p">.</span><span class="nf">getCssMetaData</span><span class="p">()</span> <span class="p">+</span> <span class="nc">CIRCLE_COLOUR_META_DATA</span><span class="p">)</span> <span class="k">as</span> <span class="nc">MutableList</span>
</code></pre></div></div>
<p>Finally, we need to use this new <code class="language-plaintext highlighter-rouge">Property</code> in the layout:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">children</span> <span class="p">+=</span> <span class="nc">Circle</span><span class="p">(</span><span class="mf">100.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
    <span class="nf">fillProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">circleColour</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>To summarize:</p>

<ol>
  <li>Create a <code class="language-plaintext highlighter-rouge">StyleableProperty</code> of the appropriate type, and reference a static CssMetaData class that you will create.</li>
  <li>Create a static instance of <code class="language-plaintext highlighter-rouge">CssMetaData</code> that matches your new <code class="language-plaintext highlighter-rouge">StyleableProperty</code></li>
  <li>Add your new <code class="language-plaintext highlighter-rouge">CssMetaData</code> instance to the a list that will be returned from <code class="language-plaintext highlighter-rouge">getCssMetaData()</code></li>
  <li>Use the new <code class="language-plaintext highlighter-rouge">StyleableProperty</code> in the layout somehow.</li>
</ol>

<p>Our complete code looks like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">CustomBox2</span> <span class="p">:</span> <span class="nc">Region</span><span class="p">()</span> <span class="p">{</span>

    <span class="k">private</span> <span class="kd">val</span> <span class="py">circleColour</span><span class="p">:</span> <span class="nc">StyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span>
        <span class="nc">SimpleStyleableObjectProperty</span><span class="p">(</span><span class="nc">CIRCLE_COLOUR_META_DATA</span><span class="p">,</span> <span class="nc">Color</span><span class="p">.</span><span class="nc">WHITE</span><span class="p">)</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">squareColour</span><span class="p">:</span> <span class="nc">StyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span>
        <span class="nc">SimpleStyleableObjectProperty</span><span class="p">(</span><span class="nc">SQUARE_COLOUR_META_DATA</span><span class="p">,</span> <span class="nc">Color</span><span class="p">.</span><span class="nc">WHITE</span><span class="p">)</span>

    <span class="k">companion</span> <span class="k">object</span> <span class="nc">CssStuff</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">CIRCLE_COLOUR_META_DATA</span><span class="p">:</span> <span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">CustomBox2</span><span class="p">,</span> <span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span>
            <span class="kd">object</span> <span class="err">: </span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">CustomBox2</span><span class="p">,</span> <span class="nc">Color</span><span class="p">&gt;(</span><span class="s">"-wfx-circle-colour"</span><span class="p">,</span> <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getColorConverter</span><span class="p">())</span> <span class="p">{</span>
                <span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">CustomBox2</span><span class="p">):</span> <span class="nc">Boolean</span> <span class="p">=</span> <span class="p">!</span><span class="n">p0</span><span class="p">.</span><span class="n">circleColour</span><span class="p">.</span><span class="n">isBound</span>

                <span class="k">override</span> <span class="k">fun</span> <span class="nf">getStyleableProperty</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">CustomBox2</span><span class="p">):</span> <span class="nc">StyleableProperty</span><span class="p">&lt;</span><span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span> <span class="n">p0</span><span class="p">.</span><span class="n">circleColour</span>
            <span class="p">}</span>
        <span class="kd">val</span> <span class="py">SQUARE_COLOUR_META_DATA</span><span class="p">:</span> <span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">CustomBox2</span><span class="p">,</span> <span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span>
            <span class="kd">object</span> <span class="err">: </span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">CustomBox2</span><span class="p">,</span> <span class="nc">Color</span><span class="p">&gt;(</span><span class="s">"-wfx-square-colour"</span><span class="p">,</span> <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getColorConverter</span><span class="p">())</span> <span class="p">{</span>
                <span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">CustomBox2</span><span class="p">):</span> <span class="nc">Boolean</span> <span class="p">=</span> <span class="p">!</span><span class="n">p0</span><span class="p">.</span><span class="n">squareColour</span><span class="p">.</span><span class="n">isBound</span>

                <span class="k">override</span> <span class="k">fun</span> <span class="nf">getStyleableProperty</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">CustomBox2</span><span class="p">):</span> <span class="nc">StyleableProperty</span><span class="p">&lt;</span><span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span> <span class="n">p0</span><span class="p">.</span><span class="n">squareColour</span>
            <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">getCssMetaData</span><span class="p">()</span> <span class="p">=</span>
        <span class="p">(</span><span class="nf">getClassCssMetaData</span><span class="p">()</span> <span class="p">+</span> <span class="nc">CIRCLE_COLOUR_META_DATA</span> <span class="p">+</span> <span class="nc">SQUARE_COLOUR_META_DATA</span><span class="p">)</span> <span class="k">as</span> <span class="nc">MutableList</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"custom-widget"</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">(</span><span class="mf">20.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"inner-box"</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Circle</span><span class="p">(</span><span class="mf">100.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="nf">fillProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">circleColour</span><span class="p">)</span>
            <span class="p">}</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Rectangle</span><span class="p">(</span><span class="mf">200.0</span><span class="p">,</span> <span class="mf">200.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="nf">fillProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">squareColour</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="using-an-anonymous-inner-class">Using an Anonymous Inner Class</h2>

<p>All of the documentation in the JavaDocs are oriented towards creating a resuable, possibly extendable, named custom class.  This means that the approach that’s documented goes through all of the ceremony required to ensure that things are done “by the book”.</p>

<p>But, is it possible to just create a <code class="language-plaintext highlighter-rouge">StyleableProperty</code> and <code class="language-plaintext highlighter-rouge">CssMetaData</code> in the layout code?</p>

<p>The answer, unfortunately, is no.  This is because <code class="language-plaintext highlighter-rouge">Node.getCssMetaData()</code> (or something that underlies it) needs to be overriden in order to activate any custom <code class="language-plaintext highlighter-rouge">CssMetaData</code> that we create.  And the only way to do this is to extend whatever <code class="language-plaintext highlighter-rouge">Node</code> subclass that we’re working with.</p>

<p>The next problem that we have is that we cannot declare <code class="language-plaintext highlighter-rouge">companion objects</code> (or, in Java, static members) for inner classes.  This shouldn’t be a huge problem.  If our layout element that we’re creating the custom styling for is really a “one-off”, then it shouldn’t make too big a difference if we create the custom <code class="language-plaintext highlighter-rouge">CssMetaData</code> in a non-static way.</p>

<p>The next issue that we need to deal with is this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">CustomBox2</span><span class="p">):</span> <span class="nc">Boolean</span> <span class="p">=</span> <span class="p">!</span><span class="n">p0</span><span class="p">.</span><span class="n">squareColour</span><span class="p">.</span><span class="n">isBound</span>
</code></pre></div></div>
<p>If our layout element is an anoymous inner class, then we don’t have a way to specify the type of <code class="language-plaintext highlighter-rouge">p0</code>.  We could try this variation:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">Styleable</span><span class="p">):</span> <span class="nc">Boolean</span> <span class="p">=</span> <span class="p">!(</span><span class="n">p0</span> <span class="k">as</span> <span class="nc">CustomBox2</span><span class="p">).</span><span class="n">squareColour</span><span class="p">.</span><span class="n">isBound</span>
</code></pre></div></div>
<p>That’s going to let us create a method that’s compatible with the inner workings of JavaFX, but we’re still stuck with the <code class="language-plaintext highlighter-rouge">as CustomBox2</code> because we don’t have a name for our anonymous inner class.  So we cannot do this.</p>

<p>But…</p>

<p>This is a one-off, right?  So we should be able to just create a <code class="language-plaintext highlighter-rouge">StyleableProperty</code> 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:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">circleColour</span><span class="p">:</span> <span class="nc">StyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span>
    <span class="nc">SimpleStyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">Color</span><span class="p">&gt;(</span><span class="n">circleColourMetaData</span><span class="p">,</span><span class="nc">Color</span><span class="p">.</span><span class="nc">BLUEVIOLET</span><span class="p">)</span>

<span class="kd">val</span> <span class="py">circleColourMetaData</span><span class="p">:</span> <span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">Styleable</span><span class="p">,</span> <span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span>
    <span class="kd">object</span> <span class="err">: </span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">Styleable</span><span class="p">,</span> <span class="nc">Color</span><span class="p">&gt;(</span><span class="s">"-wfx-circle-colour"</span><span class="p">,</span> <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getColorConverter</span><span class="p">())</span> <span class="p">{</span>
        <span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">Styleable</span><span class="p">):</span> <span class="nc">Boolean</span> <span class="p">=</span> <span class="p">!</span><span class="n">circleColour</span><span class="p">.</span><span class="n">isBound</span>
        <span class="k">override</span> <span class="k">fun</span> <span class="nf">getStyleableProperty</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">Styleable</span><span class="p">):</span> <span class="nc">StyleableProperty</span><span class="p">&lt;</span><span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span> <span class="n">circleColour</span>
    <span class="p">}</span>
</code></pre></div></div>
<p>Here, we are just referencing <code class="language-plaintext highlighter-rouge">circleColour</code>.  We don’t care about <code class="language-plaintext highlighter-rouge">p0</code> because it can only ever be one thing, we only ever want to return <code class="language-plaintext highlighter-rouge">circleColour</code>.</p>

<p>But…</p>

<p>There’s a problem.  In my IDE, it looks like this:</p>

<p><img src="/assets/elements/StyleableProperties2.png" alt="IDE With Error" /></p>

<p>I cannot refer to <code class="language-plaintext highlighter-rouge">circleColourMetaData</code> yet because it’s declaration comes later in the code.  And I can’t just flip the declarations around, because those two functions need to refer to <code class="language-plaintext highlighter-rouge">circleColour</code>.  So I’m stuck.</p>

<p>But…</p>

<p>We need a class that extends <code class="language-plaintext highlighter-rouge">StyleableObjectProperty</code> but there’s nothing that says that we have to use <code class="language-plaintext highlighter-rouge">SimpleStyleableObjectProperty</code>.  And it’s only <code class="language-plaintext highlighter-rouge">SimpleStyleableObjectProperty's</code> <em>implementation</em> that is demanding that we supply the corresponding <code class="language-plaintext highlighter-rouge">CssMetaData</code> in its constructor.</p>

<p>It turns out that, just like <code class="language-plaintext highlighter-rouge">SimpleObjectProperty</code>, <code class="language-plaintext highlighter-rouge">SimpleStyleableObjectProperty</code> doesn’t actually do much.  It satisfies the interface requirements for the “Bean” stuff, and provides the constructors to populate the Bean data…but that’s about all.  The one extra thing it does is to provide the <code class="language-plaintext highlighter-rouge">getCssMetaData()</code> method, and initialize it via the constructor.</p>

<p>This means that it’s trivial to create a version which doesn’t take the <code class="language-plaintext highlighter-rouge">CssMetaData</code> in the constructor, but rather exposes a <code class="language-plaintext highlighter-rouge">setCssMetaData()</code> method that can be called later.  I’ve called it <code class="language-plaintext highlighter-rouge">DeferredStyleableObjectProperty</code>:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">DeferredStyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;(</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">bean</span><span class="p">:</span> <span class="nc">Any</span><span class="p">?,</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">name</span><span class="p">:</span> <span class="nc">String</span><span class="p">,</span>
    <span class="n">initialValue</span><span class="p">:</span> <span class="nc">T</span><span class="p">?</span> <span class="p">=</span> <span class="k">null</span>
<span class="p">)</span> <span class="p">:</span> <span class="nc">StyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;()</span> <span class="p">{</span>

    <span class="k">private</span> <span class="kd">var</span> <span class="py">cssMetaData</span><span class="p">:</span> <span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="k">out</span> <span class="nc">Styleable</span><span class="p">?,</span> <span class="nc">T</span><span class="p">&gt;?</span> <span class="p">=</span> <span class="k">null</span>

    <span class="k">constructor</span><span class="p">()</span> <span class="p">:</span> <span class="k">this</span><span class="p">(</span><span class="k">null</span><span class="p">,</span> <span class="s">""</span><span class="p">,</span> <span class="k">null</span><span class="p">)</span>
    <span class="k">constructor</span><span class="p">(</span><span class="n">initialValue</span><span class="p">:</span> <span class="nc">T</span><span class="p">)</span> <span class="p">:</span> <span class="k">this</span><span class="p">(</span><span class="k">null</span><span class="p">,</span> <span class="s">""</span><span class="p">,</span> <span class="n">initialValue</span><span class="p">)</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="n">initialValue</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="k">super</span><span class="p">.</span><span class="nf">setValue</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">getBean</span><span class="p">():</span> <span class="nc">Any</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="n">bean</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">getName</span><span class="p">():</span> <span class="nc">String</span> <span class="p">{</span>
        <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="n">name</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">getCssMetaData</span><span class="p">():</span> <span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="k">out</span> <span class="nc">Styleable</span><span class="p">?,</span> <span class="nc">T</span><span class="p">&gt;?</span> <span class="p">=</span> <span class="n">cssMetaData</span>

    <span class="k">fun</span> <span class="nf">setCssMetaData</span><span class="p">(</span><span class="n">newVal</span><span class="p">:</span> <span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="k">out</span> <span class="nc">Styleable</span><span class="p">?,</span> <span class="nc">T</span><span class="p">&gt;)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">cssMetaData</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span> <span class="n">cssMetaData</span> <span class="p">=</span> <span class="n">newVal</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>As you can see, it’s pretty simple.  One thing to watch out for is that it would probably be very bad to change the value of <code class="language-plaintext highlighter-rouge">cssMetaData</code> after it has been set.  I’ve provided some protection against this, and it will ignore any attempts to set <code class="language-plaintext highlighter-rouge">cssMetaData</code> if its current value is not <code class="language-plaintext highlighter-rouge">null</code>.</p>

<p>Now we can do this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fun</span> <span class="nf">customBox3</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">circleColour</span> <span class="p">=</span> <span class="nc">DeferredStyleableObjectProperty</span><span class="p">(</span><span class="nc">Color</span><span class="p">.</span><span class="nc">BLUEVIOLET</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">circleColourMetaData</span><span class="p">:</span> <span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">Styleable</span><span class="p">,</span> <span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span>
        <span class="kd">object</span> <span class="err">: </span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">Styleable</span><span class="p">,</span> <span class="nc">Color</span><span class="p">&gt;(</span><span class="s">"-wfx-circle-colour"</span><span class="p">,</span> <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getColorConverter</span><span class="p">())</span> <span class="p">{</span>
            <span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">Styleable</span><span class="p">):</span> <span class="nc">Boolean</span> <span class="p">=</span> <span class="p">!</span><span class="n">circleColour</span><span class="p">.</span><span class="n">isBound</span>
            <span class="k">override</span> <span class="k">fun</span> <span class="nf">getStyleableProperty</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">Styleable</span><span class="p">):</span> <span class="nc">StyleableProperty</span><span class="p">&lt;</span><span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span> <span class="n">circleColour</span>
        <span class="p">}</span>
    <span class="n">circleColour</span><span class="p">.</span><span class="nf">setCssMetaData</span><span class="p">(</span><span class="n">circleColourMetaData</span><span class="p">)</span>

    <span class="kd">val</span> <span class="py">squareColour</span> <span class="p">=</span> <span class="nc">DeferredStyleableObjectProperty</span><span class="p">(</span><span class="nc">Color</span><span class="p">.</span><span class="nc">BLUEVIOLET</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">squareColourMetaData</span><span class="p">:</span> <span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">Styleable</span><span class="p">,</span> <span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span>
        <span class="kd">object</span> <span class="err">: </span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">Styleable</span><span class="p">,</span> <span class="nc">Color</span><span class="p">&gt;(</span><span class="s">"-wfx-square-colour"</span><span class="p">,</span> <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getColorConverter</span><span class="p">())</span> <span class="p">{</span>
            <span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">Styleable</span><span class="p">):</span> <span class="nc">Boolean</span> <span class="p">=</span> <span class="p">!</span><span class="n">squareColour</span><span class="p">.</span><span class="n">isBound</span>
            <span class="k">override</span> <span class="k">fun</span> <span class="nf">getStyleableProperty</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">Styleable</span><span class="p">):</span> <span class="nc">StyleableProperty</span><span class="p">&lt;</span><span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span> <span class="n">squareColour</span>
        <span class="p">}</span>
    <span class="n">squareColour</span><span class="p">.</span><span class="nf">setCssMetaData</span><span class="p">(</span><span class="n">squareColourMetaData</span><span class="p">)</span>

    <span class="k">return</span> <span class="kd">object</span> <span class="err">: </span><span class="nc">HBox</span><span class="p">(</span><span class="mf">20.0</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">override</span> <span class="k">fun</span> <span class="nf">getCssMetaData</span><span class="p">():</span> <span class="nc">MutableList</span><span class="p">&lt;</span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="k">out</span> <span class="nc">Styleable</span><span class="p">,</span> <span class="err">*</span><span class="p">&gt;&gt;</span> <span class="p">=</span>
            <span class="p">(</span><span class="nf">getClassCssMetaData</span><span class="p">()</span> <span class="p">+</span> <span class="n">circleColourMetaData</span> <span class="p">+</span> <span class="n">squareColourMetaData</span><span class="p">)</span> <span class="k">as</span> <span class="nc">MutableList</span>
    <span class="p">}.</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"custom-widget"</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">Circle</span><span class="p">(</span><span class="mf">100.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nf">fillProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">circleColour</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">Rectangle</span><span class="p">(</span><span class="mf">200.0</span><span class="p">,</span> <span class="mf">200.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nf">fillProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">squareColour</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>You can see that we create the <code class="language-plaintext highlighter-rouge">StyleableProperties</code> using the new <code class="language-plaintext highlighter-rouge">DeferredStyleableObjectProperty</code> class, then declare the <code class="language-plaintext highlighter-rouge">CssMetaData</code> using those <code class="language-plaintext highlighter-rouge">DeferredStyleableObjectProperties</code>, and then we call <code class="language-plaintext highlighter-rouge">DeferredStyleableObjectProperty.setCssMetaData()</code> to complete the initialization.</p>

<p>We still need to extend our class in order to override <code class="language-plaintext highlighter-rouge">getCssMetaData()</code>, but then we can just use <code class="language-plaintext highlighter-rouge">.apply{}</code> to complete the rest of the layout.  However, those <code class="language-plaintext highlighter-rouge">StyleableProperties</code> don’t have to be declared in this method, they could be fields in the ViewBuider itself:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Styleable3</span> <span class="p">:</span> <span class="nc">Application</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">circleColour</span> <span class="p">=</span> <span class="nc">DeferredStyleableObjectProperty</span><span class="p">(</span><span class="nc">Color</span><span class="p">.</span><span class="nc">BLUEVIOLET</span><span class="p">)</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">circleColourMetaData</span><span class="p">:</span> <span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">Styleable</span><span class="p">,</span> <span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span>
        <span class="kd">object</span> <span class="err">: </span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">Styleable</span><span class="p">,</span> <span class="nc">Color</span><span class="p">&gt;(</span><span class="s">"-wfx-circle-colour"</span><span class="p">,</span> <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getColorConverter</span><span class="p">())</span> <span class="p">{</span>
            <span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">Styleable</span><span class="p">):</span> <span class="nc">Boolean</span> <span class="p">=</span> <span class="p">!</span><span class="n">circleColour</span><span class="p">.</span><span class="n">isBound</span>
            <span class="k">override</span> <span class="k">fun</span> <span class="nf">getStyleableProperty</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">Styleable</span><span class="p">):</span> <span class="nc">StyleableProperty</span><span class="p">&lt;</span><span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span> <span class="n">circleColour</span>

            <span class="nf">init</span> <span class="p">{</span>
                <span class="n">circleColour</span><span class="p">.</span><span class="nf">setCssMetaData</span><span class="p">(</span><span class="k">this</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>

    <span class="k">private</span> <span class="kd">val</span> <span class="py">squareColour</span> <span class="p">=</span> <span class="nc">DeferredStyleableObjectProperty</span><span class="p">(</span><span class="nc">Color</span><span class="p">.</span><span class="nc">BLUEVIOLET</span><span class="p">)</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">squareColourMetaData</span><span class="p">:</span> <span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">Styleable</span><span class="p">,</span> <span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span>
        <span class="kd">object</span> <span class="err">: </span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">Styleable</span><span class="p">,</span> <span class="nc">Color</span><span class="p">&gt;(</span><span class="s">"-wfx-square-colour"</span><span class="p">,</span> <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getColorConverter</span><span class="p">())</span> <span class="p">{</span>
            <span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">Styleable</span><span class="p">):</span> <span class="nc">Boolean</span> <span class="p">=</span> <span class="p">!</span><span class="n">squareColour</span><span class="p">.</span><span class="n">isBound</span>
            <span class="k">override</span> <span class="k">fun</span> <span class="nf">getStyleableProperty</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">Styleable</span><span class="p">):</span> <span class="nc">StyleableProperty</span><span class="p">&lt;</span><span class="nc">Color</span><span class="p">&gt;</span> <span class="p">=</span> <span class="n">squareColour</span>

            <span class="nf">init</span> <span class="p">{</span>
                <span class="n">squareColour</span><span class="p">.</span><span class="nf">setCssMetaData</span><span class="p">(</span><span class="k">this</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">stage</span><span class="p">.</span><span class="n">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="nf">createContent</span><span class="p">()).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nc">Styleable3</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"styleable3.css"</span><span class="p">)</span><span class="o">?.</span><span class="nf">toString</span><span class="p">()</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="n">stylesheets</span> <span class="p">+=</span> <span class="n">it</span> <span class="p">}</span>
        <span class="p">}</span>
        <span class="n">stage</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">createContent</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">BorderPane</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">center</span> <span class="p">=</span> <span class="nf">customBox3</span><span class="p">()</span>
        <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">10.0</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">customBox3</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="kd">object</span> <span class="err">: </span><span class="nc">HBox</span><span class="p">(</span><span class="mf">20.0</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">override</span> <span class="k">fun</span> <span class="nf">getCssMetaData</span><span class="p">():</span> <span class="nc">MutableList</span><span class="p">&lt;</span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="k">out</span> <span class="nc">Styleable</span><span class="p">,</span> <span class="err">*</span><span class="p">&gt;&gt;</span> <span class="p">=</span>
            <span class="p">(</span><span class="nf">getClassCssMetaData</span><span class="p">()</span> <span class="p">+</span> <span class="n">circleColourMetaData</span> <span class="p">+</span> <span class="n">squareColourMetaData</span><span class="p">)</span> <span class="k">as</span> <span class="nc">MutableList</span>
    <span class="p">}.</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"custom-widget"</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">Circle</span><span class="p">(</span><span class="mf">100.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nf">fillProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">circleColour</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">Rectangle</span><span class="p">(</span><span class="mf">200.0</span><span class="p">,</span> <span class="mf">200.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nf">fillProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">squareColour</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Application</span><span class="p">.</span><span class="nf">launch</span><span class="p">(</span><span class="nc">Styleable3</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
</code></pre></div></div>
<p>We don’t have an official ViewBuilder in this simple example, so we’re just using <code class="language-plaintext highlighter-rouge">Application</code> itself, but you can see how it would work.  I like this coding arrangement.  The <code class="language-plaintext highlighter-rouge">StyleableProperties</code> and the <code class="language-plaintext highlighter-rouge">CssMetaData</code> are more like utility classes in this implementation, and they are out of the layout code and don’t clutter it up.  The layout code now looks very much like it did before, and it’s fairly easy to read.</p>

<p>You’ll just have to make sure that you don’t re-use that <code class="language-plaintext highlighter-rouge">CssMetaData</code> in another part of the layout.</p>

<p>The CSS looks like this:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.custom-widget</span> <span class="p">{</span>
  <span class="py">-wfx-circle-colour</span><span class="p">:</span> <span class="no">indianred</span><span class="p">;</span>
  <span class="py">-wfx-square-colour</span><span class="p">:</span> <span class="no">teal</span><span class="p">;</span>
  <span class="py">-fx-border-color</span><span class="p">:</span> <span class="no">blue</span><span class="p">;</span>
  <span class="py">-fx-border-width</span><span class="p">:</span> <span class="m">3px</span><span class="p">;</span>
  <span class="py">-fx-padding</span><span class="p">:</span> <span class="m">5px</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="encapsulated-cssmetadata">Encapsulated CssMetaData</h2>

<p>This gymnastics with the delayed initialization of the <code class="language-plaintext highlighter-rouge">CssMetaData</code> in the <code class="language-plaintext highlighter-rouge">StyleableProperty</code> goes away if you encapsulate the <code class="language-plaintext highlighter-rouge">CssMetaData</code> definition in the <code class="language-plaintext highlighter-rouge">StyleableProperty</code> 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:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">StandardStyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;(</span>
    <span class="n">attributeName</span><span class="p">:</span> <span class="nc">String</span><span class="p">,</span>
    <span class="n">styleConverter</span><span class="p">:</span> <span class="nc">StyleConverter</span><span class="p">&lt;</span><span class="err">*</span><span class="p">,</span> <span class="nc">T</span><span class="p">&gt;,</span>
    <span class="n">initialValue</span><span class="p">:</span> <span class="nc">T</span><span class="p">?</span> <span class="p">=</span> <span class="k">null</span>
<span class="p">)</span> <span class="p">:</span> <span class="nc">StyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;()</span> <span class="p">{</span>

    <span class="k">private</span> <span class="kd">var</span> <span class="py">cssMetaData</span><span class="p">:</span> <span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="k">out</span> <span class="nc">Styleable</span><span class="p">,</span> <span class="nc">T</span><span class="p">&gt;</span> <span class="p">=</span>
        <span class="kd">object</span> <span class="err">: </span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">Styleable</span><span class="p">,</span> <span class="nc">T</span><span class="p">&gt;(</span><span class="n">attributeName</span><span class="p">,</span> <span class="n">styleConverter</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">Styleable</span><span class="p">):</span> <span class="nc">Boolean</span> <span class="p">=</span> <span class="p">!</span><span class="k">this</span><span class="nd">@StandardStyleableObjectProperty</span><span class="p">.</span><span class="n">isBound</span>
            <span class="k">override</span> <span class="k">fun</span> <span class="nf">getStyleableProperty</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">Styleable</span><span class="p">):</span> <span class="nc">StyleableProperty</span><span class="p">&lt;</span><span class="nc">T</span><span class="p">&gt;</span> <span class="p">=</span>
                <span class="k">this</span><span class="nd">@StandardStyleableObjectProperty</span>
        <span class="p">}</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="n">initialValue</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="k">super</span><span class="p">.</span><span class="nf">setValue</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">getBean</span><span class="p">():</span> <span class="nc">Any</span><span class="p">?</span> <span class="p">=</span> <span class="k">null</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">getName</span><span class="p">():</span> <span class="nc">String</span> <span class="p">=</span> <span class="s">"Styleable Property"</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">getCssMetaData</span><span class="p">():</span> <span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="k">out</span> <span class="nc">Styleable</span><span class="p">,</span> <span class="nc">T</span><span class="p">&gt;</span> <span class="p">=</span> <span class="n">cssMetaData</span>
<span class="p">}</span>
</code></pre></div></div>
<p>I’ve called this <code class="language-plaintext highlighter-rouge">StandardStyleableObjectProperty</code> because it uses the default <code class="language-plaintext highlighter-rouge">!this@StandardStyleableObjectProperty.isBound</code> to respond to <code class="language-plaintext highlighter-rouge">isSettable()</code>.  If you want to generalize this class, you’d need to provide a constructor parameter that takes a <code class="language-plaintext highlighter-rouge">Function</code> or <code class="language-plaintext highlighter-rouge">Supplier</code> to use here.  It’s also pretty simple, coming in at around a dozen lines of code.</p>

<p>Once you’ve created this <code class="language-plaintext highlighter-rouge">StandardStyleableObjectProperty</code> class, the layout code gets much simpler:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Styleable4</span> <span class="p">:</span> <span class="nc">Application</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">circleColour</span> <span class="p">=</span>
        <span class="nc">StandardStyleableObjectProperty</span><span class="p">(</span><span class="s">"-wfx-circle-colour"</span><span class="p">,</span> <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getColorConverter</span><span class="p">(),</span> <span class="nc">Color</span><span class="p">.</span><span class="nc">BLUEVIOLET</span><span class="p">)</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">squareColour</span> <span class="p">=</span>
        <span class="nc">StandardStyleableObjectProperty</span><span class="p">(</span><span class="s">"-wfx-square-colour"</span><span class="p">,</span> <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getColorConverter</span><span class="p">(),</span> <span class="nc">Color</span><span class="p">.</span><span class="nc">BLUEVIOLET</span><span class="p">)</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">stage</span><span class="p">.</span><span class="n">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="nf">createContent</span><span class="p">()).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nc">Styleable3</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"styleable3.css"</span><span class="p">)</span><span class="o">?.</span><span class="nf">toString</span><span class="p">()</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="n">stylesheets</span> <span class="p">+=</span> <span class="n">it</span> <span class="p">}</span>
        <span class="p">}</span>
        <span class="n">stage</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">createContent</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">BorderPane</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">center</span> <span class="p">=</span> <span class="nf">customBox3</span><span class="p">()</span>
        <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">10.0</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">customBox3</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="kd">object</span> <span class="err">: </span><span class="nc">HBox</span><span class="p">(</span><span class="mf">20.0</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">override</span> <span class="k">fun</span> <span class="nf">getCssMetaData</span><span class="p">():</span> <span class="nc">MutableList</span><span class="p">&lt;</span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="k">out</span> <span class="nc">Styleable</span><span class="p">,</span> <span class="err">*</span><span class="p">&gt;&gt;</span> <span class="p">=</span>
            <span class="p">(</span><span class="nf">getClassCssMetaData</span><span class="p">()</span> <span class="p">+</span> <span class="n">circleColour</span><span class="p">.</span><span class="n">cssMetaData</span> <span class="p">+</span> <span class="n">squareColour</span><span class="p">.</span><span class="n">cssMetaData</span><span class="p">)</span> <span class="k">as</span> <span class="nc">MutableList</span>
    <span class="p">}.</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"custom-widget"</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">Circle</span><span class="p">(</span><span class="mf">100.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nf">fillProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">circleColour</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">Rectangle</span><span class="p">(</span><span class="mf">200.0</span><span class="p">,</span> <span class="mf">200.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nf">fillProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">squareColour</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Application</span><span class="p">.</span><span class="nf">launch</span><span class="p">(</span><span class="nc">Styleable4</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
</code></pre></div></div>
<p>Now, the custom <code class="language-plaintext highlighter-rouge">StyleableProperty</code> hardly takes up any space in your mind when you read the code.</p>

<h2 id="removing-the-anonymous-inner-class">Removing the Anonymous Inner Class</h2>

<p>The need to extend <code class="language-plaintext highlighter-rouge">HBox</code> in order to implement the modified <code class="language-plaintext highlighter-rouge">getCssMetaData()</code> is the biggest problem with the code readability.  It would be nicer if there was some sort of <code class="language-plaintext highlighter-rouge">setLayoutCssMetaData()</code> method that we could use to inject our custom <code class="language-plaintext highlighter-rouge">StyleableProperties</code> into our <code class="language-plaintext highlighter-rouge">HBox</code>.  Like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">StyleableHBox</span><span class="p">(</span><span class="n">spacing</span><span class="p">:</span> <span class="nc">Double</span><span class="p">)</span> <span class="p">:</span> <span class="nc">HBox</span><span class="p">(</span><span class="n">spacing</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">layoutCssMetaData</span> <span class="p">=</span> <span class="n">mutableListOf</span><span class="p">&lt;</span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="k">out</span> <span class="nc">Styleable</span><span class="p">,</span> <span class="err">*</span><span class="p">&gt;&gt;()</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">getCssMetaData</span><span class="p">():</span> <span class="nc">MutableList</span><span class="p">&lt;</span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="k">out</span> <span class="nc">Styleable</span><span class="p">,</span> <span class="err">*</span><span class="p">&gt;&gt;</span> <span class="p">{</span>
        <span class="k">return</span> <span class="p">(</span><span class="k">super</span><span class="p">.</span><span class="nf">getCssMetaData</span><span class="p">()</span> <span class="p">+</span> <span class="n">layoutCssMetaData</span><span class="p">)</span> <span class="k">as</span> <span class="nc">MutableList</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>We can use this new class this way:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Styleable5</span> <span class="p">:</span> <span class="nc">Application</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">circleColour</span> <span class="p">=</span>
        <span class="nc">StandardStyleableObjectProperty</span><span class="p">(</span><span class="s">"-wfx-circle-colour"</span><span class="p">,</span> <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getColorConverter</span><span class="p">(),</span> <span class="nc">Color</span><span class="p">.</span><span class="nc">BLUEVIOLET</span><span class="p">)</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">squareColour</span> <span class="p">=</span>
        <span class="nc">StandardStyleableObjectProperty</span><span class="p">(</span><span class="s">"-wfx-square-colour"</span><span class="p">,</span> <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getColorConverter</span><span class="p">(),</span> <span class="nc">Color</span><span class="p">.</span><span class="nc">BLUEVIOLET</span><span class="p">)</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">stage</span><span class="p">.</span><span class="n">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="nf">createContent</span><span class="p">()).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nc">Styleable3</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"styleable3.css"</span><span class="p">)</span><span class="o">?.</span><span class="nf">toString</span><span class="p">()</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="n">stylesheets</span> <span class="p">+=</span> <span class="n">it</span> <span class="p">}</span>
        <span class="p">}</span>
        <span class="n">stage</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">createContent</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">BorderPane</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">center</span> <span class="p">=</span> <span class="nf">customBox</span><span class="p">()</span>
        <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">10.0</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">customBox</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">StyleableHBox</span><span class="p">(</span><span class="mf">20.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">layoutCssMetaData</span> <span class="p">+=</span> <span class="nf">listOf</span><span class="p">(</span><span class="n">circleColour</span><span class="p">.</span><span class="n">cssMetaData</span><span class="p">,</span> <span class="n">squareColour</span><span class="p">.</span><span class="n">cssMetaData</span><span class="p">)</span>
        <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"custom-widget"</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">Circle</span><span class="p">(</span><span class="mf">100.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nf">fillProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">circleColour</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">Rectangle</span><span class="p">(</span><span class="mf">200.0</span><span class="p">,</span> <span class="mf">200.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nf">fillProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">squareColour</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Application</span><span class="p">.</span><span class="nf">launch</span><span class="p">(</span><span class="nc">Styleable5</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
</code></pre></div></div>
<p>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 <code class="language-plaintext highlighter-rouge">StyleableHBox</code> 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’d probably never need to deal with any of the other <code class="language-plaintext highlighter-rouge">HBox</code> constructors, but a rigorous implementation would have to handle them as well.</p>

<h2 id="should-you-actually-do-this">Should You Actually Do This?</h2>

<p>I should note at this point that this approach spurns almost all of the “best practices” advocated in the JavaDocs.  Specifically, the JavaDocs say this:</p>

<blockquote>
  <p>The method Node.getCssMetaData() is called to obtain the List<CssMetaData>. This method is called frequently and it is prudent to return a static list rather than creating the list on each call.</CssMetaData></p>
</blockquote>

<p>When 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 <code class="language-plaintext highlighter-rouge">println()</code> statements in various key places, including in my implementation of <code class="language-plaintext highlighter-rouge">Node.getCssMetaData()</code>.  I found that it wasn’t called more than once or twice at runtime.</p>

<p>While it may be true that, in general, that <code class="language-plaintext highlighter-rouge">Node.getCssMetaData()</code> is called a gazillion times collectively from all the <code class="language-plaintext highlighter-rouge">Nodes</code> 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.</p>

<p>There <em>are</em> caveats to using this approach.  You would have to be very careful using the same <code class="language-plaintext highlighter-rouge">StyleableProperties</code> to supply <code class="language-plaintext highlighter-rouge">CssMetaData</code> 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 <code class="language-plaintext highlighter-rouge">StyleableProperty</code>, it would probably be difficult to debug.</p>

<p>You 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’s better to simply configure it programmatically.  This less likely to be the case with custom layout elements.</p>

<p>All that being said, this approach <em>does</em> work.  Actually quite well.  If it’s an approach that you want to use extensively, then it would probably be worth creating versions of <code class="language-plaintext highlighter-rouge">StlyeleableHBox</code> for <code class="language-plaintext highlighter-rouge">VBox</code>, <code class="language-plaintext highlighter-rouge">Pane</code> and <code class="language-plaintext highlighter-rouge">StackPane</code> as well.</p>

<p>I 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.</p>

<p>Almost more importantly, I feel that going through this demonstration gives a really good understanding of how this <code class="language-plaintext highlighter-rouge">CssMetaData</code> stuff really does work.  This is an insight that you probably won’t get from reading the JavaDocs.</p>

<h1 id="a-proper-custom-class">A Proper Custom Class</h1>

<p>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’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 <code class="language-plaintext highlighter-rouge">CssMetaData</code> that are described in the JavaDocs could simply be tossed out, with little impact on performance.</p>

<p>However, 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’s take a look at that.</p>

<p>Here’s our initial screen layout:</p>

<p><img src="/assets/elements/StyleableProperties3.png" alt="Invoice Layout 1" /></p>

<p>Which was achieved via this code:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Styleable6</span> <span class="p">:</span> <span class="nc">Application</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">stage</span><span class="p">.</span><span class="n">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="nf">createContent</span><span class="p">()).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nc">Styleable3</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"styleable6.css"</span><span class="p">)</span><span class="o">?.</span><span class="nf">toString</span><span class="p">()</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="n">stylesheets</span> <span class="p">+=</span> <span class="n">it</span> <span class="p">}</span>
        <span class="p">}</span>
        <span class="n">stage</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">createContent</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">BorderPane</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">center</span> <span class="p">=</span> <span class="nf">customBox</span><span class="p">()</span>
        <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">10.0</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">customBox</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">7.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="nf">println</span><span class="p">(</span><span class="s">"Got here"</span><span class="p">)</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Parts:"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span> <span class="n">minWidth</span> <span class="p">=</span> <span class="mf">150.0</span> <span class="p">}</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"1783.98"</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Labour:"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span> <span class="n">minWidth</span> <span class="p">=</span> <span class="mf">150.0</span> <span class="p">}</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"300"</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Tax:"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span> <span class="n">minWidth</span> <span class="p">=</span> <span class="mf">150.0</span> <span class="p">}</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"103.76"</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Discount:"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span> <span class="n">minWidth</span> <span class="p">=</span> <span class="mf">150.0</span> <span class="p">}</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"-10"</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Total:"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span> <span class="n">minWidth</span> <span class="p">=</span> <span class="mf">150.0</span> <span class="p">}</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"2177.74"</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Application</span><span class="p">.</span><span class="nf">launch</span><span class="p">(</span><span class="nc">Styleable6</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
</code></pre></div></div>
<p>Obviously, this would be better achieved via a <code class="language-plaintext highlighter-rouge">GridPane</code>, but we want to keep the layout as simple as possible.  So a <code class="language-plaintext highlighter-rouge">VBox</code> of <code class="language-plaintext highlighter-rouge">HBoxes</code> is what we’ll use.  The problem that we’re going to concentrate on is the number display, because it’s horrible. We are going to customize these aspects:</p>

<ul>
  <li>Numeric input</li>
  <li>Justification</li>
  <li>Number of decimal places</li>
  <li>Formatting of negative values</li>
</ul>

<p>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’re going to create a custom <code class="language-plaintext highlighter-rouge">Label</code> class called <code class="language-plaintext highlighter-rouge">DecimalLabel</code> to handle all of this.</p>

<h2 id="numeric-input-and-justification">Numeric Input and Justification</h2>

<p>These don’t actually take any custom <code class="language-plaintext highlighter-rouge">StyleableProperties</code> to achieve, at least at first:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">DecimalLabel</span><span class="p">(</span><span class="n">value</span> <span class="p">:</span> <span class="nc">ObservableDoubleValue</span><span class="p">):</span> <span class="nc">Label</span><span class="p">()</span> <span class="p">{</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">value</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="nf">toString</span><span class="p">()</span> <span class="p">})</span>
        <span class="n">alignment</span> <span class="p">=</span> <span class="nc">Pos</span><span class="p">.</span><span class="nc">CENTER_RIGHT</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>I’m not providing a constructor that takes a <code class="language-plaintext highlighter-rouge">Double</code> 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 <code class="language-plaintext highlighter-rouge">Observable</code> wrappers, so that’s what I’m going to accept in my constructor.  This is then bound to the <code class="language-plaintext highlighter-rouge">textProperty</code> of the <code class="language-plaintext highlighter-rouge">Label</code>.  Our layout code now changes to this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Styleable6</span> <span class="p">:</span> <span class="nc">Application</span><span class="p">()</span> <span class="p">{</span>

    <span class="kd">val</span> <span class="py">partsAmount</span><span class="p">:</span> <span class="nc">DoubleProperty</span> <span class="p">=</span> <span class="nc">SimpleDoubleProperty</span><span class="p">(</span><span class="mf">1783.98</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">labourAmount</span><span class="p">:</span> <span class="nc">DoubleProperty</span> <span class="p">=</span> <span class="nc">SimpleDoubleProperty</span><span class="p">(</span><span class="mf">300.0</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">taxAmount</span><span class="p">:</span> <span class="nc">DoubleProperty</span> <span class="p">=</span> <span class="nc">SimpleDoubleProperty</span><span class="p">(</span><span class="mf">103.76</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">discountAmount</span><span class="p">:</span> <span class="nc">DoubleProperty</span> <span class="p">=</span> <span class="nc">SimpleDoubleProperty</span><span class="p">(-</span><span class="mf">10.0</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">totalAmount</span><span class="p">:</span> <span class="nc">DoubleProperty</span> <span class="p">=</span> <span class="nc">SimpleDoubleProperty</span><span class="p">(</span><span class="mf">2177.74</span><span class="p">)</span>
    <span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">stage</span><span class="p">.</span><span class="n">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="nf">createContent</span><span class="p">()).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nc">Styleable3</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"styleable6.css"</span><span class="p">)</span><span class="o">?.</span><span class="nf">toString</span><span class="p">()</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="n">stylesheets</span> <span class="p">+=</span> <span class="n">it</span> <span class="p">}</span>
        <span class="p">}</span>
        <span class="n">stage</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">createContent</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">BorderPane</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">center</span> <span class="p">=</span> <span class="nf">customBox</span><span class="p">()</span>
        <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">10.0</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">customBox</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">7.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="nf">println</span><span class="p">(</span><span class="s">"Got here"</span><span class="p">)</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Parts:"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span> <span class="n">minWidth</span> <span class="p">=</span> <span class="mf">150.0</span> <span class="p">}</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">DecimalLabel</span><span class="p">(</span><span class="n">partsAmount</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Labour:"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span> <span class="n">minWidth</span> <span class="p">=</span> <span class="mf">150.0</span> <span class="p">}</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">DecimalLabel</span><span class="p">(</span><span class="n">labourAmount</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Tax:"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span> <span class="n">minWidth</span> <span class="p">=</span> <span class="mf">150.0</span> <span class="p">}</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">DecimalLabel</span><span class="p">(</span><span class="n">taxAmount</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Discount:"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span> <span class="n">minWidth</span> <span class="p">=</span> <span class="mf">150.0</span> <span class="p">}</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">DecimalLabel</span><span class="p">(</span><span class="n">discountAmount</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">HBox</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Label</span><span class="p">(</span><span class="s">"Total:"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span> <span class="n">minWidth</span> <span class="p">=</span> <span class="mf">150.0</span> <span class="p">}</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">DecimalLabel</span><span class="p">(</span><span class="n">totalAmount</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Application</span><span class="p">.</span><span class="nf">launch</span><span class="p">(</span><span class="nc">Styleable6</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
</code></pre></div></div>
<p>But the result looks pretty much the same, even with the right justification.  If we add some styling to our <code class="language-plaintext highlighter-rouge">DecimalLabel</code> to give it a border, then we can see why:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.decimal-label</span> <span class="p">{</span>
  <span class="py">-fx-border-width</span><span class="p">:</span> <span class="m">1px</span><span class="p">;</span>
  <span class="py">-fx-border-color</span><span class="p">:</span> <span class="no">black</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p><img src="/assets/elements/StyleableProperties4.png" alt="DecimalLabel With Border" /></p>

<p>Ah!  The <code class="language-plaintext highlighter-rouge">DecimalLabels</code> might be right justified internally, but they are only as wide as they need to be to hold the text and the <code class="language-plaintext highlighter-rouge">HBox</code> aligns its elements <code class="language-plaintext highlighter-rouge">Pos.LEFT</code> by default.  We can fix this be adding a minimum width:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.decimal-label</span> <span class="p">{</span>
  <span class="py">-fx-border-width</span><span class="p">:</span> <span class="m">1px</span><span class="p">;</span>
  <span class="py">-fx-border-color</span><span class="p">:</span> <span class="no">black</span><span class="p">;</span>
  <span class="py">-fx-min-width</span><span class="p">:</span> <span class="m">5em</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p><img src="/assets/elements/StyleableProperties5.png" alt="Wider DecimalLabels" /></p>

<p>That’s better.</p>

<h2 id="controlling-the-number-of-decimal-places">Controlling the Number of Decimal Places</h2>

<p>In our example, it’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 <code class="language-plaintext highlighter-rouge">Label</code> class, we’ll make the number of decimal places a <code class="language-plaintext highlighter-rouge">StyleableProperty</code> so that we can specify it via the style sheet.  Let’s add the <code class="language-plaintext highlighter-rouge">StyleableProperty</code> and the <code class="language-plaintext highlighter-rouge">CssMetaData</code> to our <code class="language-plaintext highlighter-rouge">DecimalLabel</code>:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">DecimalLabel</span><span class="p">(</span><span class="n">value</span><span class="p">:</span> <span class="nc">ObservableDoubleValue</span><span class="p">)</span> <span class="p">:</span> <span class="nc">Label</span><span class="p">()</span> <span class="p">{</span>

    <span class="k">private</span> <span class="kd">val</span> <span class="py">decimalPlaces</span><span class="p">:</span> <span class="nc">StyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">Number</span><span class="p">&gt;</span> <span class="p">=</span> <span class="nc">SimpleStyleableObjectProperty</span><span class="p">(</span><span class="nc">DECIMAL_PLACES_META_DATA</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span>

    <span class="k">companion</span> <span class="k">object</span> <span class="nc">CssStuff</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">DECIMAL_PLACES_META_DATA</span> <span class="p">=</span>
            <span class="kd">object</span> <span class="err">: </span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">DecimalLabel</span><span class="p">,</span> <span class="nc">Number</span><span class="p">&gt;(</span><span class="s">"-wfx-decimal-places"</span><span class="p">,</span> <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getSizeConverter</span><span class="p">(),</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">DecimalLabel</span><span class="p">):</span> <span class="nc">Boolean</span> <span class="p">=</span> <span class="p">!</span><span class="n">p0</span><span class="p">.</span><span class="n">decimalPlaces</span><span class="p">.</span><span class="n">isBound</span>

                <span class="k">override</span> <span class="k">fun</span> <span class="nf">getStyleableProperty</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">DecimalLabel</span><span class="p">):</span> <span class="nc">StyleableProperty</span><span class="p">&lt;</span><span class="nc">Number</span><span class="p">&gt;</span> <span class="p">=</span> <span class="n">p0</span><span class="p">.</span><span class="n">decimalPlaces</span>
            <span class="p">}</span>
    <span class="p">}</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"decimal-label"</span>
        <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="n">value</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="nf">toString</span><span class="p">()</span> <span class="p">})</span>
        <span class="n">alignment</span> <span class="p">=</span> <span class="nc">Pos</span><span class="p">.</span><span class="nc">CENTER_RIGHT</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>We need a way to use this property to control how many decimal places are shown.  To do this, we create a <code class="language-plaintext highlighter-rouge">Binding</code> that is dependent on <code class="language-plaintext highlighter-rouge">decimalPlaces</code> and <code class="language-plaintext highlighter-rouge">value</code>.  It will use <code class="language-plaintext highlighter-rouge">String.format()</code> to do the conversion:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">ConversionBinding</span><span class="p">(</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">labelValue</span><span class="p">:</span> <span class="nc">ObservableDoubleValue</span><span class="p">,</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">decimalPlaces</span><span class="p">:</span> <span class="nc">ObservableValue</span><span class="p">&lt;</span><span class="nc">Number</span><span class="p">&gt;</span>
<span class="p">)</span> <span class="p">:</span>
    <span class="nc">StringBinding</span><span class="p">()</span> <span class="p">{</span>
    <span class="nf">init</span> <span class="p">{</span>
        <span class="k">super</span><span class="p">.</span><span class="nf">bind</span><span class="p">(</span><span class="n">labelValue</span><span class="p">,</span> <span class="n">decimalPlaces</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">computeValue</span><span class="p">():</span> <span class="nc">String</span> <span class="p">{</span>
        <span class="k">return</span> <span class="nc">String</span><span class="p">.</span><span class="nf">format</span><span class="p">(</span><span class="s">"%,.${decimalPlaces.value.toInt()}f"</span><span class="p">,</span> <span class="n">labelValue</span><span class="p">.</span><span class="n">value</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Finally, we need to make the CSS information available to JavaFX.  We do this by overriding <code class="language-plaintext highlighter-rouge">Label.getCssMetaData()</code>.</p>

<p>But…</p>

<p><code class="language-plaintext highlighter-rouge">Label.getCssMetaData()</code> is final!  Looking at the JavaDocs for this method in <code class="language-plaintext highlighter-rouge">Control</code> we see that:</p>

<blockquote>
  <p>This method returns a List containing all CssMetaData for both this Control (returned from getControlCssMetaData() and its Skin, assuming the skin property is a SkinBase.</p>

  <p>Developers who wish to provide custom CssMetaData are therefore encouraged to override getControlCssMetaData() or SkinBase.getCssMetaData(), depending on where the CssMetaData resides.</p>
</blockquote>

<p>Since we aren’t working with the skin of <code class="language-plaintext highlighter-rouge">Label</code>, we can override <code class="language-plaintext highlighter-rouge">getControlCssMetaData()</code>:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">override</span> <span class="k">fun</span> <span class="nf">getControlCssMetaData</span><span class="p">()</span> <span class="p">=</span> <span class="nf">getClassCssMetaData</span><span class="p">()</span>
</code></pre></div></div>

<p>The next step deals more with inheritability.  You can see that our instance method, <code class="language-plaintext highlighter-rouge">getControlCssMetaData()</code> is just calling the static <code class="language-plaintext highlighter-rouge">getClassCssMetaData()</code>.  We need to work with this static method to make sure that we pull in all of the <code class="language-plaintext highlighter-rouge">CssMetaData</code> from the parent class, <code class="language-plaintext highlighter-rouge">Label</code>, and then add our new, custom, <code class="language-plaintext highlighter-rouge">CssMetaData</code> to it.  Then, if someone extends <code class="language-plaintext highlighter-rouge">DecimalLabel</code>, they can follow the same methodology and inherit all the <code class="language-plaintext highlighter-rouge">CssMetaData</code> in <code class="language-plaintext highlighter-rouge">DecimalLabel</code> as well as <code class="language-plaintext highlighter-rouge">Label</code>.</p>

<p>I’ve followed the same convention as the JavaDocs here, putting the cumulative <code class="language-plaintext highlighter-rouge">CssMetaData</code> into a static <code class="language-plaintext highlighter-rouge">List</code> variable, and then returning that from <code class="language-plaintext highlighter-rouge">getClassCssMetaData()</code>.  I think this is going to be more readable, especially if we add more custom <code class="language-plaintext highlighter-rouge">CssMetaData</code>.</p>

<p>We also want to allow the <code class="language-plaintext highlighter-rouge">StyleleableProperty</code> to be accessible from layout code, but we don’t want to expose it as a <code class="language-plaintext highlighter-rouge">StyleableProperty</code> to that code, we just want it to be <code class="language-plaintext highlighter-rouge">ObjectProperty&lt;Number&gt;</code>.  In Java you would create the standard three methods to get/set the value and return the <code class="language-plaintext highlighter-rouge">Property</code>, but in Kotlin you create a (Kotlin) property of the enclosed type and then delegate its get() and set() methods to the <code class="language-plaintext highlighter-rouge">StyleableProperty</code>.  This will repsond to <code class="language-plaintext highlighter-rouge">getDecimalPlaces()</code> as well as <code class="language-plaintext highlighter-rouge">decimalPlaces = x</code>.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">DecimalLabel</span><span class="p">(</span><span class="n">value</span><span class="p">:</span> <span class="nc">ObservableDoubleValue</span><span class="p">)</span> <span class="p">:</span> <span class="nc">Label</span><span class="p">()</span> <span class="p">{</span>

    <span class="k">private</span> <span class="kd">val</span> <span class="py">decimalPlacesSP</span><span class="p">:</span> <span class="nc">StyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">Number</span><span class="p">&gt;</span> <span class="p">=</span>
        <span class="nc">SimpleStyleableObjectProperty</span><span class="p">(</span><span class="nc">DECIMAL_PLACES_META_DATA</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
    <span class="kd">var</span> <span class="py">decimalPlaces</span> <span class="p">:</span> <span class="nc">Int</span>
        <span class="k">get</span><span class="p">()</span> <span class="p">=</span> <span class="n">decimalPlacesSP</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="nf">toInt</span><span class="p">()</span>
        <span class="k">set</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="p">=</span> <span class="n">decimalPlacesSP</span><span class="p">.</span><span class="k">set</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
    <span class="k">fun</span> <span class="nf">decimalPlacesProperty</span><span class="p">()</span> <span class="p">:</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">Number</span><span class="p">&gt;</span> <span class="p">=</span> <span class="n">decimalPlacesSP</span>

    <span class="k">companion</span> <span class="k">object</span> <span class="nc">CssStuff</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">negativePseudoClass</span> <span class="p">=</span> <span class="nc">PseudoClass</span><span class="p">.</span><span class="nf">getPseudoClass</span><span class="p">(</span><span class="s">"negative"</span><span class="p">)</span>
        <span class="kd">val</span> <span class="py">DECIMAL_PLACES_META_DATA</span> <span class="p">=</span>
            <span class="kd">object</span> <span class="err">: </span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">DecimalLabel</span><span class="p">,</span> <span class="nc">Number</span><span class="p">&gt;(</span><span class="s">"-wfx-decimal-places"</span><span class="p">,</span> <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getSizeConverter</span><span class="p">(),</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">DecimalLabel</span><span class="p">):</span> <span class="nc">Boolean</span> <span class="p">=</span> <span class="p">!</span><span class="n">p0</span><span class="p">.</span><span class="n">decimalPlacesSP</span><span class="p">.</span><span class="n">isBound</span>

                <span class="k">override</span> <span class="k">fun</span> <span class="nf">getStyleableProperty</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">DecimalLabel</span><span class="p">):</span> <span class="nc">StyleableProperty</span><span class="p">&lt;</span><span class="nc">Number</span><span class="p">&gt;</span> <span class="p">=</span> <span class="n">p0</span><span class="p">.</span><span class="n">decimalPlacesSP</span>
            <span class="p">}</span>

        <span class="k">private</span> <span class="kd">val</span> <span class="py">cssMetaDataList</span> <span class="p">=</span>
            <span class="p">(</span><span class="nc">Label</span><span class="p">.</span><span class="nf">getClassCssMetaData</span><span class="p">()</span> <span class="p">+</span> <span class="nc">DECIMAL_PLACES_META_DATA</span><span class="p">)</span> <span class="k">as</span> <span class="nc">MutableList</span>

        <span class="k">fun</span> <span class="nf">getClassCssMetaData</span><span class="p">()</span> <span class="p">=</span> <span class="n">cssMetaDataList</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">getControlCssMetaData</span><span class="p">()</span> <span class="p">=</span> <span class="nf">getClassCssMetaData</span><span class="p">()</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"decimal-label"</span>
        <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="nc">ConversionBinding</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">decimalPlacesSP</span><span class="p">))</span>
        <span class="n">alignment</span> <span class="p">=</span> <span class="nc">Pos</span><span class="p">.</span><span class="nc">CENTER_RIGHT</span>
        <span class="n">value</span><span class="p">.</span><span class="nf">subscribe</span> <span class="p">{</span> <span class="n">newVal</span> <span class="p">-&gt;</span> <span class="nf">pseudoClassStateChanged</span><span class="p">(</span><span class="n">negativePseudoClass</span><span class="p">,</span> <span class="n">newVal</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">()</span> <span class="p">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">class</span> <span class="nc">ConversionBinding</span><span class="p">(</span>
        <span class="k">private</span> <span class="kd">val</span> <span class="py">labelValue</span><span class="p">:</span> <span class="nc">ObservableDoubleValue</span><span class="p">,</span>
        <span class="k">private</span> <span class="kd">val</span> <span class="py">decimalPlaces</span><span class="p">:</span> <span class="nc">ObservableValue</span><span class="p">&lt;</span><span class="nc">Number</span><span class="p">&gt;</span>
    <span class="p">)</span> <span class="p">:</span>
        <span class="nc">StringBinding</span><span class="p">()</span> <span class="p">{</span>
        <span class="nf">init</span> <span class="p">{</span>
            <span class="k">super</span><span class="p">.</span><span class="nf">bind</span><span class="p">(</span><span class="n">labelValue</span><span class="p">,</span> <span class="n">decimalPlaces</span><span class="p">)</span>
        <span class="p">}</span>

        <span class="k">override</span> <span class="k">fun</span> <span class="nf">computeValue</span><span class="p">():</span> <span class="nc">String</span> <span class="p">{</span>
            <span class="k">return</span> <span class="nc">String</span><span class="p">.</span><span class="nf">format</span><span class="p">(</span>
                <span class="s">"%,.${decimalPlaces.value.toInt()}f"</span><span class="p">,</span>
                <span class="n">labelValue</span><span class="p">.</span><span class="n">value</span>
            <span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The other thing that I’ve done here is to introduce a <code class="language-plaintext highlighter-rouge">PseudoClass</code> for when the value is negative.  This will allow us to style the <code class="language-plaintext highlighter-rouge">DecimalLabel</code> differently when the value is below zero.  To enable this, I’ve added a subcription to the <code class="language-plaintext highlighter-rouge">value ObservableValue</code> which calls <code class="language-plaintext highlighter-rouge">pseudoClassStateChanged()</code>.</p>

<p>If we use this style sheet:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.decimal-label</span> <span class="p">{</span>
  <span class="py">-fx-border-width</span><span class="p">:</span> <span class="m">1px</span><span class="p">;</span>
  <span class="py">-fx-border-color</span><span class="p">:</span> <span class="no">black</span><span class="p">;</span>
  <span class="py">-fx-min-width</span><span class="p">:</span> <span class="m">5em</span><span class="p">;</span>
  <span class="py">-wfx-decimal-places</span><span class="p">:</span> <span class="m">3</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.decimal-label</span><span class="o">:</span> <span class="nt">negative</span> <span class="p">{</span>
  <span class="py">-fx-text-fill</span><span class="p">:</span> <span class="no">red</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Then we get this result:</p>

<p><img src="/assets/elements/StyleableProperties6.png" alt="Labels with 3 Decimal Places" /></p>

<h2 id="styling-negative-values">Styling Negative Values</h2>

<p>There are two standard stylings for negative values.  One is to prefix the number with “-“ and the other is to surround the number with “()”.  In order to enable this as a choice, we’ll create an <code class="language-plaintext highlighter-rouge">Enum</code> for this called <code class="language-plaintext highlighter-rouge">NegativeFormat</code> with two values <code class="language-plaintext highlighter-rouge">SIGN</code> and <code class="language-plaintext highlighter-rouge">PAREN</code>.</p>

<p>The rest of this is pretty much rinse and repeat of the process for decimal places.  We add a new <code class="language-plaintext highlighter-rouge">StyleableProperty&lt;NegativeFormat&gt;</code> and then add the elements to expose it to client code.  Then we create the associated <code class="language-plaintext highlighter-rouge">CssMetaData</code> and add it to <code class="language-plaintext highlighter-rouge">getClassCssMetaData()</code>.  It looks like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">DecimalLabel</span><span class="p">(</span><span class="n">value</span><span class="p">:</span> <span class="nc">ObservableDoubleValue</span><span class="p">)</span> <span class="p">:</span> <span class="nc">Label</span><span class="p">()</span> <span class="p">{</span>

    <span class="k">private</span> <span class="kd">val</span> <span class="py">decimalPlacesSP</span><span class="p">:</span> <span class="nc">StyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">Number</span><span class="p">&gt;</span> <span class="p">=</span>
        <span class="nc">SimpleStyleableObjectProperty</span><span class="p">(</span><span class="nc">DECIMAL_PLACES_META_DATA</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
    <span class="kd">var</span> <span class="py">decimalPlaces</span><span class="p">:</span> <span class="nc">Int</span>
        <span class="k">get</span><span class="p">()</span> <span class="p">=</span> <span class="n">decimalPlacesSP</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="nf">toInt</span><span class="p">()</span>
        <span class="k">set</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="p">=</span> <span class="n">decimalPlacesSP</span><span class="p">.</span><span class="k">set</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>

    <span class="k">fun</span> <span class="nf">decimalPlacesProperty</span><span class="p">():</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">Number</span><span class="p">&gt;</span> <span class="p">=</span> <span class="n">decimalPlacesSP</span>

    <span class="k">private</span> <span class="kd">val</span> <span class="py">negativeFormatSP</span><span class="p">:</span> <span class="nc">StyleableObjectProperty</span><span class="p">&lt;</span><span class="nc">NegativeFormat</span><span class="p">&gt;</span> <span class="p">=</span>
        <span class="nc">SimpleStyleableObjectProperty</span><span class="p">(</span><span class="nc">NEGATIVE_FORMAT_META_DATA</span><span class="p">,</span> <span class="nc">NegativeFormat</span><span class="p">.</span><span class="nc">SIGN</span><span class="p">)</span>
    <span class="kd">var</span> <span class="py">negativeFormat</span><span class="p">:</span> <span class="nc">NegativeFormat</span>
        <span class="k">get</span><span class="p">()</span> <span class="p">=</span> <span class="n">negativeFormatSP</span><span class="p">.</span><span class="n">value</span>
        <span class="k">set</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="p">=</span> <span class="n">negativeFormatSP</span><span class="p">.</span><span class="k">set</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>

    <span class="k">fun</span> <span class="nf">negativeFormatProperty</span><span class="p">():</span> <span class="nc">ObjectProperty</span><span class="p">&lt;</span><span class="nc">NegativeFormat</span><span class="p">&gt;</span> <span class="p">=</span> <span class="n">negativeFormatSP</span>

    <span class="k">companion</span> <span class="k">object</span> <span class="nc">CssStuff</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">negativePseudoClass</span><span class="p">:</span> <span class="nc">PseudoClass</span> <span class="p">=</span> <span class="nc">PseudoClass</span><span class="p">.</span><span class="nf">getPseudoClass</span><span class="p">(</span><span class="s">"negative"</span><span class="p">)</span>
        <span class="kd">val</span> <span class="py">DECIMAL_PLACES_META_DATA</span> <span class="p">=</span>
            <span class="kd">object</span> <span class="err">: </span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">DecimalLabel</span><span class="p">,</span> <span class="nc">Number</span><span class="p">&gt;(</span><span class="s">"-wfx-decimal-places"</span><span class="p">,</span> <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getSizeConverter</span><span class="p">(),</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">DecimalLabel</span><span class="p">):</span> <span class="nc">Boolean</span> <span class="p">=</span> <span class="p">!</span><span class="n">p0</span><span class="p">.</span><span class="n">decimalPlacesSP</span><span class="p">.</span><span class="n">isBound</span>

                <span class="k">override</span> <span class="k">fun</span> <span class="nf">getStyleableProperty</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">DecimalLabel</span><span class="p">):</span> <span class="nc">StyleableProperty</span><span class="p">&lt;</span><span class="nc">Number</span><span class="p">&gt;</span> <span class="p">=</span> <span class="n">p0</span><span class="p">.</span><span class="n">decimalPlacesSP</span>
            <span class="p">}</span>
        <span class="kd">val</span> <span class="py">NEGATIVE_FORMAT_META_DATA</span> <span class="p">=</span>
            <span class="kd">object</span> <span class="err">: </span><span class="nc">CssMetaData</span><span class="p">&lt;</span><span class="nc">DecimalLabel</span><span class="p">,</span> <span class="nc">NegativeFormat</span><span class="p">&gt;(</span>
                <span class="s">"-wfx-negative-format"</span><span class="p">,</span>
                <span class="nc">StyleConverter</span><span class="p">.</span><span class="nf">getEnumConverter</span><span class="p">(</span><span class="nc">NegativeFormat</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">),</span>
                <span class="nc">NegativeFormat</span><span class="p">.</span><span class="nc">SIGN</span>
            <span class="p">)</span> <span class="p">{</span>
                <span class="k">override</span> <span class="k">fun</span> <span class="nf">isSettable</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">DecimalLabel</span><span class="p">):</span> <span class="nc">Boolean</span> <span class="p">=</span> <span class="p">!</span><span class="n">p0</span><span class="p">.</span><span class="n">negativeFormatSP</span><span class="p">.</span><span class="n">isBound</span>

                <span class="k">override</span> <span class="k">fun</span> <span class="nf">getStyleableProperty</span><span class="p">(</span><span class="n">p0</span><span class="p">:</span> <span class="nc">DecimalLabel</span><span class="p">):</span> <span class="nc">StyleableProperty</span><span class="p">&lt;</span><span class="nc">NegativeFormat</span><span class="p">&gt;</span> <span class="p">=</span>
                    <span class="n">p0</span><span class="p">.</span><span class="n">negativeFormatSP</span>
            <span class="p">}</span>

        <span class="k">private</span> <span class="kd">val</span> <span class="py">cssMetaDataList</span> <span class="p">=</span>
            <span class="p">(</span><span class="nc">Label</span><span class="p">.</span><span class="nf">getClassCssMetaData</span><span class="p">()</span> <span class="p">+</span> <span class="nc">DECIMAL_PLACES_META_DATA</span> <span class="p">+</span> <span class="nc">NEGATIVE_FORMAT_META_DATA</span><span class="p">)</span> <span class="k">as</span> <span class="nc">MutableList</span>

        <span class="k">fun</span> <span class="nf">getClassCssMetaData</span><span class="p">()</span> <span class="p">=</span> <span class="n">cssMetaDataList</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">getControlCssMetaData</span><span class="p">()</span> <span class="p">=</span> <span class="nf">getClassCssMetaData</span><span class="p">()</span>

    <span class="nf">init</span> <span class="p">{</span>
        <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"decimal-label"</span>
        <span class="nf">textProperty</span><span class="p">().</span><span class="nf">bind</span><span class="p">(</span><span class="nc">ConversionBinding</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">decimalPlacesSP</span><span class="p">,</span> <span class="n">negativeFormatSP</span><span class="p">))</span>
        <span class="n">alignment</span> <span class="p">=</span> <span class="nc">Pos</span><span class="p">.</span><span class="nc">CENTER_RIGHT</span>
        <span class="n">value</span><span class="p">.</span><span class="nf">subscribe</span> <span class="p">{</span> <span class="n">newVal</span> <span class="p">-&gt;</span> <span class="nf">pseudoClassStateChanged</span><span class="p">(</span><span class="n">negativePseudoClass</span><span class="p">,</span> <span class="n">newVal</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">()</span> <span class="p">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">enum</span> <span class="kd">class</span> <span class="nc">NegativeFormat</span> <span class="p">{</span> <span class="nc">SIGN</span><span class="p">,</span> <span class="nc">PAREN</span> <span class="p">}</span>

    <span class="kd">class</span> <span class="nc">ConversionBinding</span><span class="p">(</span>
        <span class="k">private</span> <span class="kd">val</span> <span class="py">labelValue</span><span class="p">:</span> <span class="nc">ObservableDoubleValue</span><span class="p">,</span>
        <span class="k">private</span> <span class="kd">val</span> <span class="py">decimalPlaces</span><span class="p">:</span> <span class="nc">ObservableValue</span><span class="p">&lt;</span><span class="nc">Number</span><span class="p">&gt;,</span>
        <span class="k">private</span> <span class="kd">val</span> <span class="py">negativeFormat</span><span class="p">:</span> <span class="nc">ObservableValue</span><span class="p">&lt;</span><span class="nc">NegativeFormat</span><span class="p">&gt;</span>
    <span class="p">)</span> <span class="p">:</span>
        <span class="nc">StringBinding</span><span class="p">()</span> <span class="p">{</span>
        <span class="nf">init</span> <span class="p">{</span>
            <span class="k">super</span><span class="p">.</span><span class="nf">bind</span><span class="p">(</span><span class="n">labelValue</span><span class="p">,</span> <span class="n">decimalPlaces</span><span class="p">)</span>
        <span class="p">}</span>

        <span class="k">override</span> <span class="k">fun</span> <span class="nf">computeValue</span><span class="p">():</span> <span class="nc">String</span> <span class="p">{</span>
            <span class="k">return</span> <span class="nc">String</span><span class="p">.</span><span class="nf">format</span><span class="p">(</span>
                <span class="s">"%${if (negativeFormat.value == NegativeFormat.PAREN) "</span><span class="p">(</span><span class="s">" else ""},."</span> <span class="p">+</span>
                        <span class="s">"${decimalPlaces.value.toInt()}f"</span><span class="p">,</span>
                <span class="n">labelValue</span><span class="p">.</span><span class="n">value</span>
            <span class="p">)</span>
        <span class="p">}</span>

    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">Binding</code> has been updated to take the new <code class="language-plaintext highlighter-rouge">ObservableValue&lt;NegativeFormat&gt;</code> as a parameter, and it uses it to decide to add a “(“ at the beginning of the formatting string.</p>

<p>With the style sheet updated to look like this:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.decimal-label</span> <span class="p">{</span>
  <span class="py">-fx-border-width</span><span class="p">:</span> <span class="m">1px</span><span class="p">;</span>
  <span class="py">-fx-border-color</span><span class="p">:</span> <span class="no">black</span><span class="p">;</span>
  <span class="py">-fx-min-width</span><span class="p">:</span> <span class="m">12ex</span><span class="p">;</span>
  <span class="py">-wfx-decimal-places</span><span class="p">:</span> <span class="m">2</span><span class="p">;</span>
  <span class="py">-wfx-negative-format</span><span class="p">:</span> <span class="n">paren</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.decimal-label</span><span class="o">:</span> <span class="nt">negative</span> <span class="p">{</span>
  <span class="py">-fx-text-fill</span><span class="p">:</span> <span class="no">red</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The output looks like this:</p>

<p><img src="/assets/elements/StyleableProperties7.png" alt="Output with Parenthesis Around Negatives" /></p>

<p>There’s one small problem with this.  The closing “)” shifts the negative number over one, so that the number columns don’t line up nicely.  We need to change the formatting string like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">override</span> <span class="k">fun</span> <span class="nf">computeValue</span><span class="p">():</span> <span class="nc">String</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nc">String</span><span class="p">.</span><span class="nf">format</span><span class="p">(</span>
        <span class="s">"%${if (negativeFormat.value == NegativeFormat.PAREN) "</span><span class="p">(</span><span class="s">" else ""},."</span> <span class="p">+</span>
                <span class="s">"${decimalPlaces.value.toInt()}f"</span> <span class="p">+</span>
                <span class="k">if</span> <span class="p">((</span><span class="n">labelValue</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="nf">toFloat</span><span class="p">()</span> <span class="p">&gt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">&amp;&amp;</span> <span class="p">(</span><span class="n">negativeFormat</span><span class="p">.</span><span class="n">value</span> <span class="p">==</span> <span class="nc">NegativeFormat</span><span class="p">.</span><span class="nc">PAREN</span><span class="p">))</span> <span class="s">" "</span> <span class="k">else</span> <span class="s">""</span><span class="p">,</span>
        <span class="n">labelValue</span><span class="p">.</span><span class="n">value</span>
    <span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now we put an extra “ “ at the end of the number if it isn’t negative.  This will be displayed only if the closing “)” isn’t shown.  Now it looks like this:</p>

<p><img src="/assets/elements/StyleableProperties8.png" alt="Negative Numbers" /></p>

<p>And the columns line up nicely.  With the borders removed, it looks like this:</p>

<p><img src="/assets/elements/StyleableProperties9.png" alt="Final Version Output" /></p>

<p>And fulfills the requirements.  There are more <code class="language-plaintext highlighter-rouge">StyleableProperties</code> that you could add.  I’ve formatted the output with “,” between the thousands, but you could implement that as a <code class="language-plaintext highlighter-rouge">Boolean</code> styleable value to turn them on or off.  You could create a <code class="language-plaintext highlighter-rouge">StyleableStringProperty</code> to allow the actual format string to be defined via CSS.</p>

<p>You could decide that you don’t like the implementation of negative as a <code class="language-plaintext highlighter-rouge">PseudoClass</code> and that you’d rather just control the colour via another <code class="language-plaintext highlighter-rouge">StyleableProperty</code>.  Something like <code class="language-plaintext highlighter-rouge">colourWhenNegative</code>.  That would work just fine, too.</p>

<h1 id="conclusion">Conclusion</h1>

<p>One thing that I’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’s especially frustating because the bits that do the work are buried deep inside JavaFX, and you don’t get to see them working.  This means that there’s no feedback, nothing that says, “This part broke”.  It just doesn’t work, and the lines that you put into your style sheet aren’t reflected in the GUI when it’s running.</p>

<p>Things that I found helpful:</p>

<ul>
  <li>Call <code class="language-plaintext highlighter-rouge">getCssMetaData()</code> and print the output to the console.  Make sure that your custom elements are listed.</li>
  <li>Add a <code class="language-plaintext highlighter-rouge">Subscription</code> to your <code class="language-plaintext highlighter-rouge">StyleableProperties</code> so that you can see when they change.</li>
  <li>Make sure that your custom attributes are applied to the correct selectors in your style sheet.</li>
  <li>Double-check the spelling of the attribute names in both the <code class="language-plaintext highlighter-rouge">CssMetaData</code> and the style sheet.</li>
  <li>Put unconvertable values in the attributes in the style sheet.  This will tell you if JavaFX is trying to apply it.</li>
</ul>

<p>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 <code class="language-plaintext highlighter-rouge">Label</code> subclass for handling values with special display critea, like decimals, integers and dates is generally useful, in my opinion.  JavaFX doesn’t supply those, but it does give you all the tools you need to implement them the way that you want to.</p>

<p>Layout-centric custom styling is an idea that has been completely ignored in the JavaFX library.  It’s also harder to decide if something should be configured via CSS or just implemented via code - especially since you’re already there writing code in the first place.  However, if you are just changing the presentation of a layout…code that hasn’t been modified is code that doesn’t need to be tested (as much).  This isn’t to say that you can’t mess up a screen by changing the style sheet, but at least you know that he logic behind the “Save” <code class="language-plaintext highlighter-rouge">Button</code> doesn’t need to be retested, because no code in that class has been changed.</p>]]></content><author><name>Dave Barrett</name><email>pragmaticcoding8@gmail.com</email></author><category term="javafx" /><summary type="html"><![CDATA[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.]]></summary></entry><entry><title type="html">JavaFX Styling Guide: TableView</title><link href="https://www.pragmaticcoding.ca/javafx/elements/styling-guide-tableView" rel="alternate" type="text/html" title="JavaFX Styling Guide: TableView" /><published>2024-12-08T17:00:00+00:00</published><updated>2024-12-08T17:00:00+00:00</updated><id>https://www.pragmaticcoding.ca/javafx/elements/styling-tables</id><content type="html" xml:base="https://www.pragmaticcoding.ca/javafx/elements/styling-guide-tableView"><![CDATA[<h1 id="introduction">Introduction</h1>

<p><code class="language-plaintext highlighter-rouge">TableView</code> is an extremely popular control, but it’s also probably the most complex and least understood.  This article is about how to manage the styling aspects of <code class="language-plaintext highlighter-rouge">TableView</code>, but it would help if you understood the basics.  This <a href="/javafx/elements/tableview_listview">page</a> has several articles that explain how to create and customize <code class="language-plaintext highlighter-rouge">TableViews</code> in your layouts.  If you are not comfortable with CSS in JavaFX and the Modena stylesheet, <a href="/javafx/elements/stylesheets_pseudoclasses">this page</a> has articles that can help you.</p>

<p><code class="language-plaintext highlighter-rouge">TableView</code> 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 <code class="language-plaintext highlighter-rouge">TableView</code> really means understanding how to style all of these components, and how they interact.  The approach in this article is to look at virtually <em>all</em> of the style class selectors of all of the components and to examine how they are used.</p>

<p>This 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 <code class="language-plaintext highlighter-rouge">TableView</code> 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 <code class="language-plaintext highlighter-rouge">TableView</code>.</p>

<p>There’s not much code in the article, but what code there is, is written in <a href="/kotlin/intro">Kotlin</a>.</p>

<h2 id="a-note-on-selectors-in-the-article">A Note on Selectors in the Article</h2>

<p>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 <code class="language-plaintext highlighter-rouge">.table-view</code>.</p>

<p>In practice, it is probably better to add a custom style class selector to your <code class="language-plaintext highlighter-rouge">TableView</code> and then use that as the root of your selectors.  For instance, the <code class="language-plaintext highlighter-rouge">column-header</code> selector is used with both <code class="language-plaintext highlighter-rouge">TreeView</code> and <code class="language-plaintext highlighter-rouge">TableView</code>.  Let’s say that you want to style the <code class="language-plaintext highlighter-rouge">TableColumnHeaders</code> for a <code class="language-plaintext highlighter-rouge">TableView</code> in your layout…</p>

<p>Start by assigning a selector to the <code class="language-plaintext highlighter-rouge">TableView</code>, let’s say <code class="language-plaintext highlighter-rouge">my-table</code>.  Then you would use:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.my-table</span> <span class="nc">.column-header</span> <span class="p">{}</span>
</code></pre></div></div>
<p>instead of:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.table-view</span> <span class="nc">.column-header</span> <span class="p">{}</span>
</code></pre></div></div>
<p>However, in this article you will only see the second form used.</p>

<h1 id="tutorials">Tutorials</h1>

<p>One of the biggest challenges with styling <code class="language-plaintext highlighter-rouge">TableView</code>, apart from the fact that it’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.</p>

<p>The overall structure of <code class="language-plaintext highlighter-rouge">TableView</code> is that it is composed of two main parts:  The header row, and the <code class="language-plaintext highlighter-rouge">VirtualFlow</code> which contains everything else.  Inside the <code class="language-plaintext highlighter-rouge">VirtualFlow</code> there are the <code class="language-plaintext highlighter-rouge">ScrollBars</code> if required, and a clipping rectangle holding a <code class="language-plaintext highlighter-rouge">Group</code> called <code class="language-plaintext highlighter-rouge">sheet</code>.  Inside <code class="language-plaintext highlighter-rouge">sheet</code> are the <code class="language-plaintext highlighter-rouge">TableRows</code> which in turn hold the <code class="language-plaintext highlighter-rouge">TableCells</code>.</p>

<p>Every component, from <code class="language-plaintext highlighter-rouge">TableView</code>, through <code class="language-plaintext highlighter-rouge">VirtualFlow</code> down to <code class="language-plaintext highlighter-rouge">TableCell</code> has style class selectors.  Often they have several selectors, and they may be shared with other components.</p>

<h2 id="tablerow-and-tablecell-styling">TableRow and TableCell Styling</h2>

<p>We’ll start by looking at the body of <code class="language-plaintext highlighter-rouge">TableView</code>, which are the elements contained inside <code class="language-plaintext highlighter-rouge">sheet</code>.</p>

<h3 id="standard-styling">Standard Styling</h3>

<p>The structure of the body of a <code class="language-plaintext highlighter-rouge">TableView</code> is a set of <code class="language-plaintext highlighter-rouge">TableRows</code>, each of which contains a number of <code class="language-plaintext highlighter-rouge">TableCells</code>.  This means that the <code class="language-plaintext highlighter-rouge">TableRow</code> is the enclosing layout component for all of the <code class="language-plaintext highlighter-rouge">TableCells</code>.  In standard Modena, the <code class="language-plaintext highlighter-rouge">TableCells</code> are normally transparent with a thin border on the right, resulting in much of the colour of the <code class="language-plaintext highlighter-rouge">TableCells</code> coming from the underlying <code class="language-plaintext highlighter-rouge">TableRow</code>:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.table-cell</span> <span class="p">{</span>
    <span class="py">-fx-padding</span><span class="p">:</span> <span class="m">0.166667em</span><span class="p">;</span> <span class="c">/* 2px, plus border adds 1px */</span>
    <span class="py">-fx-background-color</span><span class="p">:</span> <span class="n">null</span><span class="p">;</span>
    <span class="py">-fx-border-color</span><span class="p">:</span> <span class="nb">transparent</span> <span class="n">-fx-table-cell-border-color</span> <span class="nb">transparent</span> <span class="nb">transparent</span><span class="p">;</span>
    <span class="py">-fx-cell-size</span><span class="p">:</span> <span class="m">2.0em</span><span class="p">;</span> <span class="c">/* 24 */</span>
    <span class="py">-fx-text-fill</span><span class="p">:</span> <span class="n">-fx-text-background-color</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The main selector for <code class="language-plaintext highlighter-rouge">TableRow</code> is <code class="language-plaintext highlighter-rouge">.table-row-cell</code> and supports the <code class="language-plaintext highlighter-rouge">odd</code> and <code class="language-plaintext highlighter-rouge">even</code> Pseudo-classes from <code class="language-plaintext highlighter-rouge">IndexedCell</code>:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.table-row-cell</span> <span class="p">{</span>
    <span class="py">-fx-background</span><span class="p">:</span> <span class="n">-fx-control-inner-background</span><span class="p">;</span>
    <span class="py">-fx-background-color</span><span class="p">:</span> <span class="n">-fx-table-cell-border-color</span><span class="p">,</span> <span class="n">-fx-background</span><span class="p">;</span>
    <span class="py">-fx-background-insets</span><span class="p">:</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span> <span class="m">0</span> <span class="m">1</span> <span class="m">0</span><span class="p">;</span>
    <span class="py">-fx-padding</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
    <span class="py">-fx-text-fill</span><span class="p">:</span> <span class="n">-fx-text-background-color</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.table-row-cell</span><span class="nd">:odd</span> <span class="p">{</span>
    <span class="py">-fx-background</span><span class="p">:</span> <span class="n">-fx-control-inner-background-alt</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Where:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">-fx-control-inner-background-alt</span><span class="o">:</span> <span class="nt">derive</span><span class="o">(</span><span class="nt">-fx-control-inner-background</span><span class="o">,</span><span class="nt">-2</span><span class="o">%)</span>
</code></pre></div></div>
<p>And this is what you see in an otherwise unstyled <code class="language-plaintext highlighter-rouge">TableView</code>, the cell text sitting on alternating rows of grey and slightly-darker-grey.</p>

<p>Primarily, in Modena you’ll see styling for <code class="language-plaintext highlighter-rouge">TableRows</code> that have the Pseudo-class <code class="language-plaintext highlighter-rouge">selected</code> (often combined with <code class="language-plaintext highlighter-rouge">filled</code>):</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">.</span><span class="nc">.table-view</span><span class="nd">:focused</span> <span class="o">&gt;</span> <span class="nc">.virtual-flow</span> <span class="o">&gt;</span> <span class="nc">.clipped-container</span> <span class="o">&gt;</span> <span class="nc">.sheet</span> <span class="o">&gt;</span> <span class="nc">.table-row-cell</span><span class="nd">:filled:selected</span> <span class="p">{</span>
  <span class="py">-fx-background</span><span class="p">:</span> <span class="n">-fx-selection-bar</span><span class="p">;</span>
  <span class="py">-fx-table-cell-border-color</span><span class="p">:</span> <span class="n">derive</span><span class="p">(</span><span class="n">-fx-selection-bar</span><span class="p">,</span> <span class="m">20%</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Which is the bluish colour you’re familiar with:</p>

<p><img src="/assets/elements/TableStyle22.png" alt="Focused TableView with Selection" /></p>

<p>With the focus somewhere else, it looks like this:</p>

<p><img src="/assets/elements/TableStyle23.png" alt="Unfocused TableView with Selection" /></p>

<h3 id="custom-cell-styling">Custom Cell Styling</h3>

<p>For sure, you can create a <code class="language-plaintext highlighter-rouge">CellFactory</code> 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 <code class="language-plaintext highlighter-rouge">TableCells</code> in a particular column without doing all that.  Primarily, that you don’t have to create a custom <code class="language-plaintext highlighter-rouge">CellFactory</code> to do it.</p>

<p>Also, it’s probably a good practice to set up a bunch of re-usable custom <code class="language-plaintext highlighter-rouge">TableColumns</code> that deal with particular data types that you commonly encounter.  For instance, you might want to create a <code class="language-plaintext highlighter-rouge">DateTableColumn</code> that handles date display in a standardized way, or a <code class="language-plaintext highlighter-rouge">MoneyTableColumn</code> or <code class="language-plaintext highlighter-rouge">IntegerTableColumn</code>.</p>

<p>In these cases, assign a style class selector to the cells in a <code class="language-plaintext highlighter-rouge">TableColumn</code> and all of the cells in that column will get that selector.  This includes the <code class="language-plaintext highlighter-rouge">TabelColumnHeader</code> as well.  You can exclude the <code class="language-plaintext highlighter-rouge">TableColumnHeader</code> in your style sheet by specifying your selector as a sub-component of <code class="language-plaintext highlighter-rouge">.table-row-cell</code>.  This would typically be one of these forms:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.table-row-cell</span> <span class="nc">.my-selector</span> <span class="p">{}</span>
<span class="nc">.table-view</span> <span class="nc">.table-row-cell</span> <span class="nc">.my-selector</span> <span class="p">{}</span>
<span class="nc">.table-view</span> <span class="o">&gt;</span> <span class="nc">.virtual-flow</span> <span class="o">&gt;</span> <span class="nc">.clipped-container</span> <span class="o">&gt;</span> <span class="nc">.sheet</span> <span class="o">&gt;</span> <span class="nc">.table-row-cell</span> <span class="nc">.my-selector</span> <span class="p">{}</span>
<span class="nc">.my-table-selector</span> <span class="nc">.table-row-cell</span> <span class="nc">.my-selector</span> <span class="p">{}</span>
</code></pre></div></div>

<p>You can use it like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">columns</span> <span class="p">+=</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">TableData1</span><span class="p">,</span> <span class="nc">String</span><span class="p">&gt;(</span><span class="s">"Column A"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
    <span class="nf">setCellValueFactory</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value1</span> <span class="p">}</span>
    <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"a-column"</span>
<span class="p">}</span>
</code></pre></div></div>
<p>and</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.table-row-cell</span> <span class="o">&gt;</span> <span class="nc">.a-column</span> <span class="p">{</span>
  <span class="py">-fx-background-color</span><span class="p">:</span> <span class="no">red</span><span class="p">;</span>
  <span class="py">-fx-background-insets</span><span class="p">:</span> <span class="m">0</span> <span class="m">0</span> <span class="m">1</span> <span class="m">0px</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Which looks like this:</p>

<p><img src="/assets/elements/TableStyle18.png" alt="Screen Snap 18" /></p>

<p>If you don’t specify <code class="language-plaintext highlighter-rouge">.table-row-cell</code> as the parent, you’ll get this:</p>

<p><img src="/assets/elements/TableStyle21.png" alt="Screen Snap 21" /></p>

<p>Which is probably not what you want.</p>

<p>Once you apply this kind of styling to a <code class="language-plaintext highlighter-rouge">TableCell</code>, you’ll have to contend with the fact that its non-transparency will mess with the <code class="language-plaintext highlighter-rouge">TableRow</code> styling underneath, particularly with selected <code class="language-plaintext highlighter-rouge">TableRows</code>:</p>

<p><img src="/assets/elements/TableStyle24.png" alt="Screen Snap 24" /></p>

<p>One way to deal with this is to add a custom styling for <code class="language-plaintext highlighter-rouge">a-column</code> as a child of a <code class="language-plaintext highlighter-rouge">TableRow</code> with the <code class="language-plaintext highlighter-rouge">selected</code> Pseudo-class applied:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.table-row-cell</span><span class="nd">:selected</span> <span class="o">&gt;</span> <span class="nc">.a-column</span> <span class="p">{</span>
  <span class="py">-fx-background-color</span><span class="p">:</span> <span class="m">#ee000070</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Here, the background colour has had its opacity reduced:</p>

<p><img src="/assets/elements/TableStyle25.png" alt="Screen Snap 25" /></p>

<h3 id="row-styling-based-on-data-values">Row Styling Based on Data Values</h3>

<p><code class="language-plaintext highlighter-rouge">TableRow</code> inherits from <code class="language-plaintext highlighter-rouge">Cell</code> through <code class="language-plaintext highlighter-rouge">IndexedCell</code>, which means that it supports customization just like <code class="language-plaintext highlighter-rouge">TableCell</code>.  However, while you are not meant to mess about with the layout of <code class="language-plaintext highlighter-rouge">TableRow</code>, you can add other custom features like Pseudo-classes and behaviours based on data in the <code class="language-plaintext highlighter-rouge">TableRow</code>.</p>

<p>Just like <code class="language-plaintext highlighter-rouge">TableCell</code>, enough <code class="language-plaintext highlighter-rouge">TableRows</code> are created to fill the viewport of the <code class="language-plaintext highlighter-rouge">TableView</code> and then they are recycled and reloaded with data as the user scrolls through the <code class="language-plaintext highlighter-rouge">TableView</code>.  The key method here is <code class="language-plaintext highlighter-rouge">Cell.updateItem()</code> which you can customize as required.  Let’s look at how to add a <code class="language-plaintext highlighter-rouge">PseudoClass</code> to the <code class="language-plaintext highlighter-rouge">TableRow</code> and then link it to an element in the <code class="language-plaintext highlighter-rouge">TableRow</code> data:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">CustomTableRow</span> <span class="p">:</span> <span class="nc">TableRow</span><span class="p">&lt;</span><span class="nc">TableData1</span><span class="p">&gt;()</span> <span class="p">{</span>
    <span class="k">companion</span> <span class="k">object</span> <span class="nc">PseudoClasses</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">specialPC</span> <span class="p">=</span> <span class="nc">PseudoClass</span><span class="p">.</span><span class="nf">getPseudoClass</span><span class="p">(</span><span class="s">"special"</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">updateItem</span><span class="p">(</span><span class="n">newValue</span><span class="p">:</span> <span class="nc">TableData1</span><span class="p">?,</span> <span class="n">empty</span><span class="p">:</span> <span class="nc">Boolean</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">super</span><span class="p">.</span><span class="nf">updateItem</span><span class="p">(</span><span class="n">newValue</span><span class="p">,</span> <span class="n">empty</span><span class="p">)</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">empty</span> <span class="p">||</span> <span class="n">newValue</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span> <span class="p">{</span>
            <span class="nf">pseudoClassStateChanged</span><span class="p">(</span><span class="n">specialPC</span><span class="p">,</span> <span class="k">false</span><span class="p">)</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="nf">pseudoClassStateChanged</span><span class="p">(</span><span class="n">specialPC</span><span class="p">,</span> <span class="n">newValue</span><span class="p">.</span><span class="n">value4</span><span class="p">.</span><span class="n">value</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The only customization that we’ve done here is to create a <code class="language-plaintext highlighter-rouge">PseudoClass</code> as a static member of the <code class="language-plaintext highlighter-rouge">CustomTableRow</code> and then used <code class="language-plaintext highlighter-rouge">CustomTableRow.updateItem()</code> to call <code class="language-plaintext highlighter-rouge">pseudoClassStateChanged()</code> according to the new data being loaded.</p>

<p>If we apply the following styling:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.table-row-cell</span><span class="nd">:special</span> <span class="p">{</span>
  <span class="py">-fx-background-color</span><span class="p">:</span> <span class="no">red</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>We get the following result:</p>

<p><img src="/assets/elements/TableStyle19.png" alt="Screen Snap 19" /></p>

<p>We don’t just have to style the entire row.  We can apply styling to a specific cell if we’ve added a style class selector to its <code class="language-plaintext highlighter-rouge">TableColumn</code> by doing this:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.table-row-cell</span><span class="nd">:special</span> <span class="o">&gt;</span> <span class="nc">.a-column</span> <span class="p">{</span>
   <span class="py">-fx-background-color</span><span class="p">:</span> <span class="no">red</span><span class="p">;</span>
   <span class="py">-fx-background-insets</span><span class="p">:</span> <span class="m">0</span> <span class="m">0</span> <span class="m">1</span> <span class="m">0px</span><span class="p">;</span>
 <span class="p">}</span>
</code></pre></div></div>
<p>Note that this is exactly the same as the example in the previous section except that we’ve applied the <code class="language-plaintext highlighter-rouge">special</code> Pseudo-class to <code class="language-plaintext highlighter-rouge">.table-row-cell</code> instead of <code class="language-plaintext highlighter-rouge">selected</code>:</p>

<p><img src="/assets/elements/TableStyle20.png" alt="Screen Snap 20" /></p>

<p>However, note that we have the same problem as before:</p>

<p><img src="/assets/elements/TableStyle26.png" alt="Screen Snap 26" /></p>

<p>But we can fix it by adding a new selector for <code class="language-plaintext highlighter-rouge">TableRow</code> with <em>both</em> Pseudo-classes applied and reducing the opacity again:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.table-row-cell</span><span class="nd">:selected:special</span> <span class="o">&gt;</span> <span class="nc">.a-column</span> <span class="p">{</span>
  <span class="py">-fx-background-color</span><span class="p">:</span> <span class="m">#ee000070</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p><img src="/assets/elements/TableStyle27.png" alt="Screen Snap 27" /></p>

<p>And you can see that the rows without the <code class="language-plaintext highlighter-rouge">special</code> Pseudo-class continue to work as normal:</p>

<p><img src="/assets/elements/TableStyle28.png" alt="Screen Snap 28" /></p>

<h2 id="accessing-specific-column-headers">Accessing Specific Column Headers</h2>

<p>Instances of <code class="language-plaintext highlighter-rouge">TableColumnHeader</code> are not going to be programmatically accessible to you, since they are purely internal to the <code class="language-plaintext highlighter-rouge">TableView</code>.  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 <code class="language-plaintext highlighter-rouge">TableColumn</code>, <code class="language-plaintext highlighter-rouge">TableRow</code> and <code class="language-plaintext highlighter-rouge">TableCell</code>.</p>

<p>Fortunately, a <code class="language-plaintext highlighter-rouge">TableColumnHeader</code> will automatically be assigned any style class selectors added to their associated <code class="language-plaintext highlighter-rouge">TableColumn</code>.  So the following code will add the selector, “a-column” to the <code class="language-plaintext highlighter-rouge">TabelColumnHeader</code> associated with this column.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">columns</span> <span class="p">+=</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">TableData</span><span class="p">,</span> <span class="nc">String</span><span class="p">&gt;(</span><span class="s">"Column A"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
    <span class="nf">setCellFactory</span> <span class="p">{</span> <span class="nc">CustomTableCell</span><span class="p">()</span> <span class="p">}</span>
    <span class="nf">setCellValueFactory</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value1</span> <span class="p">}</span>
    <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"a-column"</span>
<span class="p">}</span>
</code></pre></div></div>
<p>If we try to style this column directly using something like this:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.a-column</span> <span class="nc">.text</span> <span class="p">{</span>
  <span class="py">-fx-fill</span><span class="p">:</span> <span class="no">red</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>We get this:</p>

<p><img src="/assets/elements/TableStyle12.png" alt="Screen Snap 12" /></p>

<p>You can see that it’s styled all the <code class="language-plaintext highlighter-rouge">Text</code> in the column, and not just the column header.  However, if we use the fact that the column header is enclosed in the <code class="language-plaintext highlighter-rouge">TableHeaderRow</code>, which has the selector, “.column-header-background”, then we can isolate the column header:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.table-view</span> <span class="nc">.column-header-background</span> <span class="nc">.a-column</span> <span class="nc">.text</span> <span class="p">{</span>
  <span class="py">-fx-fill</span><span class="p">:</span> <span class="no">red</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Which looks like this:</p>

<p><img src="/assets/elements/TableStyle11.png" alt="Screen Snap 11" /></p>

<h2 id="nested-column-headers">Nested Column Headers</h2>

<p>In <code class="language-plaintext highlighter-rouge">TableView</code> all <code class="language-plaintext highlighter-rouge">TableColumns</code> are potentially parents of nested columns.  The entire header row is actually styled as a single nested column, and then using the <code class="language-plaintext highlighter-rouge">TableColumn.getColumns().add()</code> method on any <code class="language-plaintext highlighter-rouge">TableColumn</code> will generate another nested column inside the main nested column.  Rinse and repeat if you want nested nested columns.</p>

<p>The JavaFX terminology is that the parent column gets the term “Nested” even though the it’s the child columns that are nested.  The class for the parent column header is <code class="language-plaintext highlighter-rouge">NestedTableColumnHeader</code>, and it has a style sheet selector of <code class="language-plaintext highlighter-rouge">nested-column-header</code>.  Unfortunately, you can’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.</p>

<p>This presents a problem if you want to style the header for different nested columns differently, especially since JavaFX CSS doesn’t support sibling selectors.  Let’s look at this code:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">TableView</span><span class="p">&lt;</span><span class="nc">TableData</span><span class="p">&gt;().</span><span class="nf">apply</span> <span class="p">{</span>
   <span class="n">columns</span> <span class="p">+=</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">TableData</span><span class="p">,</span> <span class="nc">String</span><span class="p">&gt;(</span><span class="s">"Column A"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
      <span class="nf">setCellFactory</span> <span class="p">{</span> <span class="nc">CustomTableCell</span><span class="p">()</span> <span class="p">}</span>
      <span class="nf">setCellValueFactory</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value1</span> <span class="p">}</span>
  <span class="p">}</span>
  <span class="n">columns</span> <span class="p">+=</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">TableData</span><span class="p">,</span> <span class="nc">String</span><span class="p">&gt;(</span><span class="s">"B and C"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
      <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"bc-nested-column"</span>
      <span class="n">columns</span> <span class="p">+=</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">TableData</span><span class="p">,</span> <span class="nc">String</span><span class="p">&gt;(</span><span class="s">"Column B"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
          <span class="nf">setCellFactory</span> <span class="p">{</span> <span class="nc">CustomTableCell</span><span class="p">()</span> <span class="p">}</span>
          <span class="nf">setCellValueFactory</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value2</span> <span class="p">}</span>
          <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"bc-column"</span>
      <span class="p">}</span>
      <span class="n">columns</span> <span class="p">+=</span> <span class="nc">TableColumn</span><span class="p">&lt;</span><span class="nc">TableData</span><span class="p">,</span> <span class="nc">String</span><span class="p">&gt;(</span><span class="s">"Column C"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
          <span class="nf">setCellFactory</span> <span class="p">{</span> <span class="nc">CustomTableCell</span><span class="p">()</span> <span class="p">}</span>
          <span class="nf">setCellValueFactory</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value3</span> <span class="p">}</span>
          <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"bc-column"</span>
      <span class="p">}</span>
   <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Columns “B” and “C” are inside a nested column with the title “B and C”.  The nested column gets the selector <code class="language-plaintext highlighter-rouge">bc-nested-column</code> and the two inner columns get the selector <code class="language-plaintext highlighter-rouge">bc-column</code>.  Since we don’t have sibling combinators to allow us to use <code class="language-plaintext highlighter-rouge">.nested-column-header ~ bc-nested-column</code> as a selector, our options for styling are limited.  However, we can do this:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.root</span> <span class="p">{</span>
  <span class="py">-wfx-header-background</span><span class="p">:</span> <span class="n">linear-gradient</span><span class="p">(</span><span class="n">to</span> <span class="nb">right</span><span class="p">,</span> <span class="m">#1dbbdd</span><span class="p">,</span> <span class="m">#9310b9</span><span class="p">)</span>
<span class="p">}</span>

<span class="nc">.table-view</span> <span class="nc">.column-header-background</span> <span class="p">{</span>
   <span class="py">-fx-background-color</span><span class="p">:</span> <span class="n">-wfx-header-background</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.table-view</span> <span class="nc">.column-header</span> <span class="p">{</span>
  <span class="py">-fx-background-color</span><span class="p">:</span> <span class="nb">transparent</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.filler</span> <span class="p">{</span>
  <span class="py">-fx-background-color</span><span class="p">:</span> <span class="nb">transparent</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.nested-column-header</span> <span class="p">{</span>
   <span class="py">-fx-background-color</span><span class="p">:</span> <span class="n">-wfx-header-background</span><span class="p">;</span>
   <span class="py">-wfx-header-background</span><span class="p">:</span> <span class="nb">transparent</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.bc-nested-column</span> <span class="p">{</span>
  <span class="py">-wfx-header-background</span><span class="p">:</span> <span class="n">linear-gradient</span><span class="p">(</span><span class="n">to</span> <span class="nb">bottom</span><span class="p">,</span> <span class="m">#0dffee</span><span class="p">,</span> <span class="m">#9310b9</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Here, we have a custom colour defined as a blue to purple gradient to the right in the <code class="language-plaintext highlighter-rouge">.root</code> 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 <code class="language-plaintext highlighter-rouge">-fx-background-color</code> for the nested column headers to our custom colour and redefine it to be transparent in this context.  Finally, for our nested “B and C” column, we redefine our custom colour to be the same gradient, but going down instead of to the right.</p>

<p>It looks like this:</p>

<p><img src="/assets/elements/TableStyle7.png" alt="Screen Snap 7" /></p>

<p>The biggest issue here is the filler column at the right.  This is defined <strong>outside</strong> 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 <code class="language-plaintext highlighter-rouge">ColumnResizePolicy</code>, and then adding a fourth column just to have something at the right, then we can simplify the style sheet:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.root</span> <span class="p">{</span>
  <span class="py">-wfx-header-background</span><span class="p">:</span> <span class="n">linear-gradient</span><span class="p">(</span><span class="n">to</span> <span class="nb">right</span><span class="p">,</span> <span class="m">#1dbbdd</span><span class="p">,</span> <span class="m">#9310b9</span><span class="p">)</span>
<span class="p">}</span>

<span class="nc">.table-view</span> <span class="nc">.column-header</span> <span class="p">{</span>
  <span class="py">-fx-background-color</span><span class="p">:</span> <span class="nb">transparent</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.nested-column-header</span> <span class="p">{</span>
   <span class="py">-fx-background-color</span><span class="p">:</span> <span class="n">-wfx-header-background</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.bc-nested-column</span> <span class="p">{</span>
  <span class="py">-wfx-header-background</span><span class="p">:</span> <span class="n">linear-gradient</span><span class="p">(</span><span class="n">to</span> <span class="nb">bottom</span><span class="p">,</span> <span class="m">#0dffee</span><span class="p">,</span> <span class="m">#9310b9</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Which looks like this:</p>

<p><img src="/assets/elements/TableStyle8.png" alt="Screen Snap 8" /></p>

<p>Now we don’t need to worry about the header background or the filler, and we can set the initial gradient on the main nested column header.</p>

<p>Unfortunately, JavaFX only supports name elements for colours, so we cannot use this technique for other attributes, like background radii or insets.</p>

<h2 id="alternate-approaches">Alternate Approaches</h2>

<p>We can use the fact that the main wrapper class for the header row is a <code class="language-plaintext highlighter-rouge">NestedTableColumnHeader</code> and specify further children of that selector:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.nested-column-header</span> <span class="p">{</span>
   <span class="py">-fx-background-color</span><span class="p">:</span> <span class="n">linear-gradient</span><span class="p">(</span><span class="n">to</span> <span class="nb">right</span><span class="p">,</span> <span class="no">red</span><span class="p">,</span> <span class="no">blue</span><span class="p">);</span>
<span class="p">}</span>

<span class="nc">.nested-column-header</span> <span class="o">&gt;</span> <span class="nc">.nested-column-header</span> <span class="p">{</span>
   <span class="py">-fx-background-color</span><span class="p">:</span> <span class="n">linear-gradient</span><span class="p">(</span><span class="n">to</span> <span class="nb">bottom</span><span class="p">,</span> <span class="no">lightgreen</span><span class="p">,</span> <span class="no">pink</span><span class="p">);</span>
   <span class="py">-fx-background-radius</span><span class="p">:</span> <span class="m">10</span> <span class="m">10</span> <span class="m">3</span> <span class="m">3px</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.column-header</span> <span class="p">{</span>
  <span class="py">-fx-background-color</span><span class="p">:</span> <span class="nb">transparent</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Which looks like this:</p>

<p><img src="/assets/elements/TableStyle9.png" alt="Screen Snap 9" /></p>

<p>As 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.</p>

<p>Finally, you can also assign a style class to the <code class="language-plaintext highlighter-rouge">TableColumn</code> that corresponds to the <code class="language-plaintext highlighter-rouge">NestedTableColumnHeader</code> and use the technique outlined in the “Accessing Specific Column Headers” section above.  That technique works just as well for <code class="language-plaintext highlighter-rouge">NestedTableColumnHeaders</code>.  The following style sheet:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.nested-column-header</span> <span class="p">{</span>
   <span class="py">-fx-background-color</span><span class="p">:</span> <span class="n">linear-gradient</span><span class="p">(</span><span class="n">to</span> <span class="nb">right</span><span class="p">,</span> <span class="no">red</span><span class="p">,</span> <span class="no">blue</span><span class="p">);</span>
<span class="p">}</span>

<span class="nc">.bc-nested-column</span> <span class="p">{</span>
   <span class="py">-fx-background-color</span><span class="p">:</span> <span class="n">linear-gradient</span><span class="p">(</span><span class="n">to</span> <span class="nb">bottom</span><span class="p">,</span> <span class="no">lightgreen</span><span class="p">,</span> <span class="no">pink</span><span class="p">);</span>
   <span class="py">-fx-background-radius</span><span class="p">:</span> <span class="m">10</span> <span class="m">10</span> <span class="m">3</span> <span class="m">3px</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.column-header</span> <span class="p">{</span>
  <span class="py">-fx-background-color</span><span class="p">:</span> <span class="nb">transparent</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Also yields:</p>

<p><img src="/assets/elements/TableStyle9.png" alt="Screen Snap 9" /></p>

<h1 id="styled-components-of-tableview">Styled Components of TableView</h1>

<p>This section contains as complete a listing as I could compose of the styleable elements of <code class="language-plaintext highlighter-rouge">TableView</code>.  It’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.</p>

<p>For each entry, I’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’ve also included images that highlight the component in action.</p>

<p>Additionally, I’ve included Pseudo-classes that are supported by each component that <em>aren’t</em> documented somewhere else.</p>

<p>At the top of the hierarchy is <code class="language-plaintext highlighter-rouge">TableView</code> itself, which has a single selector:</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right"><strong>CSS Selector:</strong></th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">table-view</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">StackPane</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is the <code class="language-plaintext highlighter-rouge">TableView</code> itself.  It supports a number of Pseudo-classes that are also used as selectors for child elements.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.table-view</code><br /><code class="language-plaintext highlighter-rouge">.table-view:disabled</code><br /><code class="language-plaintext highlighter-rouge">.table-view:focused</code></td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Pseudo-classes</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">constrained-resize</code><br /><code class="language-plaintext highlighter-rouge">unconstrained-resize</code><br /><code class="language-plaintext highlighter-rouge">cell-selection</code><br /><code class="language-plaintext highlighter-rouge">row-selection</code></td>
    </tr>
  </tbody>
</table>

<p>Note that there are two entries for <code class="language-plaintext highlighter-rouge">.table-view</code> in Modena.  One, in the section called “Box-like Things” sets styling like background colours and insets.  The other, in the section called “TableView” defines some colours.</p>

<h2 id="tableheaderrow">TableHeaderRow</h2>

<p>This is the wrapper class for the entire header row.</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right"><strong>CSS Selector:</strong></th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">column-header-background</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">StackPane</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is the area at the top of the <code class="language-plaintext highlighter-rouge">TableView</code> that holds the column headings and the related controls.  Don’t let “background” confuse you, this is the entire header row.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.table-view .column-header-background</code></td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/elements/TableStyle1.png" alt="Screen Snap 1" /></p>

<h3 id="root-nestedtablecolumnheader">Root NestedTableColumnHeader</h3>

<p>Every <code class="language-plaintext highlighter-rouge">TableColumnHeader</code> can act as a <code class="language-plaintext highlighter-rouge">NestedTableColumnHeader</code> by adding <code class="language-plaintext highlighter-rouge">TableColumns</code> to its associated <code class="language-plaintext highlighter-rouge">TableColumn</code>.  Technically, every <code class="language-plaintext highlighter-rouge">TableColumnHeader</code> is the child of a <code class="language-plaintext highlighter-rouge">NestedTableColumnHeader</code> because the <code class="language-plaintext highlighter-rouge">TableHeaderRow</code> contains a single, main, <code class="language-plaintext highlighter-rouge">NestedTableColumnHeader</code> to which all of the other <code class="language-plaintext highlighter-rouge">TableColumnHeaders</code> (or <code class="language-plaintext highlighter-rouge">NestedTableColumnHeaders</code>) are added.</p>

<p>It’s impossible to add style class selectors to the top-level <code class="language-plaintext highlighter-rouge">NestedTableColumnHeader</code> because it’s not exposed programmatically, and all <code class="language-plaintext highlighter-rouge">NestedTableColumnHeaders</code> have the some selector.  You can access this specific, top-level, <code class="language-plaintext highlighter-rouge">NestedTableColumnHeader</code> via: <code class="language-plaintext highlighter-rouge">.table-view .column-header-background &gt; .nested-column-header</code>.</p>

<p>Other <code class="language-plaintext highlighter-rouge">NestedTableColumnHeaders</code> can be given custom style class selectors by adding one to the corresponding <code class="language-plaintext highlighter-rouge">TableColumn</code>.</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right"><strong>CSS Selector:</strong></th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">nested-column-header</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">StackPane</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is the master <code class="language-plaintext highlighter-rouge">NestedTableColumnHeader</code> that serves as the container for all of the other <code class="language-plaintext highlighter-rouge">TableColumnHeaders</code> and <code class="language-plaintext highlighter-rouge">NestedTableColumnHeaders</code> that are defined in the <code class="language-plaintext highlighter-rouge">TableView</code>.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left">None</td>
    </tr>
  </tbody>
</table>

<p>You can access the other <code class="language-plaintext highlighter-rouge">NestedTableColumnHeaders</code> via: <code class="language-plaintext highlighter-rouge">.table-view .column-header-background &gt; .nested-column-header .nested-column-header</code>.</p>

<h4 id="tablecolumnheader">TableColumnHeader</h4>

<p>For each <code class="language-plaintext highlighter-rouge">NestedTableColumnHeader</code> in the <code class="language-plaintext highlighter-rouge">TableView</code> there will be some number of <code class="language-plaintext highlighter-rouge">TableColumnHeaders</code> that have the following CSS structure:</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right"><strong>CSS Selector:</strong></th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">column-header</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">TableColumnHeader (extends Region)</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">The header for a TableColumn.  This inherits any style classes added to its associated <code class="language-plaintext highlighter-rouge">TableColumn</code></td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.table-view .column-header</code></td>
    </tr>
  </tbody>
</table>

<p>This also supports the <code class="language-plaintext highlighter-rouge">last-visible</code> Pseudo-class.</p>

<p>Additionally, the column header inherits any style classes applied to its associated <code class="language-plaintext highlighter-rouge">TableColumn</code>.  Initially, this is just <code class="language-plaintext highlighter-rouge">table-column</code>:</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right"><strong>CSS Selector:</strong></th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">table-column</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">TableColumnHeader (extends Region)</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is the standard inherited styleclass from the <code class="language-plaintext highlighter-rouge">TableColumn</code>.  This is also shared with all of the <code class="language-plaintext highlighter-rouge">TableCells</code> in the column.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left">None</td>
    </tr>
  </tbody>
</table>

<p>This also supports the <code class="language-plaintext highlighter-rouge">last-visible</code> Pseudo-class.</p>

<h5 id="column-label">Column Label</h5>

<p><code class="language-plaintext highlighter-rouge">TableColumnHeader</code> holds a <code class="language-plaintext highlighter-rouge">Label</code> to hold the column title:</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">label</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">Label</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is just a standard <code class="language-plaintext highlighter-rouge">Label</code></td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.table-view .column-header .label</code></td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">text</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">LabeledText</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is the <code class="language-plaintext highlighter-rouge">Text</code> element inside the <code class="language-plaintext highlighter-rouge">Label</code> in the column header.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left">None</td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/elements/TableStyle4.png" alt="Screen Snap 4" /></p>

<h5 id="column-context-menu">Column Context Menu</h5>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">context-menu</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">ContextMenu</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">If a <code class="language-plaintext highlighter-rouge">ContextMenu</code> has been established for the related <code class="language-plaintext highlighter-rouge">TableColumn</code> then a right click in the <code class="language-plaintext highlighter-rouge">TableColumnHeader</code> will invoke the context menu.  This is the default selector for this.  However, since the <code class="language-plaintext highlighter-rouge">ContextMenu</code> 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 <code class="language-plaintext highlighter-rouge">ContextMenu</code>.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left">None</td>
    </tr>
  </tbody>
</table>

<h5 id="column-sort-indicators">Column Sort Indicators</h5>

<p><code class="language-plaintext highlighter-rouge">TableColumns</code> can be sorted by clicking on the column headers.  Secondary and tertiary sort columns can be specified.  The <code class="language-plaintext highlighter-rouge">TableColumns</code> contain a <code class="language-plaintext highlighter-rouge">GridPane</code> holding an arrow, sort number dots and pontentially a <code class="language-plaintext highlighter-rouge">Label</code> 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.</p>

<p><img src="/assets/elements/TableStyle5.png" alt="Screen Snap 5" /></p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">arrow</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">Region</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">In Modena, this is an SVG shape of a triangle pointed down.  If the column sort is ascending, then it is rotated 180 degrees.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">table-view .arrow</code></td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">sort-order</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">Label</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">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.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.table-view .column-header .sort-order</code></td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/elements/TableStyle10.png" alt="Screen Snap 10" /></p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">sort-order-dots-container</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">HBox</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is just an <code class="language-plaintext highlighter-rouge">HBox</code> that holds the sort order dots.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.table-view .column-header .sort-order-dots-container</code></td>
    </tr>
  </tbody>
</table>

<p>The next two selectors are applied to the actual “dots”:</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">sort-order-dot</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">Region</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is just a small <code class="language-plaintext highlighter-rouge">Region</code> with a background colour and curved corners that looks like a “dot”.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.table-view .column-header .sort-order-dot</code></td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">ascending</code> or <code class="language-plaintext highlighter-rouge">descending</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">Region</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">You can style the dots differently for ascending vs decsending sort orders.  These selectors are not used in Modena itself.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left">None</td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/elements/TableStyle6.png" alt="Screen Snap 6" /></p>

<h3 id="column-drag">Column Drag</h3>

<p>In case you didn’t know, you can drag the column headings around to change their order in the <code class="language-plaintext highlighter-rouge">TableView</code>.  These elements style the drag action:</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">column-drag-header</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">StackPane</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">When a column is being ‘dragged’ 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.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.table-view .column-drag-header</code></td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/elements/TableStyle13.png" alt="Screen Snap 13" /></p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">column-overlay</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">StackPane</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This semi-transparent overlay to indicate the column that is currently being moved.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.table-view .column-overlay</code></td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/elements/TableStyle14.png" alt="Screen Snap 14" /></p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">column-resize-line</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">Region</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">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.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.table-view .column-resize-line</code></td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/elements/TableStyle17.png" alt="Screen Snap 17" /></p>

<h3 id="filler-column">Filler Column</h3>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">filler</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">Region</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">If you haven’t set the <code class="language-plaintext highlighter-rouge">TableView.columnResizePolicyProperty()</code> to one of the “constrained” 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.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.table-view .filler</code></td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/elements/TableStyle2.png" alt="Screen Snap 3" /></p>

<h3 id="showhide-column-button">Show/Hide Column Button</h3>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">show-hide-columns-button</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">Pane</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">The space at the top right corner, above the vertical scrollbar is a “Button” (but not a <code class="language-plaintext highlighter-rouge">Button</code>) 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 “Button”.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.table-view &gt; .column-header-background &gt; .show-hide-columns-button</code></td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/elements/TableStyle3.png" alt="Screen Snap 4" /></p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">show-hide-column-image</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">StackPane</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is the “+” that appears in the column show/hide <code class="language-plaintext highlighter-rouge">Button</code>.  It is a <code class="language-plaintext highlighter-rouge">StackPane</code> with an SVG shape applied.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.table-view .show-hide-column-image</code></td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">context-menu</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">ContextMenu</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">When you click on the “button” 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 <code class="language-plaintext highlighter-rouge">ContextMenu</code>.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.table-view &gt; .column-header-background &gt; .show-hide-columns-button .context-menu</code></td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/elements/TableStyle15.png" alt="Screen Snap 15" /></p>

<h2 id="virtualflow">VirtualFlow</h2>

<p><code class="language-plaintext highlighter-rouge">TableView</code> is one of the controls that uses <code class="language-plaintext highlighter-rouge">VirtualFlow</code> which enables it to have a descrete set of screen <code class="language-plaintext highlighter-rouge">Nodes</code> to support a limitless number of elements in a <code class="language-plaintext highlighter-rouge">List</code>.  This set of <code class="language-plaintext highlighter-rouge">Nodes</code> is recycled as they scroll on/off the viewport of the <code class="language-plaintext highlighter-rouge">TableView</code>.</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">virtual-flow</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">VirtualFlow (extends Region)</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">VirtualFlow</code> is the container for all of the <code class="language-plaintext highlighter-rouge">TableView</code> elements except those in the <code class="language-plaintext highlighter-rouge">TableHeaderRow</code>.  This includes the rows and columns and the scrollbars.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left">No direct uses, but it is specified as the parent of other elements.</td>
    </tr>
  </tbody>
</table>

<h3 id="clippedcontainer">ClippedContainer</h3>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">clipped-container</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">VirtualFlow.ClippedContainer (extends Region)</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is simple just a <code class="language-plaintext highlighter-rouge">Region</code> with a clipping rectangle specified.  It acts as a wrapper around <code class="language-plaintext highlighter-rouge">sheet</code> to limit the visibility of the contents of the <code class="language-plaintext highlighter-rouge">TableView</code> so that it will fit within the space allocated.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left">No direct uses, but it is specified as the parent of other elements.</td>
    </tr>
  </tbody>
</table>

<h3 id="clippedcontainer---sheet">ClippedContainer -&gt; Sheet</h3>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">sheet</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">Group</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is the element that contains all of the individual <code class="language-plaintext highlighter-rouge">TableCells</code> and <code class="language-plaintext highlighter-rouge">TableRows</code>.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left">No direct uses, but it is specified as the parent of other elements.</td>
    </tr>
  </tbody>
</table>

<h4 id="tablerow">TableRow</h4>

<p><img src="/assets/elements/TableStyle29.png" alt="Table Row" /></p>

<p><code class="language-plaintext highlighter-rouge">TableRow</code> is itself a <code class="language-plaintext highlighter-rouge">Cell</code> and it has three selectors.  Since you can specify a <code class="language-plaintext highlighter-rouge">RowFactory</code> for a <code class="language-plaintext highlighter-rouge">TableView</code> it <em>is</em> possible to apply your own selectors to a customized <code class="language-plaintext highlighter-rouge">TableRow</code>.  However, if you just want to change the styling of the <code class="language-plaintext highlighter-rouge">TableRow</code> without creating a <code class="language-plaintext highlighter-rouge">RowFactory</code>, you can use these selectors.</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">cell</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">TableRow</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This selector is applied to all of the <code class="language-plaintext highlighter-rouge">Cell</code> subclass elements in the <code class="language-plaintext highlighter-rouge">TableView</code> including the individual <code class="language-plaintext highlighter-rouge">TableCells</code>, so any styling here will also apply to all of the <code class="language-plaintext highlighter-rouge">TableCells</code>.  This is inherited from the standard implementation of <code class="language-plaintext highlighter-rouge">Cell</code></td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left">No direct uses, but it is specified as the parent of other elements.</td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">indexed-cell</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">TableRow</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This selector is applied to all of the <code class="language-plaintext highlighter-rouge">Cell</code> subclass elements in the <code class="language-plaintext highlighter-rouge">TableView</code> including the individual <code class="language-plaintext highlighter-rouge">TableCells</code>, so any styling here will also apply to all of the <code class="language-plaintext highlighter-rouge">TableCells</code>.  This is inherited from the standard implementation of <code class="language-plaintext highlighter-rouge">IndexedCell</code>.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left">None</td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">table-row-cell</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">TableRow</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is the only selector which is specific to <code class="language-plaintext highlighter-rouge">TableRow</code>.  Use this for styling that you don’t want applied to individual <code class="language-plaintext highlighter-rouge">TableCells</code>.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.table-row-cell</code><br /><code class="language-plaintext highlighter-rouge">.table-row-cell:odd</code><br /><code class="language-plaintext highlighter-rouge">.table-view:focused &gt; .virtual-flow &gt; .clipped-container &gt; .sheet &gt; .table-row-cell:filled:selected</code><br /><code class="language-plaintext highlighter-rouge">.table-row-cell:filled:selected</code><br /><code class="language-plaintext highlighter-rouge">.table-view:focused:row-selection &gt; .virtual-flow &gt; .clipped-container &gt; .sheet &gt; .table-row-cell:focused</code></td>
    </tr>
  </tbody>
</table>

<h4 id="tablecolumn">TableColumn</h4>

<p>While you do create <code class="language-plaintext highlighter-rouge">TableColumns</code> and add them to the <code class="language-plaintext highlighter-rouge">TableView</code>, <code class="language-plaintext highlighter-rouge">TableColumn</code> doesn’t appear as an actual element in the skin of <code class="language-plaintext highlighter-rouge">TableView</code>.  Instead it is used to create <code class="language-plaintext highlighter-rouge">TableCells</code> and the <code class="language-plaintext highlighter-rouge">TableColumnHeaders</code>, both of which will inherit any style sheet selectors applied to the related <code class="language-plaintext highlighter-rouge">TableView</code>.  As a default, all <code class="language-plaintext highlighter-rouge">TableColumns</code> have the following selector applied:</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">table-column</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">Various</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">The default style sheet selector added to all elements that derive from <code class="language-plaintext highlighter-rouge">TableColumn</code>.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left">None</td>
    </tr>
  </tbody>
</table>

<h5 id="tablecell">TableCell</h5>

<p><img src="/assets/elements/TableStyle30.png" alt="Table Cell" /></p>

<p>Since you can specify a <code class="language-plaintext highlighter-rouge">CellFactory</code> for a <code class="language-plaintext highlighter-rouge">TableColumn</code> it <em>is</em> possible to add a custom style class selector to your custom <code class="language-plaintext highlighter-rouge">TableCells</code>.  However, if you wish to style all of the <code class="language-plaintext highlighter-rouge">TableCells</code> in a <code class="language-plaintext highlighter-rouge">TableView</code>, then you an use one of the selectors listed here.</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">cell</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">TableCell</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This selector is applied to all of the <code class="language-plaintext highlighter-rouge">Cell</code> subclass elements in the <code class="language-plaintext highlighter-rouge">TableView</code> including the individual <code class="language-plaintext highlighter-rouge">TableRows</code>, so any styling here will also apply to all of the <code class="language-plaintext highlighter-rouge">TableRows</code>.  This is inherited from the standard implementation of <code class="language-plaintext highlighter-rouge">Cell</code>.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left">No direct uses, but it is specified as the parent of other elements.</td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">indexed-cell</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">TableCell</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This selector is applied to all of the <code class="language-plaintext highlighter-rouge">IndexedCell</code> subclass elements in the <code class="language-plaintext highlighter-rouge">TableView</code> including the individual <code class="language-plaintext highlighter-rouge">TableRows</code>, so any styling here will also apply to all of the <code class="language-plaintext highlighter-rouge">TableRows</code>.  This is inherited from the standard implementation of <code class="language-plaintext highlighter-rouge">IndexedCell</code>.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left">None</td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">table-cell</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">TableCell</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is the sole standard style class selector related only to <code class="language-plaintext highlighter-rouge">TableCell</code> and comes from the standard implementation of <code class="language-plaintext highlighter-rouge">TableCell</code>.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.table-cell</code><br /><code class="language-plaintext highlighter-rouge">.table-cell:selected:disabled</code><br /><code class="language-plaintext highlighter-rouge">.table-row-cell:filled &gt; .table-cell:selected</code><br /><code class="language-plaintext highlighter-rouge">.table-view:focused:cell-selection &gt; .virtual-flow &gt; .clipped-container &gt; .sheet &gt; .table-row-cell &gt; .table-cell:focused</code><br /><code class="language-plaintext highlighter-rouge">.table-view &gt; .virtual-flow &gt; .clipped-container &gt; .sheet &gt; .table-row-cell .table-cell:selected</code><br /><code class="language-plaintext highlighter-rouge">.table-view:constrained-resize &gt; .virtual-flow &gt; .clipped-container &gt; .sheet &gt; .table-row-cell &gt; .table-cell:last-visible</code></td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">table-column</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">TableCell</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is the standard inherited styleclass from the <code class="language-plaintext highlighter-rouge">TableColumn</code>.  This is also shared with the <code class="language-plaintext highlighter-rouge">TableColumnHeader</code> for the column.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left">None</td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">text</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">LabeledText</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">Since <code class="language-plaintext highlighter-rouge">TableCell</code> inherits from <code class="language-plaintext highlighter-rouge">Labeled</code>, it also inherits this standard style class selector from <code class="language-plaintext highlighter-rouge">Labeled</code> that is associated with the <code class="language-plaintext highlighter-rouge">LabeledText</code> element in <code class="language-plaintext highlighter-rouge">Labeled</code>.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.text</code></td>
    </tr>
  </tbody>
</table>

<h3 id="scrollbars">ScrollBars</h3>
<p>The scrollbars in <code class="language-plaintext highlighter-rouge">TableView</code> are an instance of <code class="language-plaintext highlighter-rouge">VirtualScrollBar</code> which extends <code class="language-plaintext highlighter-rouge">ScrollBar</code> and is intended to work with <code class="language-plaintext highlighter-rouge">VirtualFlow</code>.  <code class="language-plaintext highlighter-rouge">ScrollBar</code> itself is skinned, also.  All of the selectors come from the <code class="language-plaintext highlighter-rouge">ScrollBar</code> class itself, and there’s nothing added from the <code class="language-plaintext highlighter-rouge">TableView</code>.</p>

<p>The standard <code class="language-plaintext highlighter-rouge">ScrollBar</code> has and arrow “Button” at each end of a track that has an indicator called a “thumb” that runs up and down it.</p>

<p>The orientation of a <code class="language-plaintext highlighter-rouge">ScrollBar</code> is indicated by a Pseudo-classes applied to the <code class="language-plaintext highlighter-rouge">ScrollBar</code> itself and will be either <code class="language-plaintext highlighter-rouge">horizontal</code> or <code class="language-plaintext highlighter-rouge">vertical</code>.</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">scroll-bar</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">VirtualScrollBar</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is the selector for the entire ScrollBar. The <code class="language-plaintext highlighter-rouge">horizontal</code> and <code class="language-plaintext highlighter-rouge">vertical</code> Pseudo-classes are applied to this selector.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.table-view &gt; .virtual-flow &gt; .scroll-bar:vertical</code><br /><code class="language-plaintext highlighter-rouge">.table-view &gt; .virtual-flow &gt; .scroll-bar:horizontal</code></td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/elements/TableStyle31.png" alt="ScrollBar" /></p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">track-background</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">StackPane</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">The track background covers the entire space of the <code class="language-plaintext highlighter-rouge">ScrollBar</code> minus any padding set on the <code class="language-plaintext highlighter-rouge">ScrollBar</code> itself.  All of the other components sit on top of the track background.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left">None</td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">track</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">StackPane</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">The track is the space between the end buttons that the thumb runs up and down.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left">None</td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/elements/TableStyle32.png" alt="ScrollBar Track" /></p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">increment-button</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">Region</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is one of the two end “Buttons” in the <code class="language-plaintext highlighter-rouge">ScrollBar</code>, and is at the bottom of a vertical <code class="language-plaintext highlighter-rouge">ScrollBar</code> and the right of a horizontal <code class="language-plaintext highlighter-rouge">ScrollBar</code>.  Not this this is not an actual <code class="language-plaintext highlighter-rouge">Button</code> but just a <code class="language-plaintext highlighter-rouge">Region</code>.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.scroll-bar &gt; .increment-button</code><br /><code class="language-plaintext highlighter-rouge">.scroll-bar:horizontal &gt; .increment-button</code><br /><code class="language-plaintext highlighter-rouge">.scroll-bar:vertical &gt; .increment-button</code></td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/elements/TableStyle33.png" alt="ScrollBar Increment Button" /></p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">increment-arrow</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">Region</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is the actual “arrow” that is drawn in the increment “Button”.  In Modena, it is styled by appling an SVG shape to the <code class="language-plaintext highlighter-rouge">Region</code>.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.scroll-bar &gt; .increment-button &gt; .increment-arrow</code><br /><code class="language-plaintext highlighter-rouge">.scroll-bar &gt; .increment-button:hover &gt; .increment-arrow</code><br /><code class="language-plaintext highlighter-rouge">.scroll-bar &gt; .increment-button:pressed &gt; .increment-arrow</code><br /><code class="language-plaintext highlighter-rouge">.scroll-bar:horizontal &gt; .increment-button &gt; .increment-arrow </code><br /><code class="language-plaintext highlighter-rouge">.scroll-bar:vertical &gt; .increment-button &gt; .increment-arrow </code></td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">decrement-button</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">Region</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is one of the two end “Buttons” in the <code class="language-plaintext highlighter-rouge">ScrollBar</code>, and is at the top of a vertical <code class="language-plaintext highlighter-rouge">ScrollBar</code> and the left of a horizontal <code class="language-plaintext highlighter-rouge">ScrollBar</code>.  Not this this is not an actual <code class="language-plaintext highlighter-rouge">Button</code> but just a <code class="language-plaintext highlighter-rouge">Region</code>.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.scroll-bar &gt; .decrement-button</code><br /><code class="language-plaintext highlighter-rouge">.scroll-bar:horizontal &gt; .decrement-button</code><br /><code class="language-plaintext highlighter-rouge">.scroll-bar:vertical &gt; .decrement-button</code></td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/elements/TableStyle34.png" alt="ScrollBar Decrement Button" /></p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">decrement-arrow</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">Region</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is the actual “arrow” that is drawn in the decrement “Button”.  In Modena, it is styled by appling an SVG shape to the <code class="language-plaintext highlighter-rouge">Region</code>.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.scroll-bar &gt; .decrement-button &gt; .decrement-arrow </code><br /><code class="language-plaintext highlighter-rouge">.scroll-bar &gt; .decrement-button:hover &gt; .decrement-arrow</code><br /><code class="language-plaintext highlighter-rouge">.scroll-bar &gt; .decrement-button:pressed &gt; .decrement-arrow </code><br /><code class="language-plaintext highlighter-rouge">.scroll-bar:horizontal &gt; .decrement-button &gt; .decrement-arrow</code><br /><code class="language-plaintext highlighter-rouge">.scroll-bar:vertical &gt; .decrement-button &gt; .decrement-arrow</code></td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">thumb</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">StackPane</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is the indicator/control that sits in the track and slides along the track as the <code class="language-plaintext highlighter-rouge">ScrollBar</code> scrolls.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.scroll-bar &gt; .thumb </code><br /><code class="language-plaintext highlighter-rouge">.scroll-bar:vertical &gt; .thumb</code><br /><code class="language-plaintext highlighter-rouge">.scroll-bar &gt; .thumb:hover</code><br /><code class="language-plaintext highlighter-rouge">.scroll-bar &gt; .thumb:pressed</code></td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/elements/TableStyle35.png" alt="ScrollBar Thumb" /></p>

<h3 id="corner">Corner</h3>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">corner</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">StackPane</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is the little square that appears at the bottom right corner where the two scrollbars meet, if they are both visible.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.table-view &gt; .virtual-flow &gt; .corner</code></td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/elements/TableStyle16.png" alt="Screen Snap 16" /></p>

<h3 id="placeholder">PlaceHolder</h3>

<p>There is a <code class="language-plaintext highlighter-rouge">Node</code> that will be displayed if the <code class="language-plaintext highlighter-rouge">TableView</code> is empty which is called “placeholder”.  <code class="language-plaintext highlighter-rouge">TableView</code> has a <code class="language-plaintext highlighter-rouge">Property</code> called <code class="language-plaintext highlighter-rouge">placeholder</code> that you can use to set your own, custom, <code class="language-plaintext highlighter-rouge">Node</code> and set your own style class selectors.  In the event that you use the default placeholder, which is a <code class="language-plaintext highlighter-rouge">StackPane</code> holding a <code class="language-plaintext highlighter-rouge">Label</code>, these are the selectors:</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">placeholder</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">StackPane</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">When the <code class="language-plaintext highlighter-rouge">TableView</code> is empty, or if it contains no column, then a “placeholder” <code class="language-plaintext highlighter-rouge">StackPane</code> containing a <code class="language-plaintext highlighter-rouge">Label</code> will be shown instead of the usual rows and columns.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left">None</td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/elements/TableStyle36.png" alt="PlaceHolder" /></p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">empty-table</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">Unknown</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">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.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left"><code class="language-plaintext highlighter-rouge">.table-view .empty-table</code></td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th style="text-align: right">CSS Selector:</th>
      <th style="text-align: left"><code class="language-plaintext highlighter-rouge">label</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>Type:</strong></td>
      <td style="text-align: left">Label</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Description:</strong></td>
      <td style="text-align: left">This is the selector inherited from the standard implementation of <code class="language-plaintext highlighter-rouge">Label</code>.</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Modena:</strong></td>
      <td style="text-align: left">None</td>
    </tr>
  </tbody>
</table>

<h1 id="conclusion">Conclusion</h1>

<p>If you are interested in doing this kind of investigation into other JavaFX controls, these are the techniques that I used:</p>

<dl>
  <dt>Node.lookupAll(“*”)</dt>
  <dd>
    <p>This command will give you a list of all of the elements with style class selectors in the <code class="language-plaintext highlighter-rouge">Node</code> to which it is applied.</p>
  </dd>
  <dd>
    <p>However, it will not show items that are not on the SceneGraph, and will not show parent -&gt; 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.</p>
  </dd>
  <dt>Searching the Source Code</dt>
  <dd>
    <p>At some point you’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’t see with <code class="language-plaintext highlighter-rouge">Node.lookupAll("*")</code> - and there <em>will</em> be some of those.</p>
  </dd>
  <dd>
    <p>A good starting point is to search through the source code for “getStyleClass”, or just “StyleClass” because that is the only way to add style class selectors.</p>
  </dd>
  <dd>
    <p>Remember that some selectors are created at the “control” level, and more might be added in the skin.  Many components of <code class="language-plaintext highlighter-rouge">TableView</code> are themselves skinned.</p>
  </dd>
  <dd>
    <p>Finally, you need to find the components, and the components of the components.  Look for <code class="language-plaintext highlighter-rouge">Node</code> subclasses that are declared as fields.  Search for “getChildren” and “layoutChildren” in the source code.</p>
  </dd>
  <dt>Searching Through Modena</dt>
  <dd>
    <p>After all of that, you still might/probably have missed something.  Search through Modena for the style class selectors that you <em>have</em> 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.</p>
  </dd>
</dl>

<p>I 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.</p>

<p>For instance the <code class="language-plaintext highlighter-rouge">TableView</code> Pseudo-class <code class="language-plaintext highlighter-rouge">unconstrained-resize</code> is defined in <code class="language-plaintext highlighter-rouge">TableView</code> but isn’t used anywhere in Modena and won’t appear in <code class="language-plaintext highlighter-rouge">Node.lookupAll("*")</code> since it is a Pseudo-class.  The only way to find it is to infer its existence from Modena’s use of <code class="language-plaintext highlighter-rouge">constrained-resize</code> and then go looking for it in the source code.   Even then it’s not directly used, but comes from the <code class="language-plaintext highlighter-rouge">toString()</code> of the <code class="language-plaintext highlighter-rouge">UNCONSTRAINED_RESIZE_POLICY Callback</code>.  And if you are looking to see if <code class="language-plaintext highlighter-rouge">constrained-resize</code> is a special case of one of the other policies (it isn’t) you need to drill down into <code class="language-plaintext highlighter-rouge">ConstrainedColumnResizeBase</code> to find its <code class="language-plaintext highlighter-rouge">toString()</code> method.</p>

<p>What can you use these Pseudo-class for?  In Modena, <code class="language-plaintext highlighter-rouge">constrained-resize</code> is used turn the right-hand side border of the last visible column’s <code class="language-plaintext highlighter-rouge">TableCells</code> 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.</p>

<p>Regardless of that, hopefully you will find this tutorial and reference helpful in styling your own <code class="language-plaintext highlighter-rouge">TableViews</code>.</p>]]></content><author><name>Dave Barrett</name><email>pragmaticcoding8@gmail.com</email></author><category term="javafx" /><summary type="html"><![CDATA[A comprehensive guide to styling TableView with tutorials and a reference of all of the styling selectors available.]]></summary></entry><entry><title type="html">Dealing With Modena</title><link href="https://www.pragmaticcoding.ca/javafx/elements/modena" rel="alternate" type="text/html" title="Dealing With Modena" /><published>2024-11-20T05:00:00+00:00</published><updated>2024-11-20T05:00:00+00:00</updated><id>https://www.pragmaticcoding.ca/javafx/elements/modena</id><content type="html" xml:base="https://www.pragmaticcoding.ca/javafx/elements/modena"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>If you use JavaFX, then you are almost certainly using the Modena stylesheet - or a custom stylesheet designed to work with the standard JavaFX <code class="language-plaintext highlighter-rouge">Node</code> subclasses in much the same way that Modena does.</p>

<p>Unfortunately, Modena is big.  Like… 3,440 lines big.  And it can seem very complicated because it uses a lot of advanced and subtle techniques that aren’t obvious if you’re not a CSS guru.</p>

<p>This article is about understanding Modena, and how it interacts with the standard JavaFX <code class="language-plaintext highlighter-rouge">Nodes</code>.  The intention is that once you’ve read this article, you should be able to look at the styling of just about any of those <code class="language-plaintext highlighter-rouge">Nodes</code> on the screen, and then find the right places in Modena that make it look way and understand how it does it.</p>

<p>You aren’t going to see any specific methods for handling any specific <code class="language-plaintext highlighter-rouge">Node</code> classes here.  This article is all about giving you the tools to be able to figure that for yourself, which is far more valuable.</p>

<h1 id="the-components-of-javafx-styling">The Components of JavaFX Styling</h1>

<p>In JavaFX there are four basic elements that are used to create styling for a <code class="language-plaintext highlighter-rouge">Node</code> subclass.</p>

<ol>
  <li>StyleableProperties<br />These are the base units of styling in JavaFX.  Every <code class="language-plaintext highlighter-rouge">StyleableProperty</code> has a connection back to a stylesheet attribute and provides the mechanism to turn that stylesheet attribute setting into something visual in the GUI.</li>
  <li>CSS Selectors<br />Stylesheets are broken down into sections that are identified via a name called a “Selector”.  JavaFX <code class="language-plaintext highlighter-rouge">Nodes</code> contain a list of CSS selectors that will be applied, in order, to provide values for the various <code class="language-plaintext highlighter-rouge">StyleableProperties</code> defined for the <code class="language-plaintext highlighter-rouge">Node</code>.  When <code class="language-plaintext highlighter-rouge">Node</code> subclasses inherit from their parent <code class="language-plaintext highlighter-rouge">Nodes</code> they can either start a brand new list of selectors, or add new values to the inherited list.</li>
  <li>Pseudoclasses<br /><code class="language-plaintext highlighter-rouge">PseudoClasses</code> are used to provide selectors that are applied to a <code class="language-plaintext highlighter-rouge">Node</code> on a temporary basis.  Each <code class="language-plaintext highlighter-rouge">PseudoClass</code> is a <code class="language-plaintext highlighter-rouge">Boolean</code> value that is associated with a CSS selector.  When the <code class="language-plaintext highlighter-rouge">PseudoClass</code> value is <code class="language-plaintext highlighter-rouge">true</code>, then the selector is applied to the <code class="language-plaintext highlighter-rouge">Node</code> and then removed when it changes to <code class="language-plaintext highlighter-rouge">false</code>.</li>
  <li>StyleSheets<br />Stylesheets contain all of the specifications of how the <code class="language-plaintext highlighter-rouge">StyleableProperties</code> will be applied to various <code class="language-plaintext highlighter-rouge">Nodes</code> in the GUI.  Multiple stylesheets can be added at the <code class="language-plaintext highlighter-rouge">Stage</code>, <code class="language-plaintext highlighter-rouge">Scene</code> or root <code class="language-plaintext highlighter-rouge">Node</code> level of a window and they will be applied in the order in which they are listed.</li>
</ol>

<p>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 <a href="/assets/elements/modena.css">Modena</a> stylesheet, which is the default stylesheet that has shipped with JavaFX for quite a few years.</p>

<h2 id="modena-is-integrated-with-the-javafx-library">Modena is Integrated with the JavaFX Library</h2>

<p>While it is easy enough to say, “We’re going to look at the Modena stylesheet”, in truth you cannot study it in isolation.  Take a look at this snippet:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.web-view</span> <span class="nc">.form-select-button</span> <span class="p">{</span>
    <span class="py">-fx-background-radius</span><span class="p">:</span> <span class="m">2</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="m">0</span><span class="p">;</span>
    <span class="py">-fx-background-insets</span><span class="p">:</span> <span class="m">2</span> <span class="m">2</span> <span class="m">1</span> <span class="m">2</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">3</span><span class="p">,</span> <span class="m">4</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>To understand what this does you need to understand what the selector “.web-view .form-select-button” means.  This is defined somewhere in the JavaFX source code, probably in the skin for the <code class="language-plaintext highlighter-rouge">WebView</code> control.  In the same way, the meanings of “-fx-background-radius” and “-fx-background-insets” are defined as <code class="language-plaintext highlighter-rouge">StyleableProperties</code> somewhere in the class hierarchy leading to <code class="language-plaintext highlighter-rouge">WebView</code>, probably in <code class="language-plaintext highlighter-rouge">Region</code>.</p>

<p>It 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 <code class="language-plaintext highlighter-rouge">Node</code> classes work and are interelated.  One of the biggest challenges is when a <code class="language-plaintext highlighter-rouge">Control</code> is actually composed of a number of different <code class="language-plaintext highlighter-rouge">Controls</code> - and this happens a <strong>lot</strong> in JavaFX.  Just look at the example above, we can infer that <code class="language-plaintext highlighter-rouge">WebView</code> is composed of a number of different elements, one of which is a <code class="language-plaintext highlighter-rouge">Button</code> that is related to some function identified as “form select”.</p>

<p>Applying your own custom styling to a <code class="language-plaintext highlighter-rouge">Node</code> is generally going to involve adding a CSS selector in your own stylesheet for that <code class="language-plaintext highlighter-rouge">Node</code>, and then defining values for the various attributes that apply to that <code class="language-plaintext highlighter-rouge">Node</code> class, or its superclasses.</p>

<h2 id="getting-a-copy-of-modena">Getting a Copy of Modena</h2>

<p>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’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.</p>

<p>Once you’ve compiled a JavaFX project at least once with Gradle or Maven, you should have a section in your project called “External Libraries”.  Somewhere in that section you’ll have a bunch of OpenJFX items.  You’re looking for the one that is called “org.openjfx:javafx-controls:{O/S}:{version}”.  For me this is “org.openjfx:javafx-controls:linux:23”.</p>

<p>Inside that library, you are looking for the following path: <code class="language-plaintext highlighter-rouge">com.sun.javafx.scene.control.skin.modena.modena.css</code>.  And that’s the file.  You can just open it in your IDE, or you can copy it somewhere that makes it easier for you.</p>

<p>You can get a copy directly from the GitHub project for <a href="https://github.com/openjdk/jfx23u/blob/master/modules/javafx.controls/src/main/resources/com/sun/javafx/scene/control/skin/modena/modena.css">OpenJFX</a> is the link for the JFX 23 version.</p>

<h2 id="the-reference-guide">The Reference Guide</h2>

<p>The last thing that you’ll need is the CSS Reference Guide for JavaFX.  This is available as a web page.  For JFX 23, it can be found here:</p>

<p><a href="https://openjfx.io/javadoc/23/javafx.graphics/javafx/scene/doc-files/cssref.html">https://openjfx.io/javadoc/23/javafx.graphics/javafx/scene/doc-files/cssref.html</a></p>

<p>You should bookmark this, you’ll need it a lot.</p>

<h1 id="the-colours">The Colours</h1>

<p>A large part of understanding Modena boils down to understanding how it deals with colours.  Modena does a <strong>lot</strong> of hocus-pocus with colours and huge amounts of the definitions are about defining and applying various colours.</p>

<h2 id="base-colours">Base Colours</h2>

<p>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’s take a look at them, these are all defined in the <code class="language-plaintext highlighter-rouge">.root</code> section:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">-fx-base</span><span class="o">:</span> <span class="err">#</span><span class="o">;</span>

<span class="c">/* 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.
 */</span>
<span class="nt">-fx-background</span><span class="o">:</span> <span class="nt">derive</span><span class="o">(</span><span class="nt">-fx-base</span><span class="o">,</span><span class="err">26</span><span class="o">.</span><span class="err">4</span><span class="o">%);</span>

<span class="c">/* 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.
 */</span>
<span class="nt">-fx-control-inner-background</span><span class="o">:</span> <span class="nt">derive</span><span class="o">(</span><span class="nt">-fx-base</span><span class="o">,</span><span class="err">80</span><span class="o">%);</span>


<span class="nt">-fx-dark-text-color</span><span class="o">:</span> <span class="nt">black</span><span class="o">;</span>
<span class="nt">-fx-mid-text-color</span><span class="o">:</span> <span class="err">#333</span><span class="o">;</span>
<span class="nt">-fx-light-text-color</span><span class="o">:</span> <span class="nt">white</span><span class="o">;</span>

<span class="nt">-fx-accent</span><span class="o">:</span> <span class="err">#0096</span><span class="nt">C9</span><span class="o">;</span>

<span class="nt">-fx-default-button</span><span class="o">:</span> <span class="nf">#ABD8ED</span><span class="o">;</span>

<span class="nt">-fx-focus-color</span><span class="o">:</span> <span class="err">#039</span><span class="nt">ED3</span><span class="o">;</span>
<span class="nt">-fx-faint-focus-color</span><span class="o">:</span> <span class="err">#039</span><span class="nt">ED322</span><span class="o">;</span>

<span class="c">/* 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 "hover" pseudoclass will typically set -fx-color to
 * -fx-hover-base (see below) and the "armed" pseudoclass will typically
 * set -fx-color to -fx-pressed-base.
 */</span>
<span class="nt">-fx-color</span><span class="o">:</span> <span class="nt">-fx-base</span><span class="o">;</span>
<span class="nt">-fx-background-color</span><span class="o">:</span> <span class="nt">-fx-background</span><span class="o">;</span>
</code></pre></div></div>
<p>I’ve left the comments in because they are useful.  Here are the main colours:</p>

<div style="width:120px;height:50px;background-color: #ABD8ED;">-fx-default-button</div>
<div style="width:120px;height:50px;background-color: #ececec;">-fx-base</div>
<div style="width:120px;height:50px;background-color: #0096C9;">-fx-accent</div>
<div style="width:120px;height:50px;background-color: #039ED3;">-fx-focus-color</div>
<p><br />
This is, as you can see, the familiar blue and gray theme that you see in most JavaFX applications.</p>

<p><img src="/assets/elements/Modena0.png" alt="Screen Snap 0" /></p>

<p>You can change it by adding the following stylesheet to your application:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.root</span> <span class="p">{</span>
  <span class="py">-fx-default-button</span><span class="p">:</span> <span class="m">#EE8434</span><span class="p">;</span>
  <span class="py">-fx-focus-color</span><span class="p">:</span> <span class="m">#C95D63</span><span class="p">;</span>
  <span class="py">-fx-faint-focus-color</span><span class="p">:</span> <span class="m">#C95D6322</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>And it will look like this:</p>

<p><img src="/assets/elements/Modena1.png" alt="Screen Snap 1" /></p>

<p>There’s a lot of other colour definitions in the <code class="language-plaintext highlighter-rouge">.root</code> section, but they virtually all involve values derived from the handful listed above.  For instance:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">/* A little lighter than -fx-base and used as the -fx-color for the
 * "hovered" pseudoclass state.
 */</span>
<span class="nt">-fx-hover-base</span><span class="o">:</span> <span class="nt">ladder</span><span class="o">(</span>
    <span class="nt">-fx-base</span><span class="o">,</span>
    <span class="nt">derive</span><span class="o">(</span><span class="nt">-fx-base</span><span class="o">,</span><span class="err">20</span><span class="o">%)</span> <span class="err">20</span><span class="o">%,</span>
    <span class="nt">derive</span><span class="o">(</span><span class="nt">-fx-base</span><span class="o">,</span><span class="err">30</span><span class="o">%)</span> <span class="err">35</span><span class="o">%,</span>
    <span class="nt">derive</span><span class="o">(</span><span class="nt">-fx-base</span><span class="o">,</span><span class="err">40</span><span class="o">%)</span> <span class="err">50</span><span class="o">%</span>
 <span class="o">);</span>
</code></pre></div></div>
<p>or:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">/* 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.
 */</span>
<span class="nt">-fx-box-border</span><span class="o">:</span> <span class="nt">ladder</span><span class="o">(</span>
    <span class="nt">-fx-color</span><span class="o">,</span>
    <span class="nt">black</span> <span class="err">20</span><span class="o">%,</span>
    <span class="nt">derive</span><span class="o">(</span><span class="nt">-fx-color</span><span class="o">,</span><span class="nt">-15</span><span class="o">%)</span> <span class="err">30</span><span class="o">%</span>
<span class="o">);</span>

<span class="c">/* Darker than -fx-background and used to draw boxes around text boxes and
 * password boxes.
 */</span>
<span class="nt">-fx-text-box-border</span><span class="o">:</span> <span class="nt">ladder</span><span class="o">(</span>
    <span class="nt">-fx-background</span><span class="o">,</span>
    <span class="nt">black</span> <span class="err">10</span><span class="o">%,</span>
    <span class="nt">derive</span><span class="o">(</span><span class="nt">-fx-background</span><span class="o">,</span> <span class="nt">-15</span><span class="o">%)</span> <span class="err">30</span><span class="o">%</span>
<span class="o">);</span>
</code></pre></div></div>
<p>You get the idea.</p>

<p>You will also see a lot of uses of <code class="language-plaintext highlighter-rouge">derive</code>, <code class="language-plaintext highlighter-rouge">gradient</code> and <code class="language-plaintext highlighter-rouge">ladder</code> in Modena.  You will need to become comfortable with these concepts in order to understand how Modena works.</p>

<h2 id="redefining-colours">Redefining Colours</h2>

<p>One “trick” that you’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:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.button</span><span class="nd">:hover</span> <span class="p">{</span>
    <span class="py">-fx-color</span><span class="p">:</span> <span class="n">-fx-hover-base</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>What this means is that for a <code class="language-plaintext highlighter-rouge">Button</code>, when it has the <code class="language-plaintext highlighter-rouge">hover</code> Pseudo-class applied, the value of <code class="language-plaintext highlighter-rouge">-fx-color</code> will be changed to be whatever color <code class="language-plaintext highlighter-rouge">-fx-hover-base</code> has been defined as.  As you’ll see in the next section <code class="language-plaintext highlighter-rouge">Button</code> has 4 background colours.  Let’s look at one of them:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">-fx-body-color</span><span class="o">:</span> <span class="nt">linear-gradient</span><span class="o">(</span><span class="nt">to</span> <span class="nt">bottom</span><span class="o">,</span>
        <span class="nt">ladder</span><span class="o">(</span>
            <span class="nt">-fx-color</span><span class="o">,</span>
            <span class="nt">derive</span><span class="o">(</span><span class="nt">-fx-color</span><span class="o">,</span><span class="err">8</span><span class="o">%)</span> <span class="err">75</span><span class="o">%,</span>
            <span class="nt">derive</span><span class="o">(</span><span class="nt">-fx-color</span><span class="o">,</span><span class="err">10</span><span class="o">%)</span> <span class="err">80</span><span class="o">%</span>
        <span class="o">),</span>
        <span class="nt">derive</span><span class="o">(</span><span class="nt">-fx-color</span><span class="o">,</span><span class="nt">-8</span><span class="o">%));</span>
</code></pre></div></div>
<p>You can see how changing <code class="language-plaintext highlighter-rouge">-fx-color</code> will change the definition of <code class="language-plaintext highlighter-rouge">-fx-body-color</code>, and this will change the look of the <code class="language-plaintext highlighter-rouge">Button</code> when the mouse is hovering over it.</p>

<p>Sometimes a <code class="language-plaintext highlighter-rouge">Node</code> will retain most of its inherited styling, but just change the definition of a colour:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.list-cell</span><span class="o">,</span>
<span class="nc">.tree-cell</span> <span class="p">{</span>
    <span class="py">-fx-background</span><span class="p">:</span> <span class="n">-fx-control-inner-background</span><span class="p">;</span>
    <span class="py">-fx-background-color</span><span class="p">:</span> <span class="n">-fx-background</span><span class="p">;</span>
    <span class="py">-fx-text-fill</span><span class="p">:</span> <span class="n">-fx-text-background-color</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Remember that this colour change will also hold for any sub-components of the <code class="language-plaintext highlighter-rouge">Node</code>.  In this case, since <code class="language-plaintext highlighter-rouge">ListCell</code> is a subclass of <code class="language-plaintext highlighter-rouge">Labeled</code>, there will be a <code class="language-plaintext highlighter-rouge">graphic</code> and a <code class="language-plaintext highlighter-rouge">text</code> element.</p>

<p>Sometimes you see this redefinition used inside a sub-component of another <code class="language-plaintext highlighter-rouge">Node</code>:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.combo-box</span> <span class="o">&gt;</span> <span class="nc">.list-cell</span> <span class="p">{</span>
    <span class="py">-fx-background</span><span class="p">:</span> <span class="nb">transparent</span><span class="p">;</span>
    <span class="py">-fx-background-color</span><span class="p">:</span> <span class="nb">transparent</span><span class="p">;</span>
    <span class="py">-fx-text-fill</span><span class="p">:</span> <span class="n">-fx-text-base-color</span><span class="p">;</span>
    <span class="py">-fx-padding</span><span class="p">:</span> <span class="m">0.333333em</span> <span class="m">0.666667em</span> <span class="m">0.333333em</span> <span class="m">0.666667em</span><span class="p">;</span> <span class="c">/* 4 8 4 8 */</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Here we see the <code class="language-plaintext highlighter-rouge">-fx-background</code> redefined from its previous redefinition when it is the the <code class="language-plaintext highlighter-rouge">ListCell</code> in the main part of a <code class="language-plaintext highlighter-rouge">ComboBox</code>.</p>

<h1 id="default-styling">Default Styling</h1>

<p>If you look at the styling for <code class="language-plaintext highlighter-rouge">list-cell</code> in the section above, you’ll see that there isn’t much there.  Where does the rest of the styling come from?</p>

<p>The hierarchy to get to <code class="language-plaintext highlighter-rouge">ListCell</code> is this<code class="language-plaintext highlighter-rouge">Node</code> -&gt; <code class="language-plaintext highlighter-rouge">Parent</code> -&gt; <code class="language-plaintext highlighter-rouge">Region</code> -&gt; <code class="language-plaintext highlighter-rouge">Control</code> -&gt; <code class="language-plaintext highlighter-rouge">Labeled</code> -&gt; <code class="language-plaintext highlighter-rouge">Cell</code> -&gt; <code class="language-plaintext highlighter-rouge">IndexedCell</code> -&gt; <code class="language-plaintext highlighter-rouge">ListCell</code>.  Not all of these classes have CSS selectors specified, <code class="language-plaintext highlighter-rouge">Node</code>, <code class="language-plaintext highlighter-rouge">Parent</code>, <code class="language-plaintext highlighter-rouge">Region</code>, <code class="language-plaintext highlighter-rouge">Control</code> and <code class="language-plaintext highlighter-rouge">Labeled</code> do not.  While there are selectors for <code class="language-plaintext highlighter-rouge">ListCell</code>, as we’ve seen, there are no selectors in Modena for <code class="language-plaintext highlighter-rouge">IndexedCell</code> or <code class="language-plaintext highlighter-rouge">Cell</code>, although there are selectors for sub-components and Pseudo-classes of <code class="language-plaintext highlighter-rouge">Cell</code>.</p>

<p>So where does the default styling come from?</p>

<p>The answer is to look at <code class="language-plaintext highlighter-rouge">Region</code> where you’ll find this:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">StyleableProperties</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">CssMetaData</span><span class="o">&lt;</span><span class="nc">Region</span><span class="o">,</span> <span class="nc">Insets</span><span class="o">&gt;</span> <span class="no">PADDING</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">CssMetaData</span><span class="o">&lt;</span><span class="nc">Region</span><span class="o">,</span> <span class="nc">Insets</span><span class="o">&gt;</span> <span class="no">OPAQUE_INSETS</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">CssMetaData</span><span class="o">&lt;</span><span class="nc">Region</span><span class="o">,</span> <span class="nc">Background</span><span class="o">&gt;</span> <span class="no">BACKGROUND</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">CssMetaData</span><span class="o">&lt;</span><span class="nc">Region</span><span class="o">,</span> <span class="nc">Border</span><span class="o">&gt;</span> <span class="no">BORDER</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">CssMetaData</span><span class="o">&lt;</span><span class="nc">Region</span><span class="o">,</span> <span class="nc">Shape</span><span class="o">&gt;</span> <span class="no">SHAPE</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">CssMetaData</span><span class="o">&lt;</span><span class="nc">Region</span><span class="o">,</span> <span class="nc">Boolean</span><span class="o">&gt;</span> <span class="no">SCALE_SHAPE</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">CssMetaData</span><span class="o">&lt;</span><span class="nc">Region</span><span class="o">,</span> <span class="nc">Boolean</span><span class="o">&gt;</span> <span class="no">POSITION_SHAPE</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">CssMetaData</span><span class="o">&lt;</span><span class="nc">Region</span><span class="o">,</span> <span class="nc">Boolean</span><span class="o">&gt;</span> <span class="no">CACHE_SHAPE</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">CssMetaData</span><span class="o">&lt;</span><span class="nc">Region</span><span class="o">,</span> <span class="nc">Boolean</span><span class="o">&gt;</span> <span class="no">SNAP_TO_PIXEL</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">CssMetaData</span><span class="o">&lt;</span><span class="nc">Region</span><span class="o">,</span> <span class="nc">Number</span><span class="o">&gt;</span> <span class="no">MIN_HEIGHT</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">CssMetaData</span><span class="o">&lt;</span><span class="nc">Region</span><span class="o">,</span> <span class="nc">Number</span><span class="o">&gt;</span> <span class="no">PREF_HEIGHT</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">CssMetaData</span><span class="o">&lt;</span><span class="nc">Region</span><span class="o">,</span> <span class="nc">Number</span><span class="o">&gt;</span> <span class="no">MAX_HEIGHT</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">CssMetaData</span><span class="o">&lt;</span><span class="nc">Region</span><span class="o">,</span> <span class="nc">Number</span><span class="o">&gt;</span> <span class="no">MIN_WIDTH</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">CssMetaData</span><span class="o">&lt;</span><span class="nc">Region</span><span class="o">,</span> <span class="nc">Number</span><span class="o">&gt;</span> <span class="no">PREF_WIDTH</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">CssMetaData</span><span class="o">&lt;</span><span class="nc">Region</span><span class="o">,</span> <span class="nc">Number</span><span class="o">&gt;</span> <span class="no">MAX_WIDTH</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">CssMetaData</span><span class="o">&lt;?</span> <span class="kd">extends</span> <span class="nc">Styleable</span><span class="o">,</span> <span class="o">?&gt;&gt;</span> <span class="no">STYLEABLES</span><span class="o">;</span>

    <span class="kd">private</span> <span class="nf">StyleableProperties</span><span class="o">()</span> <span class="o">{</span>
    <span class="o">}</span>

    <span class="kd">static</span> <span class="o">{</span>
        <span class="no">PADDING</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">CssMetaData</span><span class="o">&lt;</span><span class="nc">Region</span><span class="o">,</span> <span class="nc">Insets</span><span class="o">&gt;(</span><span class="s">"-fx-padding"</span><span class="o">,</span> <span class="nc">InsetsConverter</span><span class="o">.</span><span class="na">getInstance</span><span class="o">(),</span> <span class="nc">Insets</span><span class="o">.</span><span class="na">EMPTY</span><span class="o">)</span> <span class="o">{</span>
            <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">isSettable</span><span class="o">(</span><span class="nc">Region</span> <span class="n">var1</span><span class="o">)</span> <span class="o">{</span>
                <span class="k">return</span> <span class="n">var1</span><span class="o">.</span><span class="na">padding</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="o">!</span><span class="n">var1</span><span class="o">.</span><span class="na">padding</span><span class="o">.</span><span class="na">isBound</span><span class="o">();</span>
            <span class="o">}</span>

            <span class="kd">public</span> <span class="nc">StyleableProperty</span><span class="o">&lt;</span><span class="nc">Insets</span><span class="o">&gt;</span> <span class="nf">getStyleableProperty</span><span class="o">(</span><span class="nc">Region</span> <span class="n">var1</span><span class="o">)</span> <span class="o">{</span>
                <span class="k">return</span> <span class="o">(</span><span class="nc">StyleableProperty</span><span class="o">)</span><span class="n">var1</span><span class="o">.</span><span class="na">paddingProperty</span><span class="o">();</span>
            <span class="o">}</span>
        <span class="o">};</span>
        <span class="o">.</span>
        <span class="o">.</span>
        <span class="o">.</span>
        <span class="no">MAX_WIDTH</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">CssMetaData</span><span class="o">&lt;</span><span class="nc">Region</span><span class="o">,</span> <span class="nc">Number</span><span class="o">&gt;(</span><span class="s">"-fx-max-width"</span><span class="o">,</span> <span class="nc">SizeConverter</span><span class="o">.</span><span class="na">getInstance</span><span class="o">(),</span> <span class="o">-</span><span class="mf">1.0</span><span class="o">)</span> <span class="o">{</span>
            <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">isSettable</span><span class="o">(</span><span class="nc">Region</span> <span class="n">var1</span><span class="o">)</span> <span class="o">{</span>
                <span class="k">return</span> <span class="n">var1</span><span class="o">.</span><span class="na">maxWidth</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="o">!</span><span class="n">var1</span><span class="o">.</span><span class="na">maxWidth</span><span class="o">.</span><span class="na">isBound</span><span class="o">();</span>
            <span class="o">}</span>

            <span class="kd">public</span> <span class="nc">StyleableProperty</span><span class="o">&lt;</span><span class="nc">Number</span><span class="o">&gt;</span> <span class="nf">getStyleableProperty</span><span class="o">(</span><span class="nc">Region</span> <span class="n">var1</span><span class="o">)</span> <span class="o">{</span>
                <span class="k">return</span> <span class="o">(</span><span class="nc">StyleableProperty</span><span class="o">)</span><span class="n">var1</span><span class="o">.</span><span class="na">maxWidthProperty</span><span class="o">();</span>
            <span class="o">}</span>
        <span class="o">};</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>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 <code class="language-plaintext highlighter-rouge">Node</code>, and presumably any class in the hierachy can inject its own styleable properties into the structure.  You don’t have to look into the code to find the values, though, the <a href="https://openjfx.io/javadoc/23/javafx.graphics/javafx/scene/doc-files/cssref.html#region">CSS Reference Guide</a> for <code class="language-plaintext highlighter-rouge">Region</code> has all this information.</p>

<h1 id="enclosed-components">Enclosed Components</h1>

<p>It is important to understand that the standard JavaFX <code class="language-plaintext highlighter-rouge">Nodes</code> that are composed of other <code class="language-plaintext highlighter-rouge">Nodes</code>, especially the ones with skins, don’t have any special qualities when it comes to the stylesheets.  Any <code class="language-plaintext highlighter-rouge">Region</code> that you use in a layout to hold child <code class="language-plaintext highlighter-rouge">Nodes</code>, even nested layout classes, will interact with the stylesheet in exactly the same way.</p>

<p>CSS has two ways to specify compound selectors (which refer to <code class="language-plaintext highlighter-rouge">Nodes</code> which are children of other <code class="language-plaintext highlighter-rouge">Nodes</code>).  The first is <code class="language-plaintext highlighter-rouge">.</code>:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.slider</span> <span class="nc">.thumb</span> <span class="p">{</span>
    <span class="py">-fx-background-color</span><span class="p">:</span>
        <span class="n">linear-gradient</span><span class="p">(</span><span class="n">to</span> <span class="nb">bottom</span><span class="p">,</span> <span class="n">derive</span><span class="p">(</span><span class="n">-fx-text-box-border</span><span class="p">,</span> <span class="m">-20%</span><span class="p">),</span> <span class="n">derive</span><span class="p">(</span><span class="n">-fx-text-box-border</span><span class="p">,</span> <span class="m">-30%</span><span class="p">)),</span>
        <span class="n">-fx-inner-border</span><span class="p">,</span>
        <span class="n">-fx-body-color</span><span class="p">;</span>
    <span class="py">-fx-background-insets</span><span class="p">:</span> <span class="m">0</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="m">2</span><span class="p">;</span>
    <span class="py">-fx-background-radius</span><span class="p">:</span> <span class="m">1.0em</span><span class="p">;</span> <span class="c">/* makes sure this remains circular */</span>
    <span class="py">-fx-padding</span><span class="p">:</span> <span class="m">0.583333em</span><span class="p">;</span>  <span class="c">/* 7 */</span>
    <span class="py">-fx-effect</span><span class="p">:</span> <span class="n">dropshadow</span><span class="p">(</span><span class="n">two-pass-box</span> <span class="p">,</span> <span class="n">rgba</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">0.1</span><span class="p">),</span> <span class="m">5</span><span class="p">,</span> <span class="m">0.0</span> <span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">2</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>and the second is <code class="language-plaintext highlighter-rouge">&gt; .</code>:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.tool-bar</span> <span class="o">&gt;</span> <span class="nc">.container</span> <span class="o">&gt;</span> <span class="nc">.separator</span> <span class="p">{</span>
    <span class="py">-fx-orientation</span><span class="p">:</span> <span class="n">vertical</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>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 <strong>direct</strong> children of the first selector.  You’ll find many more usages of the second form in Modena than you will of the first form.</p>

<p>Here’s an example of how this works:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">ModenaExample1</span> <span class="p">:</span> <span class="nc">Application</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">stage</span><span class="p">.</span><span class="n">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="nf">createContent</span><span class="p">(),</span> <span class="mf">300.0</span><span class="p">,</span> <span class="mf">240.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="nc">ModenaExample1</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">.</span><span class="nf">getResource</span><span class="p">(</span><span class="s">"example1.css"</span><span class="p">)</span><span class="o">?.</span><span class="nf">toString</span><span class="p">()</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="n">stylesheets</span> <span class="p">+=</span> <span class="n">it</span> <span class="p">}</span>
        <span class="p">}</span>
        <span class="n">stage</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">createContent</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"nest-outer"</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">Button</span><span class="p">(</span><span class="s">"Outside"</span><span class="p">)</span>
        <span class="n">children</span> <span class="p">+=</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"nest-middle"</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Button</span><span class="p">(</span><span class="s">"Middle"</span><span class="p">)</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">VBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"nest-inner"</span>
                <span class="n">children</span> <span class="p">+=</span> <span class="nc">Button</span><span class="p">(</span><span class="s">"Inside"</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Application</span><span class="p">.</span><span class="nf">launch</span><span class="p">(</span><span class="nc">ModenaExample1</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
</code></pre></div></div>
<p>This is just a set of nested <code class="language-plaintext highlighter-rouge">VBoxes</code>, each having a single <code class="language-plaintext highlighter-rouge">Button</code> in them.  Here’s the stylesheet:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.nest-outer</span> <span class="p">{</span>
  <span class="py">-fx-background</span> <span class="p">:</span> <span class="no">blanchedalmond</span><span class="p">;</span>
  <span class="py">-fx-padding</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span>
  <span class="py">-fx-border-color</span><span class="p">:</span> <span class="no">green</span><span class="p">;</span>
  <span class="py">-fx-border-width</span><span class="p">:</span> <span class="m">3px</span><span class="p">;</span>
  <span class="py">-fx-body-color</span><span class="p">:</span> <span class="no">coral</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.nest-middle</span> <span class="p">{</span>
  <span class="py">-fx-border-color</span><span class="p">:</span> <span class="no">blue</span><span class="p">;</span>
  <span class="py">-fx-border-width</span><span class="p">:</span> <span class="m">3px</span><span class="p">;</span>
  <span class="py">-fx-padding</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.nest-inner</span> <span class="p">{</span>
  <span class="py">-fx-border-color</span><span class="p">:</span> <span class="no">red</span><span class="p">;</span>
  <span class="py">-fx-border-width</span><span class="p">:</span> <span class="m">3px</span><span class="p">;</span>
  <span class="py">-fx-padding</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.nest-middle</span> <span class="nc">.button</span> <span class="p">{</span>
  <span class="py">-fx-body-color</span><span class="p">:</span> <span class="no">thistle</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.nest-middle</span> <span class="o">&gt;</span> <span class="nc">.button</span> <span class="p">{</span>
  <span class="py">-fx-body-color</span><span class="p">:</span> <span class="no">aqua</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Each of the <code class="language-plaintext highlighter-rouge">VBoxes</code> has a different coloured border, so that you can see where they are, and the outermost <code class="language-plaintext highlighter-rouge">VBox</code> with the selector, <code class="language-plaintext highlighter-rouge">nest-outer</code>, redefines <code class="language-plaintext highlighter-rouge">-fx-body-color</code> which is the innermost colour in the background for <code class="language-plaintext highlighter-rouge">Button</code>.  It looks like this:</p>

<p><img src="/assets/elements/Modena3.png" alt="Screen Snap 3" /></p>

<p>First, you can see that the redefinition of <code class="language-plaintext highlighter-rouge">-fx-background</code> holds for all of the elements inside of the <code class="language-plaintext highlighter-rouge">nest-outer VBox</code> because all three <code class="language-plaintext highlighter-rouge">VBoxes</code> have that same colour.</p>

<p>The last two selectors in the stylesheet redefine <code class="language-plaintext highlighter-rouge">-fx-body-color</code> for <code class="language-plaintext highlighter-rouge">Buttons</code> inside of the <code class="language-plaintext highlighter-rouge">nest-middle VBox</code>.  The first one redefines it for <em>every</em> <code class="language-plaintext highlighter-rouge">Button</code> inside <code class="language-plaintext highlighter-rouge">nest-middle</code> and the second, the <code class="language-plaintext highlighter-rouge">&gt; .</code> selector, redefines it for <code class="language-plaintext highlighter-rouge">Buttons</code> directly contained inside <code class="language-plaintext highlighter-rouge">nest-middle</code>.  The order of the selectors inside the stylesheet is important, and if we reverse these two last selectors we get this:</p>

<p><img src="/assets/elements/Modena4.png" alt="Screen Snap 4" /></p>

<p>This is becasue the more general selector would supercede the more specific <code class="language-plaintext highlighter-rouge">&gt; .</code> selector because it came second.</p>

<h1 id="modena-doesnt-use-borders">Modena Doesn’t Use Borders!</h1>

<p>Let’s look at the styling for (amongst other things) <code class="language-plaintext highlighter-rouge">Button</code>:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.button</span> <span class="p">{</span>
    <span class="py">-fx-background-color</span><span class="p">:</span> <span class="n">-fx-shadow-highlight-color</span><span class="p">,</span> <span class="n">-fx-outer-border</span><span class="p">,</span> <span class="n">-fx-inner-border</span><span class="p">,</span> <span class="n">-fx-body-color</span><span class="p">;</span>
    <span class="py">-fx-background-insets</span><span class="p">:</span> <span class="m">0</span> <span class="m">0</span> <span class="m">-1</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="m">2</span><span class="p">;</span>
    <span class="py">-fx-background-radius</span><span class="p">:</span> <span class="m">3px</span><span class="p">,</span> <span class="m">3px</span><span class="p">,</span> <span class="m">2px</span><span class="p">,</span> <span class="m">1px</span><span class="p">;</span>
    <span class="py">-fx-padding</span><span class="p">:</span> <span class="m">0.333333em</span> <span class="m">0.666667em</span> <span class="m">0.333333em</span> <span class="m">0.666667em</span><span class="p">;</span> <span class="c">/* 4 8 4 8 */</span>
    <span class="py">-fx-text-fill</span><span class="p">:</span> <span class="n">-fx-text-base-color</span><span class="p">;</span>
    <span class="py">-fx-alignment</span><span class="p">:</span> <span class="n">CENTER</span><span class="p">;</span>
    <span class="py">-fx-content-display</span><span class="p">:</span> <span class="n">LEFT</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>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.</p>

<p>Let’s look at how this works…</p>

<p>The background on the bottom is <code class="language-plaintext highlighter-rouge">-fx-shadow-highlight-color</code> and its insets are 0 except for the bottom, which is -1.  This means that this background will exactly match the size of the <code class="language-plaintext highlighter-rouge">Button</code> on the top, left and right, but extend 1px down below the bottom of the <code class="language-plaintext highlighter-rouge">Button</code>.</p>

<p>The next background is <code class="language-plaintext highlighter-rouge">-fx-outer-border</code> and it has insets of 0 (or 0,0,0,0), which means that it will exactly match the size of the <code class="language-plaintext highlighter-rouge">Button</code>.  This will cover up all of the first background, excepty for that strip of 1px at the bottom where it sticks out below the <code class="language-plaintext highlighter-rouge">Button</code>.  This is hard to see with the normal palette on a white background, but if we change <code class="language-plaintext highlighter-rouge">-fx-shadow-highlight-color</code> to <code class="language-plaintext highlighter-rouge">red</code> in our custom colour stylesheet, it’s quite clear:</p>

<p><img src="/assets/elements/Modena2.png" alt="Screen Snap 2" /></p>

<p>The third background is <code class="language-plaintext highlighter-rouge">-fx-inner-border</code> and it has insets of 1.  This will leave a 1px wide strip all around the edge of the <code class="language-plaintext highlighter-rouge">Button</code> where the second background is visible.</p>

<p>Finally, the last background is <code class="language-plaintext highlighter-rouge">-fx-body-color</code> and it has insets of 2.  This will be the colour that you see for most of <code class="language-plaintext highlighter-rouge">Button</code> and it will leave a 1px wide ring of the third background visible just inside the dimensions of the <code class="language-plaintext highlighter-rouge">Button</code>.</p>

<p>Essentially, we have a “border” made up of three different colours - at least on the bottom - and then a background of <code class="language-plaintext highlighter-rouge">-fx-body-color</code>.  It makes the <code class="language-plaintext highlighter-rouge">Button</code> look three dimensional.</p>

<p>This is the technique that you will see used over, and over in Modena. It’s actually very rare to see a real border used in Modena.  The attribute <code class="language-plaintext highlighter-rouge">-fx-border-width</code> is set only 7 times in the entire file, and <code class="language-plaintext highlighter-rouge">-fx-border-color</code> is set to something non-transparent only 15 times.</p>

<p>So borders are used in Modena, it’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.</p>

<h1 id="the-standard-pseudoclasses">The Standard PseudoClasses</h1>

<p>In order to understand Modena, you’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.</p>

<p>All <code class="language-plaintext highlighter-rouge">Node</code> subclasses support the following Pseudo-classes:</p>

<ul>
  <li>disabled</li>
  <li>focused</li>
  <li>focus-visible</li>
  <li>focus-within</li>
  <li>hover</li>
  <li>pressed</li>
  <li>show-mnemomonic</li>
</ul>

<p>Various other <code class="language-plaintext highlighter-rouge">Node</code> subclasses support some of these common Pseudo-classes:</p>

<ul>
  <li>armed</li>
  <li>cancel</li>
  <li>default</li>
  <li>selected</li>
  <li>determinate</li>
  <li>indeterminate</li>
  <li>empty</li>
  <li>filled</li>
  <li>editable</li>
  <li>showing</li>
  <li>vertical</li>
  <li>horizontal</li>
</ul>

<p>And then there are a number of Pseudo-classes with a very specific scope.  All of these are documented in the <a href="https://openjfx.io/javadoc/23/javafx.graphics/javafx/scene/doc-files/cssref.html">JavaFX CSS Reference Guide</a>.</p>

<p>Here’s an example of how Modena commonly implements Pseudo-classes:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.toggle-button</span><span class="nd">:selected</span> <span class="p">{</span>
        <span class="py">-fx-background-color</span><span class="p">:</span>
            <span class="n">-fx-shadow-highlight-color</span><span class="p">,</span>
            <span class="n">linear-gradient</span><span class="p">(</span><span class="n">to</span> <span class="nb">bottom</span><span class="p">,</span> <span class="n">derive</span><span class="p">(</span><span class="n">-fx-outer-border</span><span class="p">,</span> <span class="m">-20%</span><span class="p">),</span> <span class="n">-fx-outer-border</span><span class="p">),</span>
            <span class="n">linear-gradient</span><span class="p">(</span><span class="n">to</span> <span class="nb">bottom</span><span class="p">,</span>
                    <span class="n">derive</span><span class="p">(</span><span class="n">-fx-color</span><span class="p">,</span> <span class="m">-22%</span><span class="p">)</span> <span class="m">0%</span><span class="p">,</span>
                    <span class="n">derive</span><span class="p">(</span><span class="n">-fx-color</span><span class="p">,</span> <span class="m">-13%</span><span class="p">)</span> <span class="m">20%</span><span class="p">,</span>
                    <span class="n">derive</span><span class="p">(</span><span class="n">-fx-color</span><span class="p">,</span> <span class="m">-11%</span><span class="p">)</span> <span class="m">50%</span><span class="p">);</span>
    <span class="py">-fx-background-insets</span><span class="p">:</span> <span class="m">0</span> <span class="m">0</span> <span class="m">-1</span> <span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This is the <code class="language-plaintext highlighter-rouge">selected</code> Pseudo-class for a <code class="language-plaintext highlighter-rouge">ToggleButton</code>.  You can see that it’s mostly a colour change, which isn’t surprising.  This definition sets the background colour and insets.</p>

<p>You can see that there are three different background colours stacked on top of each other to create a three dimensional “border” in a manner similar to what we saw with <code class="language-plaintext highlighter-rouge">Button</code>.  And that’s all this Pseudo-class does, changes the background and border so that it looks “selected”.</p>

<p>You will also see Pseudo-classes stacked quite often:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.toggle-button</span><span class="nd">:selected:focused</span> <span class="p">{</span>
    <span class="py">-fx-background-color</span><span class="p">:</span>
        <span class="n">-fx-focus-color</span><span class="p">,</span>
        <span class="n">linear-gradient</span><span class="p">(</span><span class="n">to</span> <span class="nb">bottom</span><span class="p">,</span>
            <span class="n">derive</span><span class="p">(</span><span class="n">-fx-color</span><span class="p">,</span> <span class="m">-22%</span><span class="p">)</span> <span class="m">0%</span><span class="p">,</span>
            <span class="n">derive</span><span class="p">(</span><span class="n">-fx-color</span><span class="p">,</span> <span class="m">-13%</span><span class="p">)</span> <span class="m">20%</span><span class="p">,</span>
            <span class="n">derive</span><span class="p">(</span><span class="n">-fx-color</span><span class="p">,</span> <span class="m">-11%</span><span class="p">)</span> <span class="m">50%</span><span class="p">),</span>
        <span class="n">-fx-faint-focus-color</span><span class="p">,</span>
        <span class="n">linear-gradient</span><span class="p">(</span><span class="n">to</span> <span class="nb">bottom</span><span class="p">,</span>
            <span class="n">derive</span><span class="p">(</span><span class="n">-fx-color</span><span class="p">,</span> <span class="m">-22%</span><span class="p">)</span> <span class="m">0%</span><span class="p">,</span>
            <span class="n">derive</span><span class="p">(</span><span class="n">-fx-color</span><span class="p">,</span> <span class="m">-13%</span><span class="p">)</span> <span class="m">20%</span><span class="p">,</span>
            <span class="n">derive</span><span class="p">(</span><span class="n">-fx-color</span><span class="p">,</span> <span class="m">-11%</span><span class="p">)</span> <span class="m">50%</span><span class="p">);</span>
    <span class="py">-fx-background-insets</span><span class="p">:</span> <span class="m">-0.2</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="m">-1.4</span><span class="p">,</span> <span class="m">2.6</span><span class="p">;</span>
    <span class="py">-fx-background-radius</span><span class="p">:</span> <span class="m">3</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">4</span><span class="p">,</span> <span class="m">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This is the selector for a <code class="language-plaintext highlighter-rouge">ToggleButton</code> that is both “selected” and “focused”.</p>

<h1 id="shared-definitions">Shared Definitions</h1>

<p>When multiple selectors have the same settings, Modena will combine them into a single entry using “,” to separate the selectors.  For instance, the selector that defines <code class="language-plaintext highlighter-rouge">Button</code> is</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.button</span><span class="o">,</span>
<span class="nc">.toggle-button</span><span class="o">,</span>
<span class="nc">.radio-button</span> <span class="o">&gt;</span> <span class="nc">.radio</span><span class="o">,</span>
<span class="nc">.check-box</span> <span class="o">&gt;</span> <span class="nc">.box</span><span class="o">,</span>
<span class="nc">.menu-button</span><span class="o">,</span>
<span class="nc">.choice-box</span><span class="o">,</span>
<span class="nc">.color-picker.split-button</span> <span class="o">&gt;</span> <span class="nc">.color-picker-label</span><span class="o">,</span>
<span class="nc">.combo-box-base</span><span class="o">,</span>
<span class="nc">.combo-box-base</span><span class="nd">:editable</span> <span class="o">&gt;</span> <span class="nc">.arrow-button</span> <span class="p">{</span>
  <span class="err">.</span>
  <span class="err">.</span>
  <span class="err">.</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This means that if you want to change the styling of <code class="language-plaintext highlighter-rouge">Button</code> you’re going to have to decide if you really just mean <code class="language-plaintext highlighter-rouge">Button</code> or everything that is “button-like”.  Otherwise, your application could take on an inconsistent look.</p>

<h1 id="compound-nodes">Compound Nodes</h1>

<p>There are a large number of <code class="language-plaintext highlighter-rouge">Node</code> classes that are actually composed of multiple internal <code class="language-plaintext highlighter-rouge">Nodes</code>.  Each of these internal components can have their own CSS selectors and be styled individually.</p>

<p>Many times you can spot these in Modena.  Here’s <code class="language-plaintext highlighter-rouge">CheckBox</code>:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.check-box</span> <span class="p">{</span>
    <span class="py">-fx-label-padding</span><span class="p">:</span> <span class="m">0.0em</span> <span class="m">0.0em</span> <span class="m">0.0em</span> <span class="m">0.416667em</span><span class="p">;</span> <span class="c">/* 0 0 0 5 */</span>
    <span class="py">-fx-text-fill</span><span class="p">:</span> <span class="n">-fx-text-background-color</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.check-box</span> <span class="o">&gt;</span> <span class="nc">.box</span> <span class="p">{</span>
    <span class="py">-fx-background-radius</span><span class="p">:</span> <span class="m">3</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">1</span><span class="p">;</span>
    <span class="py">-fx-padding</span><span class="p">:</span> <span class="m">0.166667em</span> <span class="m">0.166667em</span> <span class="m">0.25em</span> <span class="m">0.25em</span><span class="p">;</span> <span class="c">/* 2 2 3 3 */</span>
<span class="p">}</span>
<span class="nc">.check-box</span> <span class="o">&gt;</span> <span class="nc">.box</span> <span class="o">&gt;</span> <span class="nc">.mark</span> <span class="p">{</span>
    <span class="py">-fx-background-color</span><span class="p">:</span> <span class="n">null</span><span class="p">;</span>
    <span class="py">-fx-padding</span><span class="p">:</span> <span class="m">0.416667em</span> <span class="m">0.416667em</span> <span class="m">0.5em</span> <span class="m">0.5em</span><span class="p">;</span> <span class="c">/* 5 5 6 6 */</span>
    <span class="py">-fx-shape</span><span class="p">:</span> <span class="s1">"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"</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>But how do we know that this is all of the internal components that might be styled?  Just as there’s no requirement that all of the Pseudo-classes for each <code class="language-plaintext highlighter-rouge">Node</code> are styled in Modena, it’s possible that internal components are defined that are just left at their default styling.</p>

<p>To find out definitively, we will need to check the source code for the <code class="language-plaintext highlighter-rouge">Node</code>.  Let’s do that for <code class="language-plaintext highlighter-rouge">CheckBox</code>…</p>

<p>If we put a call to <code class="language-plaintext highlighter-rouge">CheckBox()</code> in an application, then we can click through and see the decompiled code for <code class="language-plaintext highlighter-rouge">CheckBox</code>.  From there we can do a search for “getStyleClass”, which has to be called to add a CSS selector to the class.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kt">void</span> <span class="nf">initialize</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">.</span><span class="na">getStyleClass</span><span class="o">().</span><span class="na">setAll</span><span class="o">(</span><span class="k">new</span> <span class="nc">String</span><span class="o">[]{</span><span class="s">"check-box"</span><span class="o">});</span>
    <span class="k">this</span><span class="o">.</span><span class="na">setAccessibleRole</span><span class="o">(</span><span class="nc">AccessibleRole</span><span class="o">.</span><span class="na">CHECK_BOX</span><span class="o">);</span>
    <span class="k">this</span><span class="o">.</span><span class="na">setAlignment</span><span class="o">(</span><span class="nc">Pos</span><span class="o">.</span><span class="na">CENTER_LEFT</span><span class="o">);</span>
    <span class="k">this</span><span class="o">.</span><span class="na">setMnemonicParsing</span><span class="o">(</span><span class="kc">true</span><span class="o">);</span>
    <span class="k">this</span><span class="o">.</span><span class="na">pseudoClassStateChanged</span><span class="o">(</span><span class="no">PSEUDO_CLASS_DETERMINATE</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The first thing we can see is that all of the <code class="language-plaintext highlighter-rouge">StyleClasses</code> that might have been inherited from any superclasses are cleaned out via the <code class="language-plaintext highlighter-rouge">setAll()</code> method.  And the <code class="language-plaintext highlighter-rouge">CheckBox</code> overall is assigned the selector of <code class="language-plaintext highlighter-rouge">check-box</code>.  There are no other calls to <code class="language-plaintext highlighter-rouge">getStyleClass()</code> in the class.  This means that any other CSS selectors have to be assigned in the skin.</p>

<p>We need to find where the skin is created.  Searching in the code for “skin” gives this:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protected</span> <span class="nc">Skin</span><span class="o">&lt;?&gt;</span> <span class="n">createDefaultSkin</span><span class="o">()</span> <span class="o">{</span>
     <span class="k">return</span> <span class="k">new</span> <span class="nf">CheckBoxSkin</span><span class="o">(</span><span class="k">this</span><span class="o">);</span>
 <span class="o">}</span>
</code></pre></div></div>
<p>If we click-through into this we’ll come to the constructor for <code class="language-plaintext highlighter-rouge">CheckBoxSkin</code> which gives is this:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nf">CheckBoxSkin</span><span class="o">(</span><span class="nc">CheckBox</span> <span class="n">var1</span><span class="o">)</span> <span class="o">{</span>
    <span class="kd">super</span><span class="o">(</span><span class="n">var1</span><span class="o">);</span>
    <span class="k">this</span><span class="o">.</span><span class="na">behavior</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ButtonBehavior</span><span class="o">(</span><span class="n">var1</span><span class="o">);</span>
    <span class="k">this</span><span class="o">.</span><span class="na">box</span><span class="o">.</span><span class="na">getStyleClass</span><span class="o">().</span><span class="na">setAll</span><span class="o">(</span><span class="k">new</span> <span class="nc">String</span><span class="o">[]{</span><span class="s">"box"</span><span class="o">});</span>
    <span class="k">this</span><span class="o">.</span><span class="na">innerbox</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">StackPane</span><span class="o">();</span>
    <span class="k">this</span><span class="o">.</span><span class="na">innerbox</span><span class="o">.</span><span class="na">getStyleClass</span><span class="o">().</span><span class="na">setAll</span><span class="o">(</span><span class="k">new</span> <span class="nc">String</span><span class="o">[]{</span><span class="s">"mark"</span><span class="o">});</span>
    <span class="k">this</span><span class="o">.</span><span class="na">innerbox</span><span class="o">.</span><span class="na">setNodeOrientation</span><span class="o">(</span><span class="nc">NodeOrientation</span><span class="o">.</span><span class="na">LEFT_TO_RIGHT</span><span class="o">);</span>
    <span class="k">this</span><span class="o">.</span><span class="na">box</span><span class="o">.</span><span class="na">getChildren</span><span class="o">().</span><span class="na">add</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">innerbox</span><span class="o">);</span>
    <span class="k">this</span><span class="o">.</span><span class="na">updateChildren</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<p>And a search for “getStyleClass” 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 <code class="language-plaintext highlighter-rouge">CheckBox</code> with these three selectors.</p>

<p>We can also now find out two very valuable pieces of information - the types of <code class="language-plaintext highlighter-rouge">box</code> and <code class="language-plaintext highlighter-rouge">mark</code>.  This is important because this will tell us what styling attributes are available to us to customize.</p>

<p>Right above that constructor are these two lines:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kd">final</span> <span class="nc">StackPane</span> <span class="n">box</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">StackPane</span><span class="o">();</span>
<span class="kd">private</span> <span class="nc">StackPane</span> <span class="n">innerbox</span><span class="o">;</span>
</code></pre></div></div>
<p>Now we know that both of these are <code class="language-plaintext highlighter-rouge">StackPanes</code>.  We could have guessed that they were some kind of <code class="language-plaintext highlighter-rouge">Region</code> from the CSS in Modena, but now we know exactly what they are.</p>

<h2 id="exploring-via-nodelookupall">Exploring Via Node.lookupAll()</h2>

<p>If looking through the source code seems daunting, there is another method you can try.  <code class="language-plaintext highlighter-rouge">Node</code> has a method called <code class="language-plaintext highlighter-rouge">lookupAll()</code> which will accept wildcard characters.  This means that you can call <code class="language-plaintext highlighter-rouge">Node.lookupAll("*")</code> to find all of the styleclass selectors that are present in a compound <code class="language-plaintext highlighter-rouge">Control</code>.  It works like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">ModenaExample0</span> <span class="p">:</span> <span class="nc">Application</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">override</span> <span class="k">fun</span> <span class="nf">start</span><span class="p">(</span><span class="n">stage</span><span class="p">:</span> <span class="nc">Stage</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">stage</span><span class="p">.</span><span class="n">scene</span> <span class="p">=</span> <span class="nc">Scene</span><span class="p">(</span><span class="nf">createContent</span><span class="p">())</span>
        <span class="n">stage</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">createContent</span><span class="p">():</span> <span class="nc">Region</span> <span class="p">=</span> <span class="nc">BorderPane</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">checkBox</span> <span class="p">=</span> <span class="nc">CheckBox</span><span class="p">().</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">styleClass</span> <span class="p">+=</span> <span class="s">"fred"</span>
        <span class="p">}</span>
        <span class="n">center</span> <span class="p">=</span> <span class="nc">HBox</span><span class="p">(</span><span class="mf">10.0</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="nc">Button</span><span class="p">(</span><span class="s">"Button"</span><span class="p">).</span><span class="nf">apply</span> <span class="p">{</span>
                <span class="n">isDefaultButton</span> <span class="p">=</span> <span class="k">false</span>
                <span class="n">onAction</span> <span class="p">=</span> <span class="nc">EventHandler</span> <span class="p">{</span>
                    <span class="n">checkBox</span><span class="p">.</span><span class="nf">lookupAll</span><span class="p">(</span><span class="s">"*"</span><span class="p">).</span><span class="nf">forEach</span> <span class="p">{</span>
                        <span class="nf">println</span><span class="p">(</span><span class="s">"Id: ${it.id}\tStyleClass: ${it.styleClass}\t\tType: ${it::class.java.simpleName}"</span><span class="p">)</span>
                    <span class="p">}</span>
                <span class="p">}</span>
            <span class="p">}</span>
            <span class="n">children</span> <span class="p">+=</span> <span class="n">checkBox</span>
        <span class="p">}</span>
        <span class="n">padding</span> <span class="p">=</span> <span class="nc">Insets</span><span class="p">(</span><span class="mf">30.0</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fun</span> <span class="nf">main</span><span class="p">()</span> <span class="p">=</span> <span class="nc">Application</span><span class="p">.</span><span class="nf">launch</span><span class="p">(</span><span class="nc">ModenaExample0</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
</code></pre></div></div>
<p>You need to run <code class="language-plaintext highlighter-rouge">Node.lookupAll()</code> after the SceneGraph has been displayed.  That’s why it has been implemented inside the <code class="language-plaintext highlighter-rouge">onAction EventHandler</code> for a <code class="language-plaintext highlighter-rouge">Button</code>.  When you click on the <code class="language-plaintext highlighter-rouge">Button</code>, this is what you get:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Id: 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
</code></pre></div></div>
<p>We see <code class="language-plaintext highlighter-rouge">check-box</code>, <code class="language-plaintext highlighter-rouge">box</code> and <code class="language-plaintext highlighter-rouge">mark</code>, as well as <code class="language-plaintext highlighter-rouge">fred</code> that I explicitly added to the <code class="language-plaintext highlighter-rouge">CheckBox</code> to see how it would show in the listing.  We get to see all of the selectors added to each <code class="language-plaintext highlighter-rouge">Node</code>, which is good.</p>

<p>But what’s this <code class="language-plaintext highlighter-rouge">text</code>??? We can see that it’s a <code class="language-plaintext highlighter-rouge">LabeledText</code>, but where did it come from?</p>

<p>If we go back to the skin of <code class="language-plaintext highlighter-rouge">ComboBox</code>, we see that it extends <code class="language-plaintext highlighter-rouge">LabeledSkinBase</code>.  If we look at <code class="language-plaintext highlighter-rouge">LabeledSkinBase</code> and search for usages of <code class="language-plaintext highlighter-rouge">LabeledText</code> we find it right away at the top of the class:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">LabeledSkinBase</span><span class="o">&lt;</span><span class="no">C</span> <span class="kd">extends</span> <span class="nc">Labeled</span><span class="o">&gt;</span> <span class="kd">extends</span> <span class="nc">SkinBase</span><span class="o">&lt;</span><span class="no">C</span><span class="o">&gt;</span> <span class="o">{</span>
    <span class="nc">LabeledText</span> <span class="n">text</span><span class="o">;</span>
    <span class="o">.</span>
    <span class="o">.</span>
    <span class="o">.</span>
<span class="o">}</span>
</code></pre></div></div>
<p>But you won’t find any place where it is assigned a styleclass selector.  However, if you look at the source code for <code class="language-plaintext highlighter-rouge">LabeledText</code> you will find it assigns itself the selector <code class="language-plaintext highlighter-rouge">text</code>, and this just stays when it’s part of <code class="language-plaintext highlighter-rouge">CheckBoxSkin</code>.</p>

<p>If you want to know everything that you might style in a JavaFX <code class="language-plaintext highlighter-rouge">Control</code>, especially one with a skin, then it looks like the best place to start is with a test program like this that uses <code class="language-plaintext highlighter-rouge">Node.lookupAll("*")</code> to find all of the components that stylesheet selectors.  However, this won’t show you how the various components are related to each other, and how they are used.  For that, you’ll have to look at the source code.</p>

<p>All that being said, for <code class="language-plaintext highlighter-rouge">CheckBox</code> the elements are pretty clear.  The <code class="language-plaintext highlighter-rouge">text</code> element is obviously the text beside the checkbox, and the mark is the checkmark that goes inside the checkbox.  Other skinned <code class="language-plaintext highlighter-rouge">Controls</code> could be much more complicated.</p>

<h1 id="customizing-modena-selectors">Customizing Modena Selectors</h1>

<p>I see lots and lots of example programs, especially when people are asking questions on StackOverflow, where they’ll employ a custom style sheet that uses one of the standard <code class="language-plaintext highlighter-rouge">Node</code> selectors.  Like <code class="language-plaintext highlighter-rouge">button</code> or <code class="language-plaintext highlighter-rouge">check-box</code>.  This is fine in a small sample program, but isn’t necessarily the best approach with a bigger application with multiple screens.</p>

<p>There’s two potential issues with this.</p>

<p>The first is that you are changing the styling of <em>every</em> <code class="language-plaintext highlighter-rouge">Node</code> of that type, everywhere.  Is this really what you want to do?  OK, the Modena styling isn’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.</p>

<p>The second is that many of the stylings work together.  If you go back and look at the styling for <code class="language-plaintext highlighter-rouge">button</code>, you can see that it is shared with 9 other selectors, some of which are enclosed in other <code class="language-plaintext highlighter-rouge">Nodes</code>.  Do you want to just change <code class="language-plaintext highlighter-rouge">Button</code>?  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 <code class="language-plaintext highlighter-rouge">Nodes</code>.</p>

<p>Essentially, you are coupling your styling change all over your application.  That’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’ve made a change for a specific use case and it impacts seemingly unrelated parts of your GUI.</p>

<p>The alternative is to create a new selector and then perform <code class="language-plaintext highlighter-rouge">styleClass += "{new selector}"</code> on the <code class="language-plaintext highlighter-rouge">Node</code>.  Now you are just styling a particular instance of that <code class="language-plaintext highlighter-rouge">Node</code> type, and you won’t have any global issues.  If you are adding this new styleclass selector to a whole bunch of instances of the <code class="language-plaintext highlighter-rouge">Node</code> then create a builder method to instantiate the <code class="language-plaintext highlighter-rouge">Node</code> with the selector added.</p>

<h1 id="conclusion">Conclusion</h1>

<p>My biggest complaint about the JavaFX documentation is that there is no “Styling Guide” that details <strong>all</strong> of the styleclass selectors and nested components of the standard JavaFX <code class="language-plaintext highlighter-rouge">Nodes</code>.  It would make life so much easier to be able to simply look up the documentation for a skin class and see what they’ve put there for you to customize.  Instead, you’ll have to pour over the source code, search through Modena and figure out how make it work.</p>

<p>However, once you’re comfortable with the techniques used in Modena it is fairly easy to understand how the styling that you see on screen (for most <code class="language-plaintext highlighter-rouge">Nodes</code>) is achieved through the stylesheet.  Hopefully, this article has given you the tools to understand these techniques.</p>]]></content><author><name>Dave Barrett</name><email>pragmaticcoding8@gmail.com</email></author><category term="javafx" /><summary type="html"><![CDATA[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.]]></summary></entry></feed>