[JAVA] Implémentez des fonctionnalités d'édition de texte enrichi avec RichTextFX

Cet article est le 14ème jour du Calendrier de l'Avent JavaFX 2016. Hier, c'était "Définir l'icône native avec javafx-maven-plugin" de @ sk44_. Demain, ce sont les "Notes sur l'appel de l'API Payara Micro depuis JavaFX" de @ khasunuma.

Aperçu

Lors de l'implémentation d'un éditeur dans JavaFX, vous êtes-vous déjà senti insatisfait de l'expressivité et des subtilités de TextArea standard?

Dans cet article, je vais vous présenter la bibliothèque "RichTextFX" que je voudrais recommander à ceux qui veulent faire des outils avec fonction éditeur avec JavaFX ou qui l'ont fait.


Qu'est-ce que RichTextFX?

Une bibliothèque développée par Tomas Mikula qui fournit des composants d'édition de texte riche. Vous pouvez trouver le code de démonstration et son utilisation dans le référentiel GitHub.

Licence

Licence BSD à 2 clauses et licence double d'exception de chemin de classe GPLv2.

Exigences

JDK 8 ou supérieur, car il utilise javafx.scene.text.TextFlow Est requis.

Ce que vous pouvez faire avec RichTextFX

  1. Affichage du numéro de ligne
  2. Points forts de la syntaxe
  3. Changer la police

Présentez d'abord

environnement

L'environnement utilisé dans cette introduction est le suivant.

Java SE 1.8.0_102
OS Windows 10
Gradle 3.0

Code d'essai

Utilisez FizzBuzz de Groovy comme un code très simple.

FizzBuzz.groovy


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

Avant correction

ss2016_1.png

Remplacer en premier

Ajout de la dépendance Gradle

Ajoutez la ligne suivante aux dépendances.

build.gradle


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

Modification FXML

Il est implémenté comme une entité complètement distincte du TextArea standard et n'est pas compatible. Faites attention.

Ajouter une importation

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

Remplacer la balise TextArea par CodeArea

Veuillez noter que l'info-bulle ne peut pas être utilisée.

-        <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" />

Modification du code source

La classe Controller doit également être modifiée.

Correction de l'importation

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

Remplacer TextArea par CodeArea

TextArea et CodeArea ne sont pas compatibles et doivent être remplacés.

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

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

Changer setText pour remplacerText

Veuillez noter que les noms des méthodes sont également différents.

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

Capture d'écran modifiée

Dans son état d'origine, il n'y a pas de mise en surbrillance du code, pas d'affichage du numéro de ligne et pas de retour à la ligne.

ss2016_2.png


Afficher le numéro de ligne

Ensuite, affichons le numéro de ligne.

Ajouter une importation

+import org.fxmisc.richtext.LineNumberFactory;

Spécification de ParagraphGraphicFactory

Pour chaque objet CodeArea, spécifiez ParagraphGraphicFactory comme indiqué ci-dessous.

Définir l'affichage du numéro de ligne avec setParagraphGraphicFactory


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

Capture d'écran modifiée

rtfx1.png

De cette manière, le numéro de ligne est affiché.

rtfx2.png

Si vous essayez de couper la ligne, le nombre de lignes s'affiche autant que le saut de ligne.

rtfx3.png

S'il dépasse 100 lignes, il s'affichera correctement sur 3 chiffres.


Points forts du code

CodeArea nécessite une petite implémentation, mais il peut être mis en évidence avec un format / mot-clé spécifique. Une fonctionnalité de cette bibliothèque est la mise en évidence asynchrone. Vous pouvez mettre en évidence vos modifications en temps réel. L'implémentation utilise ReactFX, une bibliothèque de Reactive Streams développée par le même Tomas Mikula.

Un exemple d'implémentation dans le langage de programmation Java est fourni ci-dessous.

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

Mise en œuvre des points forts du code Groovy

Cette fois, implémentons un exemple dans Groovy.

Créer une liste de mots-clés Groovy

Il semble qu'il y ait 57 types ci-dessous. Si vous êtes un expert, veuillez le signaler si vous faites une erreur.

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

Enregistrez-le dans un fichier texte.

Préparer le fichier CSS pour la coloration des mots-clés

Cette fois, [Échantillon officiel](https://github.com/TomasMikula/RichTextFX/blob/2b4ba4378cfe27facfd86051771946ed9c0dc691/richtextfx-demos/src/main/resources/org/fxmisc/rich/demo est utilisé comme mot-clé). Je vais l'utiliser.

CSS pour les mots-clés


.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;
}

Vous pouvez placer ce fichier CSS n'importe où, mais cette fois, c'est juste sous src / main / resources.

Définir la classe Highlight de base

Tout d'abord, définissez une classe qui possède des méthodes couramment utilisées pour mettre en évidence les implémentations. Élevé à partir du code de démonstration JavaKeywordsAsync.java Extrayez uniquement la pièce à traiter et définissez uniquement le traitement commun en tant que classe abstraite.

Exemple d'implémentation: Highlight.java

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

méthode de mise en évidence

Une méthode qui exécute un traitement asynchrone. Appelez cette méthode depuis votre application.

Groovy Créer une classe Highlight individuelle

Héritez de la classe Highlight ci-dessus pour créer une classe qui met en évidence la syntaxe de Groovy.

Exemple d'implémentation: GroovyHighlight.java

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

méthode makePattern

Cette méthode lit les mots-clés à partir du fichier de mots-clés préparé précédemment et crée une expression régulière pour la mise en évidence du code. Les expressions régulières autres que les mots-clés sont prédéfinies. Vous pouvez créer une classe Highlight pour chaque langue en modifiant les éléments suivants pour chaque langue.

Expression régulière pour la mise en évidence du code


    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)*?\\*/";
méthode computeHighlighting

Une méthode qui prend le texte de CodeArea et le convertit dans un format qui permet la mise en évidence du code.

Ajouter un traitement à la classe Controller

Ajoutez ce qui suit à la dernière ligne de la méthode initialize.

initialize()


new GroovyHighlight(scripterInput).highlight();

En outre, ajoutez l'URI du fichier CSS pour la coloration des mots clés que vous avez préparée précédemment aux feuilles de style de l'objet Scene.

src/main/Directement sous les ressources"keywords.css"Vers les feuilles de style


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

Ceci termine la préparation.

Capture d'écran modifiée

Essayez de saisir le code FizzBuzz ci-dessous.

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

Comme vous pouvez le voir, int est rougeâtre, les commentaires sont vert clair et les chaînes littérales entre "sont bleues.

Exemple d'implémentation

Pour d'autres exemples d'implémentation simples de points forts du code, veuillez consulter le référentiel ci-dessous si vous êtes intéressé (l'application elle-même n'est pas incluse).

https://github.com/toastkidjp/simple_highlight


(Ajout) À propos du problème de l'entrée japonaise

J'ai recommandé RichTextFX jusqu'à présent, mais j'ai écrit cet article avant de l'intégrer dans l'application réelle, et plus tard j'ai remarqué un bogue qui ne peut pas être négligé, je vais donc l'ajouter.

Qu'est-ce qui ne va pas?

Jetez d'abord un œil à l'image ci-dessous. Une fenêtre avec trois zones de texte alignées s'affiche, et les candidats à la conversion sont affichés en entrant «à côté de» dans la zone de texte à gauche (CodeArea de RichTextFX).

rtfx1.png

Avez-vous ressenti un inconfort? Oui, le texte saisi et les candidats à la conversion sont affichés dans le coin supérieur gauche de l'écran. C'est un état assez stupide ... C'est la classe de contrôle de texte de RichTextFX [javafx.scene.control.TextInputControl](https://docs.oracle.com/javase/jp/8/javafx/api/javafx/scene/ Il semble que la cause soit que control / TextInputControl.html) n'est pas implémenté. ~~ Je me demande si l'auteur n'a pas eu besoin de saisir des caractères multi-octets …… ~~

Personne d'autre n'a pensé que c'était drôle?

Bien sûr, ce n'était pas le cas, et la personne qui a signalé et proposé des mesures d'amélioration a créé le problème.

How to insert text using an Input Method #146

Implémenter InputMethodRequests

En regardant le problème ci-dessus, il a été dit que "j'ai implémenté InputMethodRequests et je l'ai résolu", j'ai donc pensé essayer s'il pouvait être amélioré, et j'ai implémenté ceci [EditorInputMethodRequests](https: // github. com / toastkidjp / RichTextFX_verification / blob / verification / src / main / java / jp / toastkid / rtfx_verification / EditorInputMethodRequests.java). Il était assez facile à mettre en œuvre en utilisant la méthode getCaretBounds () ajoutée dans SNAPSHOT 1.0.0.

codeArea.setInputMethodRequests(new EditorInputMethodRequests(improved));

Eh bien, plutôt que cela, implémentez le contenu de setOnInputMethodTextChanged C'était plus difficile, mais ...

codeArea.setOnInputMethodTextChanged(this::handleInputMethodEvent);

Après amélioration

La zone de texte centrale est l'endroit où ces correctifs sont appliqués. Cette

rtfx2.png

Le texte d'entrée s'affiche correctement à la position du report et les candidats à la conversion sont également affichés en dessous. Enfin, c'est à un niveau qui peut être utilisé comme un éditeur minimum.

Des jours d'amélioration sans fin

Je pensais que ce n'était pas le cas.

rtfx4.png

La zone de texte sur la droite est le TextArea standard JavaFX. La partie incertaine du texte d'entrée est soulignée et la plage des candidats à la conversion est mise en évidence avec un arrière-plan bleu. Il est difficile de le mettre en pratique s'il n'atteint pas ce niveau, mais nous n'avons pas été en mesure de mettre en œuvre les fonctions jusqu'à présent. Si vous êtes intéressé, il peut être intéressant de l'essayer en vous référant au code des 3 classes suivantes.

  1. TextInputControlSkin
  2. TextFieldSkin
  3. TextAreaSkin

1.0.0-Notes sur l'utilisation de SNAPSHOT

Pour utiliser RichTextFX 1.0.0-SNAPSHOT à partir du 6 février 2017, ajoutez `` https: // oss.sonatype.org / content / repositories / snapshots / '' dans les référentiels de build.gradle. Il est nécessaire d'ajouter la spécification du référentiel de.

build.gradle


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

Exemple de référentiel d'application (GitHub)

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


JavaFX 9 ou version ultérieure

Selon l'article de @ nodamushi "JavaFX 9 s'améliore" du JavaFX Advent Calendar 2016, JavaFX 9 Il semble qu'il soit possible d'implémenter TextArea qui ne peut afficher les numéros de ligne qu'avec l'API standard. Les attentes augmentent.


Résumé

Nous venons de présenter brièvement RichTextFX, une bibliothèque qui vous aide à implémenter des fonctionnalités d'édition de texte enrichi dans JavaFX. Le référentiel officiel contient divers codes de démonstration qui n'ont pas pu être introduits cette fois, tels que l'éditeur XML et le redimensionnement des polices. Si vous développez un éditeur Markdown ou HTML, ou si vous souhaitez insérer un exemple de code dans un outil de présentation développé avec JavaFX, vous pouvez envisager d'introduire cette bibliothèque.


référence

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

Ce code source

Recommended Posts

Implémentez des fonctionnalités d'édition de texte enrichi avec RichTextFX
Implémenter le lien texte avec Springboot + Thymeleaf