QWv3 – Dev blog 20

My apologies for the delay in posting. The move to using RichTextFX has been a long and tortuous adventure.

RichTextFX is a great component but it only provides the “ability” to do certain things. I’ve had to actually implement end-user features that make use of these abilities. I’ve also had to push RichTextFX far past what it was initially designed to do and as such, when you are experimenting on the razor’s edge, you should expect some blood.

Thankfully, Jugen, the current maintainer of RichTextFX has been a fantastic help in quickly providing requested features and updates. The guy is a legend and has been very patient when I’ve been bombarding him with my idiocy.

Two of the most important features of QW (to me at least) are now, essentially, done, they are:

  1. The text margin and the ability to “mark” a location in the text. i.e. adding scenes, outline items, notes.
  2. The Problem Finder.

The text margin has been the biggest pain for me and it wasn’t until I stopped adding nodes to the margin while laying out the margin did things become easier. To be honest this was a stupid approach to being with but, in my defense, it prevented a number of other issues that, once I separated out adding nodes from laying out nodes, needed to be tackled.

I also had a lot of issues with the difference between the following two calls:

editMarker.resizeRelocate()

and

editMarker.relocate()

The “editMarker” is the strip of color that is displayed in the margin when the chapter has an edit position or is marked as edit complete.

Unfortunately, using “resizeRelocate” would cause an infinite loop but “relocate” just positions the node, it doesn’t do anything with size. I’ve still no idea what the issue is with resizeRelocate but I finally got around the problem by using the following instead:

double w = this.editMarker.prefWidth (-1);
this.editMarker.setPrefHeight (h);
this.editMarker.autosize ();
this.editMarker.relocate (b.getMaxX () - w, 0);

This code determines the desired width (gained from the stylesheet), sets the height based on the edit location, sets the layout bounds then sets the position of the marker.

Here’s a picture of it all in action:

One of the hard learned lessons I’ve had from developing for RichTextFX is that everything needs to be done on the next layout pulse. That is, you need to give the text area time to get itself in order before you do anything with it.

For example, here is the code that allows me to scroll the text area to a specific point (for example to display an outline item popup).

    public void scrollToTextPosition (int      pos,
                                      Runnable afterScroll)
    {
        Bounds b = this.editor.getBoundsForPosition (pos);
        if (b == null)
        {
            int p = this.editor.getParagraphForOffset (pos);
            this.editor.showParagraphInViewport (p);
            UIUtils.runLater (() ->
            {
                this.scrollToTextPosition (pos,
                                           afterScroll);
            });
            return;
        }

        Bounds eb = this.editor.localToScreen (this.editor.getBoundsInLocal ());
        double diff = b.getMinY () - eb.getMinY () - (eb.getHeight () / 2);
        this.editor.scrollYBy (diff);
        UIUtils.runLater (afterScroll);

    }

The UIUtils.runLater is just a wrapper around Platform.runLater, this allows me to do error handling. You’ll notice here that I have do the actual work, i.e. the “afterScroll” Runnable after potentially making 2 runLater calls, i.e. on the 2nd layout pulse after the initial call. If the position is not currently visible on screen then I need to make the paragraph visible, then try and get the location of the position, then scroll it into a reasonable view (in this make it in the center of the view, the paragraph may be so big that it is larger than the entire view and the position may still be offscreen) then finally call “afterScroll” to do whatever I initially wanted to do.

Another issue I’ve had is the slightly bizarre way that RichTextFX handles styles. To style a particular piece of text you need to create a new style object and you need to make sure that Java knows it is a new style object. Java is a little weird on how it handles whether two objects are “the same”, depending upon the circumstance you need to do different things. To implement the Problem Finder I needed to be able to “highlight” a particular piece of text. An example is shown below.

So here the “block” of text that is at fault has been highlighted with a background color. However, when you move your mouse over the checkbox for the problem, the exact problem text is highlighted. For example:

Sounds easy enough doesn’t it? Just add a different color when moving the mouse over the text. But what happens when you move the mouse away? The text needs to revert to the previous “block” color. RichTextFX doesn’t support this out of the box so I had to add a set of “background colors” that the style could have. This is a stack of colors that the style keeps track of and it displays the color that is at the top. This allows me to keep layering colors and then just remove the top color as needed “exposing” the one underneath. Java Swing had this concept via the use of a “Highlight” and I’ve implemented that same concept in QW.

But I was stuck for a while because when I removed the highlight the background would remain the same color. Eventually I realized that it was because Java thought that the styles were the same and RichTextFX wasn’t updating the necessary style. It was an easy fix but difficult to diagnose.

Another issue I’ve had, not related to RichTextFX this time, is that the JavaFX Labeled class doesn’t support the use of a TextFlow, it only uses Text. This has huge implications for what you can and can’t do with labels and controls derived from Labeled. TextFlow was introduced in JavaFX 8 to allow for “rich text” style formatting of text, it makes uses of multiple Text objects, each styled in their own way, to form a string of text. But the Labeled class hasn’t been updated to make use of TextFlow, the upshot of this is that Labeled controls can only have a single style which means that all text in the label looks the same.

So you can’t have some of the text be bold, or a different color, or a different font size or underlined or etc, etc, etc. It baffles me why they haven’t updated the JavaFX controls to use TextFlow.

In short, I have to create my own controls to provide this obviously desirable functionality. I could, and probably will eventually have to, create a real Control but it is not as straightforward as it should be. There are modules that are not exposed that need to be and creating a custom skin would be a lot of work for something that is relatively minor at the moment and can be worked around. But it IS annoying.

The upshot of all this is that my long detour into ditching Java Swing for pure JavaFX components is nearly at an end. It was a painful detour, a long and frustrating journey but it will be worth it in the end… hopefully.

3 thoughts on “QWv3 – Dev blog 20

  1. Sean M Carlisle

    Keep up the great work, Fam.

  2. Ron Wheeler

    I want to be able to edit CSS files from a JavaFX program.
    There seems to be very little documentation about how to do and not many documented editors that other have built.
    Thanks for sharing your experience.

    1. Sorry Ron I missed your comment. You could look at the RichTextFX project. I’m using it for version 3 of QW.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: