[JAVA] Implement rich text editing capabilities with RichTextFX

This article is the 14th day of JavaFX Advent Calendar 2016. Yesterday was @ sk44_'s "Set native icon with javafx-maven-plugin". Tomorrow is @ khasunuma's "Notes on calling Payara Micro API from JavaFX".

Overview

When implementing an editor in JavaFX, have you ever felt unsatisfied with the subtleties of expressiveness and extensibility of the standard TextArea?

--Font adjustment is difficult & all characters must be in the same font --Line numbers cannot be displayed by default --Syntax highlighting is not possible

In this article, I will introduce the library "RichTextFX" that I would like to recommend to those who want to make a tool with an editor function with JavaFX or who have made it.


What is RichTextFX?

A library developed by Tomas Mikula that provides rich text editing components. You can find demo code and usage in the GitHub repository.

license

BSD 2-Clause License and GPLv2 Classpath Exception Dual License.

Requirements

Since javafx.scene.text.TextFlow is used, JDK 8 or higher Is required.

What you can do with RichTextFX

  1. Line number display
  2. Syntax highlighting
  3. Change font

Introduce first

environment

The environment used in this introduction is as follows.

Java SE 1.8.0_102
OS Windows 10
Gradle 3.0

Trial code

Use Groovy's FizzBuzz as a very simple code.

FizzBuzz.groovy


(1..100)
  .collect{it % 15 == 0 ? "FizzBuzz" :  it % 3 ==0 ? "Fizz" : it % 5 == 0 ? "Buzz" : it}
  .forEach{print it + ", "}

Before correction

ss2016_1.png

Replace first

Added Gradle dependency

Add the following line to dependencies.

build.gradle


dependencies {
+  compile 'org.fxmisc.richtext:richtextfx:0.7-M2'
......
}

FXML modification

It is implemented as completely separate from the standard TextArea and is not compatible. be careful.

Add import

<?import org.fxmisc.richtext.CodeArea?>

Replace TextArea tag with CodeArea

Please note that Tooltip cannot be used.

-        <TextArea fx:id="scripterInput" prefHeight="550.0" prefWidth="500.0">
-          <tooltip><Tooltip text="input script." /></tooltip>
-        </TextArea>
+        <CodeArea fx:id="scripterInput" prefHeight="550.0" prefWidth="500.0" />

Source code modification

The Controller class also needs to be modified.

Fix import

-import javafx.scene.control.TextArea;
+import org.fxmisc.richtext.CodeArea;

Replace TextArea with CodeArea

TextArea and CodeArea are not compatible and need to be replaced.

     @FXML
-    public TextArea scripterInput;
+    public CodeArea scripterInput;

     @FXML
-    public TextArea scripterOutput;
+    public CodeArea scripterOutput;

Change setText to replaceText

Please note that the method names are also different.

-        scripterOutput.setText(result);
+        scripterOutput.replaceText(result);

Corrected screenshot

In its original state, there is no code highlighting, no line number display, and no wrapping.

ss2016_2.png


Display line numbers

Next, let's display the line number.

Add import

+import org.fxmisc.richtext.LineNumberFactory;

Specifying ParagraphGraphicFactory

For each CodeArea object, specify ParagraphGraphicFactory as shown below.

Set line number display with setParagraphGraphicFactory


scripterOutput.setParagraphGraphicFactory(LineNumberFactory.get(scripterInput));

Corrected screenshot

rtfx1.png

The line numbers are displayed like this.

rtfx2.png

If you try to break the line, the number of lines is displayed as much as the line break.

rtfx3.png

If it exceeds 100 lines, it will be displayed properly in 3 digits.


Code highlights

CodeArea requires a little implementation, but it can be highlighted with a specific format / keyword. A feature of this library is asynchronous highlighting. You can highlight your edits in real time. The implementation uses ReactFX, a library of Reactive Streams developed by the same Tomas Mikula.

An implementation example in the programming language Java is provided below.

https://github.com/TomasMikula/RichTextFX/blob/master/richtextfx-demos/src/main/java/org/fxmisc/richtext/demo/JavaKeywordsAsync.java

Implemented Groovy code highlights

Since it's a big deal, let's implement an example in Groovy this time.

Create a list of Groovy keywords

It seems that there are 57 types below. If you are an expert, please point out if you make a mistake.

Groovy's&nbsp;keywords


abstract
as
assert
boolean
break
byte
case
catch
char
class
const
continue
def
default
do
double
else
enum
extends
false
final
finally
float
for
goto
if
implements
import
in
instanceof
int
interface
long
native
new
null
package
private
protected
public
return
short
static
strictfp
super
switch
synchronized
this
threadsafe
throw
throws
transient
true
try
void
volatile
while

Save this in a text file.

Prepare a CSS file for keyword coloring

This time, Official sample is used as it is. I will use it.

CSS for keywords


.keyword {
    -fx-fill: purple;
    -fx-font-weight: bold;
}
.semicolon {
    -fx-font-weight: bold;
}
.paren {
    -fx-fill: firebrick;
    -fx-font-weight: bold;
}
.bracket {
    -fx-fill: darkgreen;
    -fx-font-weight: bold;
}
.brace {
    -fx-fill: teal;
    -fx-font-weight: bold;
}
.string {
    -fx-fill: blue;
}

.comment {
    -fx-fill: cadetblue;
}

.paragraph-box:has-caret {
    -fx-background-color: #f2f9fc;
}

You can put this CSS file anywhere, but this time it's right under src / main / resources.

Define the base Highlight class

First, define a class that has a method that is commonly used in the highlighting implementation. High from demo code JavaKeywordsAsync.java Extract only the part to be processed, and define only the common processing as an abstract class.

Implementation example: Highlight.java

https://github.com/toastkidjp/javafx_advent2015/blob/v2016/src/main/java/jp/toastkid/highlight/Highlight.java

highlight method

A method that executes asynchronous processing. The application calls this method.

Groovy Create a separate Highlight class

Inherit the Highlight class above to create a class that highlights the Groovy grammar.

Implementation example: GroovyHighlight.java

https://github.com/toastkidjp/javafx_advent2015/blob/v2016/src/main/java/jp/toastkid/highlight/GroovyHighlight.java

makePattern method

This method reads keywords from the keyword file prepared earlier and builds a regular expression for code highlighting. Regular expressions other than keywords are predefined. You can create a Highlight class for each language by modifying the following for each language.

Regular expression for code highlighting


    private static final String PAREN_PATTERN     = "\\(|\\)";
    private static final String BRACE_PATTERN     = "\\{|\\}";
    private static final String BRACKET_PATTERN   = "\\[|\\]";
    private static final String SEMICOLON_PATTERN = "\\;";
    private static final String STRING_PATTERN    = "\"([^\"\\\\]|\\\\.)*\"";
    private static final String COMMENT_PATTERN   = "//[^\n]*" + "|" + "/\\*(.|\\R)*?\\*/";
computeHighlighting method

A method that gets the CodeArea text and converts it into a code highlighting format.

Add processing to Controller class

Add the following to the last line of the initialize method.

initialize()


new GroovyHighlight(scripterInput).highlight();

In addition, add the URI of the keyword coloring CSS file you prepared earlier to the stylesheets of the Scene object.

src/main/Directly under resources"keywords.css"To stylesheets


final ObservableList<String> stylesheets = thisStage.getScene().getStylesheets();
stylesheets.add(getClass().getClassLoader().getResource("keywords.css").toExternalForm());

This completes the preparation.

Corrected screenshot

Try entering the FizzBuzz code below.

FizzBuzz


int i = 0

(1..100)
  .collect{it % 15 == 0 
    ? "FizzBuzz" 
    : it % 3 ==0 
      ? "Fizz" 
      : it % 5 == 0 
        ? "Buzz" 
        : it
  }
  .forEach{print it + ", "}

rtfx4.png

As you can see, ints are reddish, comments are light green, and string literals enclosed in "are blue.

Implementation sample

I have put some simple implementation examples of other code highlights in the repository below, so if you are interested, please have a look (the application itself is not included).

https://github.com/toastkidjp/simple_highlight


(Addition) About Japanese input problem

I've recommended RichTextFX so far, but I wrote this article before embedding it in the actual application, and I noticed a bug that can not be overlooked later, so I will add it.

What's wrong?

First, take a look at the image below. A window with three text areas lined up is displayed, and you can enter "next to" in the text area on the left (CodeArea of RichTextFX) to display conversion candidates.

rtfx1.png

Did you feel any discomfort? Yes, the entered text and conversion candidates are displayed in the upper left of the screen. It's pretty stupid ... This is RichTextFX's text control class [javafx.scene.control.TextInputControl](https://docs.oracle.com/javase/jp/8/javafx/api/javafx/scene/ It seems that the cause is that control / TextInputControl.html) is not implemented. ~~ I wonder if the author didn't need to enter multi-byte characters …… ~~

Didn't anyone else think it was funny?

Of course, that was not the case, and the person who pointed out and proposed improvement measures created the issue.

How to insert text using an Input Method #146

Implement InputMethodRequests

Looking at the above issue, it was said that "I implemented InputMethodRequests and solved it", so I thought I would try if it could be improved, and I implemented it here [EditorInputMethodRequests](https://github. com / toastkidjp / RichTextFX_verification / blob / verification / src / main / java / jp / toastkid / rtfx_verification / EditorInputMethodRequests.java). It was fairly easy to implement using the getCaretBounds () method added in 1.0.0 of SNAPSHOT.

codeArea.setInputMethodRequests(new EditorInputMethodRequests(improved));

Well, rather than that, implement contents of setOnInputMethodTextChanged It was harder, but ...

codeArea.setOnInputMethodTextChanged(this::handleInputMethodEvent);

After improvement

The central text area is where these fixes are applied. This

rtfx2.png

The input text is displayed properly at the caret position, and conversion candidates are also displayed below it. At last, it is a level that can be used as a minimum editor.

Days of never-ending improvement

I thought that wasn't the case.

rtfx4.png

The text area on the right is the JavaFX standard TextArea. The uncertain part of the input text is underlined and the range of conversion candidates is highlighted with a blue background. It is difficult to put it to practical use unless it reaches this level, but we have not been able to implement the functions so far. If you are interested, it may be interesting to try it by referring to the code of the following 3 classes.

  1. TextInputControlSkin
  2. TextFieldSkin
  3. TextAreaSkin

1.0.0-Notes on using SNAPSHOT

To use RichTextFX 1.0.0-SNAPSHOT as of February 6, 2017, add `` `https://oss.sonatype.org/content/repositories/snapshots/``` in the repositories of build.gradle. It is necessary to add the repository specification of.

build.gradle


repositories {
    mavenCentral()
    maven {
        url 'https://oss.sonatype.org/content/repositories/snapshots/'
    }
}

Sample application repository (GitHub)

https://github.com/toastkidjp/RichTextFX_verification/tree/verification


JavaFX 9 or later

According to @ nodamushi's article "JavaFX 9 is getting better" of JavaFX Advent Calendar 2016, JavaFX 9 It seems that the implementation of TextArea that can display line numbers is possible only with the standard API. Expectations rise.


Summary

We have just briefly introduced RichTextFX, a library that helps you implement rich text editing capabilities in JavaFX. The official repository contains a variety of demo code that couldn't be introduced this time, such as an XML editor and font resizing. If you are developing a Markdown editor or HTML editor, or if you want to put sample code in a presentation tool developed in JavaFX, you may want to consider introducing this library.


reference

RichTextFX CSS Reference Guide https://github.com/TomasMikula/RichTextFX/wiki/RichTextFX-CSS-Reference-Guide

This source code

-Differences corrected this time -Whole code modified this time

Recommended Posts

Implement rich text editing capabilities with RichTextFX
Implement text link with Springboot + Thymeleaf