JavaFX from the Trenches – Part 2 – It’s Not Always About the Sex

Editorial Note: I wanted to be fair in writing this article, and it is not my goal to attack anyone personally. I contacted Jonathan Giles, tech lead for UI Components and let him know about the article ahead of time. He reviewed it for me and offered some insights without getting defensive. I am not claiming that Jonathan endorses the article, but I am grateful for his time and effort, and share his vision of a community moving JavaFX forward. -EAT

Just before Christmas I released a commercial app (Evidentia) for the PC and MAC. That’s right, PC and MAC AT THE SAME TIME!

However the process was NOT painless, and in this series of “from the Trenches” posts, I plan to document lessons learned in hopes that others may suffer less.

Let me repeat that I am a fan of JavaFX2. You can do some great things with it, and I LOVE being able to use style sheets. However, in this series I plan to be open, honest and direct including stating matter-of-factly that JavaFX2, or more accurately 2.2, was not as stable as I had expected a 2.2 version to be.
I could NOT have been successful without the help of the folks at the JavaFX2 Forum!

There are a lot of blog posts, twitters, and the like about the sexy side of JavaFX. You can do some great things with it, and on cutting edge hardware to boot. High end graphics, 3D graphics, shiny new controls – they all show well at a conference.

But for a commercial application written for those with minimal tech savvy, it’s the simple things that matter. I went into this project with a simple, not so sexy expectation: anything I could do with a web control or with Java Swing would have its counterpart in JavaFX2. I was 80% right, but I ran into some trouble along the way. I will use my “favorite” control, the ComboBox, as an example.

ComboBox

I started work on Evidentia in March of 2012, about the same time this blog post came out at JavFXperience: New in JavaFX2.1: ComboBox I was excited that JavaFX was well along, and noted without concern that the autocomplete feature was NOT included yet; no concern because I had implemented autocomplete on a control before, and it wasn’t critical for my first release anyway.

I have this one dialog that has become a real pain. The requirement is simple: collect and return a list of Subject/claim combinations for the calling window.
AttachSubject
Did I say simple? The user needs to be able to pick a subject and/or a claim type from a drop down list. If the name they want is not in the list, they need to be able to add it on the fly. Oh and if they add a name, it should be available in the drop down list should they add another entry. And it would be nice if while they typed, a filtered list of available names matching the partial text were displayed.

Prime candidate for a ComboBox!

My head still hurts from the experience. I would say over 30% of my time, even after BETA started, was spent writing, testing, rewriting, retesting this one dialog. Now a LOT of that can be chalked up with the learning curve for JavaFX2 in general. I am not saying the learning curve is high, but it exists. I learn fast, but not spontaneously!

To be completely fair, the set of requirements around this dialog box were the most complex – the application does a lot of nifty things that the non-tech savvy think is hard – but we know it’s not. Also, the expectations of my BETA testers were high for this screen – it serves a critical role in the process they use, and they had a strong set of expectations about how it should work – after all, can’t I do all that in a web page?

But I digress…

Getting down to it

OK, so a built the box, much like you see above. I made the ComboBox editable, slapped in some property bindings, threw a Key Press Event Handler in the mix for the filter and voila!Mount Vesuvius!

volcano

OK, my fault, too much all at once. Back up, cut out, recompile. Here are the real issues I ran into:
When is an edit not an edit?

When it’s ‘edit on enter only’. ┬áComboBox shares what I consider to be the same weakness other text controls share in JavaFX2 – losing focus does not commit the edit. Mouse Out? Edit not saved to the bound property. Tab out? No data. TextField does not share this weakness, but TextArea and table cells do. Not sure what the thinking is here, but from a user perspective, they expect when they type something in and tab out that there data is still there. And if they mouse out to the Save button, they certainly expect it to be persisted!

“Yes,” you say, “but if you want that behavior, just code for it.”; to which I say – it shouldn’t be that hard. IMHO it should work that way by default – don’t want it to? YOU code for it. It’s what the users expect…

So I coded for it. Ran into an issue where when the focus handler is called, you can’t tell if the control is gaining focus or losing it (for which I opened JIRA RT-24512), but I went with it anyway. Worked. Sometimes? Most of the time? Users expect all the time. I’ll come back to this.

Stuck in a ditch

OK, so the user starts to type, the event handler kicks in and the drop down is displayed. User keeps typing to enter a new name, hits the tab key because they are done and….nothing. Once the drop down was displayed the tab key no longer would let you exit the control for some reason. Navigation hassle, had to use the mouse. Started a forum discussion, noticed some JIRA issues out there already, so I moved on for now. (I went back today to look for a JIRA issue to reference, but couldn’t find one that I thought captured the issue – EAT)

Oops, did it again

Let me start out with this – I COULD NOT create this problem outside my app, and my app was certainly too complex to include in a JIRA ticket.

I discovered when the dialog was closed and I went back to the parent window – the data showed up! But if I reopened the Attach Subject Dialog, entered a new entry (rather than selecting from the drop down), and closed it again – the new entry showed up, but the old entry was overwritten by the new as well!?

I never found out why, no matter how many experiments I tried. I couldn’t create it outside my app, so I am confident I was doing something wrong, so I re-architected the whole thing. I am sorry to say I stripped out the property bindings at this point. With the unreliability of the edit model and this issue, it was adding too much complexity to the code. I take all the blame on this one, but wondered – should it be this hard?

Out of style

If you read various forum posts, you will discover that styling the elements of a ComboBox is NOT straightforward. Apparently this is true for any of the controls using a Popup Window and the ComboBoxis just a victim. Unfortunately this is not documented in the styling documentation, and excessive forum reading is the only solution.

I just wanted the CB to look like my other controls. It shouldn’t be that hard. I eventually received several good suggestions on the forum and got it to work – well sort of. It worked when I ran the code in Scene Builder, but when I compiled and ran the same code, the styling disappeared. I opened JIRA RT-25674.

Deal Breaker

I was in the last weeks of BETA when I finally had to give up. The final straw was that SOMETIMES when you exited the ComboBox, the edit was lost. This seemed to only occur A) the first time you entered data (never when you selected from list) and B) when the CB was backed by an Object, not a String – that is, when a StringConverter was in the mix. Unfortunately I could not create a little re-creatable program that would allow a JIRA that I created to get any attention. It was the final weeks. I was desperate….

My Waterloo

On December 10, 2012, with a ceremonious post on the JavaFX Forum, I surrendered. People were sympathetic. Some suggested that I never should have used JavaFX2 – it was too new, too immature.

Yeah cause dumping it now, after 8 months of my life and a product almost ready for commercial release, THAT was an option ;). I was committed. Besides, frustration aside I was still a believer!

So I removed the ComboBox and started to code my own.

I didn’t want anywhere near the popup window though. And I was running out of time. This was my LAST bug!

StackPane + ChoiceBox + TextField = ComboBox (if you add a whole bunch of event handlers). It only took me a couple of hours. It was reliable, and I had ultimate control. I was quite proud of myself. Until I accidentally performance tested it!

Turns out there is a bug in the ChoiceBox – JIRA RT-26837 I talk about on the JavaFX2 Forum. It got the attention of Jonathan Giles at Oracle, and he escalated the fix, but it won’t be out until Java8 is released. I didn’t have that kind of time.

StackPane + ComboBox + TextField = ComboBox. OK, its strange I know, using a ComboBox to replace the ComboBox. But it works! The ComboBox does not suffer from the same performance issue the ChoiceBox did. the TextField does not suffer the same weaknesses the edit field of the ComboBox does.

Using the StackPane, I was able to lay the TextField over the edit field of the ComboBox. I made the TextField shorter so that one could still use the drop down arrow of the ComboBox. The ComboBox itself is NOT editable.

       <StackPane fx:id="subjectNameStack">
          <children>
            <ComboBox fx:id="subjectDropdown" focusTraversable="false" maxWidth="1.7976931348623157E308" prefHeight="25.0" prefWidth="150.0" style="-fx-border-color:transparent;" StackPane.alignment="CENTER_LEFT">
              <items>
              </items>
            </ComboBox>
            <TextField fx:id="subjectName" editable="true" maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="25.0" prefWidth="130.0" promptText="Person, Company, Place, Thing..." style="-fx-border-color:transparent;">
              <StackPane.margin>
                <Insets right="20.0" />
              </StackPane.margin>
            </TextField>
          </children>
        </StackPane>

When text is entered in the TextField, it stays in the TextField. When a selection is made form the dropdown box, it populates the TextField. There was a lot of extra coding of course:

  • Code to hide dropdown when the TextField loses focus
  • Code to copy dropdown selection to TextField
  • Code for StringConverter for ComboBox (OK I just put that one BACK in)

I was also able to add back the autocomplete filters that I had abandoned in the interest of simplicity weeks before!

    @FXML ComboBox subjectDropdown;
    @FXML TextField subjectName;
 
   @Override
   public void initialize(URL url, ResourceBundle rb) {
       subjectName.setOnKeyReleased(new SubjectFilter());
       subjectName.setOnKeyPressed(new SubjectPressed());
   ...
   }
 
    private class SubjectPressed implements EventHandler {
        @Override
        public void handle(KeyEvent t) {
 
            if(t.getCode() == KeyCode.TAB) {
                subjectDropdown.hide();
            } 
        }
    }    
   private class SubjectFilter implements EventHandler {
        @Override
        public void handle(KeyEvent t) {
 
            if(t.getCode() == KeyCode.DOWN) {
                subjectDropdown.requestFocus();
                subjectDropdown.show();
            } else {
                String partial = subjectName.getText();
                filterSubject(oldPartialSubject, partial);
                oldPartialSubject = partial;
                mySubjectClaim.setId("");
                mySubjectClaim.setRefNo("");
                mySubjectClaim.setName(oldPartialSubject);
                mySubjectClaim.setClaimType("");
            }
        }
    }
    public void filterSubject(String oldVal, String newVal) {
        // If the number of characters in the text box is less than last time
        // it must be because the user pressed delete
        ObservableList tempList;
        if ( oldVal != null &amp;&amp; (newVal.length() &lt; oldVal.length()) ) {             // Restore the lists original set of entries             // and start from the beginning             tempList =  ListManager.getInstance().getSubjectList();         } else {             tempList = subjectDropdown.getItems();         }         // Break out all of the parts of the search text         // by splitting on white space         String[] parts = newVal.toLowerCase(Locale.US).split(" ");         // Filter out the entries that don't contain the entered text         subjectSubentries.clear();         for ( Subject entry:  tempList ) {             boolean match = true;             String entryText = entry.getName();             for ( String part: parts ) {                 // The entry needs to contain all portions of the                 // search string *but* in any order                 if ( ! entryText.toLowerCase(Locale.US).contains(part) ) {                     match = false;                     break;                 }             }             if ( match ) {                 subjectSubentries.add(entry);             }         }         subjectDropdown.setItems(subjectSubentries);         if ( newVal != null &amp;&amp; !newVal.isEmpty() &amp;&amp; subjectSubentries.size() &gt; 0 ) {
            if(!subjectDropdown.isShowing()) {
                subjectDropdown.show();
            }
        } else {
            if(subjectDropdown.isShowing()) {
                subjectDropdown.hide();
            }    
        }
    }
 
    private static class SubjectToStringConverter extends StringConverter {
        @Override
        public String toString(Subject t) {
            return ((t==null)?null:t.toString());
        }
 
        @Override
        public Subject fromString(String string) {
            Subject subject = new Subject();
            subject.setName(string);
            return subject;
        }
    }

Cumbersome perhaps, but it works, it works reliably, and the users don’t know the difference!

Summary

I honestly think if the ComboBox hadn’t been so hard to get working, and if I hadn’t needed it so desperately (what other control could have met user expectations for this type of functionality), I would not have gotten as frustrated as I did. I remember thinking at one time, I don’t CARE about all the SEXY things I am reading on the blogs and through Twitter. I just want basic controls that were easy to use and reliable!

Now that’s not fair; I know the people experimenting with the sexy side of JavaFX aren’t the ones responsible for UI functionality – they are developers doing wonderful things with a new technology, and they will ultimately be responsible for the widespread acceptance of JavaFX2. I get that. I WANT that.

But my potential customers don’t care that I used JavaFX to write Evidentia. I can’t say “It’s a limitation of the technology”. They want apps that work, work as expected, and work reliably.

(I did have to claim that in one instance for functionality that just isn’t there for MACOS, but apparently MAC users are used to that, and were happy to get the app any way. Poor excuse, I know, and I can’t wait until RT-153 and RT-20784 are addressed!)

I just hope Oracle is paying attention to the less sexy side of things. Users expect that anything they can do on a web page they can do in a desktop app. Developers expect that anything they can do in Swing they can do in JavaFX.

Me? I just want to write nifty genealogy applications, not JIRA tickets.

The world moves on. JavaFX will get better. Jave 8 will solve everything .

Two out of three ain’t bad.

This entry was posted in Evidentia, JavaFX. Bookmark the permalink.

5 Responses to JavaFX from the Trenches – Part 2 – It’s Not Always About the Sex

  1. Pingback: JavaFX links of the week, December 31 // JavaFX News, Demos and Insight // FX Experience

  2. Werner Lehmann says:

    I feel your pain. Had some (unresolved) problems with it myself. Mostly along the lines that the combobox selection model would report an item to be selected but on screen I am seeing a different one. Or vice versa, an item was selected but the selection model did not pick it up. Could well be related to what you observed, commit on enter – I did not verify this so far. In any case it is also a pain because users will expect certain behaviour for what they see on screen (a particular selected item), and they get different behaviour (using the item actually reported by the selection model).

    Spent a day or two to work around this and trying to reproduce it for a Jira ticket. Even if it gets resolved for Java 8 eventually, that doesn’t really cut it. It will take maybe 2 years until Java 8 is available at some (not all) of my customers…

    • Ed says:

      A couple things to consider – first, one of the benefits of native packaging with JavaFX is that the JRE gets deployed with your app – you don’t have to wait for your client to upgrade!

      Second, if the patch is available in the openjdk, you could consider copying the code you need into your own environment, create your own control with the fixed version.

  3. anda says:

    I had to give up using the ComboBox for my application. It seems to me that its just not ready for any kind of autocomplete/filtering. Its such a vital control, really frustrating not to be able to use it.

    I challenge everyone from the JavaFX team to try to use the ComboBox to get some sort of browser address bar functionality. Displaying a filtered list as you type into the textfield and being able to select a value from that list.

Add Comment Register



Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>