diff --git a/.github/assets/ActionsDemo.png b/.github/assets/ActionsDemo.png
new file mode 100644
index 00000000..8247c36d
Binary files /dev/null and b/.github/assets/ActionsDemo.png differ
diff --git a/.github/assets/BasicDemo.png b/.github/assets/BasicDemo.png
new file mode 100644
index 00000000..94b949f3
Binary files /dev/null and b/.github/assets/BasicDemo.png differ
diff --git a/.github/assets/EmojiDemo.png b/.github/assets/EmojiDemo.png
new file mode 100644
index 00000000..a2854d1d
Binary files /dev/null and b/.github/assets/EmojiDemo.png differ
diff --git a/.github/assets/EmojiPopupDemo.png b/.github/assets/EmojiPopupDemo.png
new file mode 100644
index 00000000..804678f5
Binary files /dev/null and b/.github/assets/EmojiPopupDemo.png differ
diff --git a/.github/assets/HighlightDemo.png b/.github/assets/HighlightDemo.png
new file mode 100644
index 00000000..3a738f67
Binary files /dev/null and b/.github/assets/HighlightDemo.png differ
diff --git a/.github/assets/ListsDemo.png b/.github/assets/ListsDemo.png
new file mode 100644
index 00000000..d3ff1063
Binary files /dev/null and b/.github/assets/ListsDemo.png differ
diff --git a/README.md b/README.md
index bc1a0f63..172b8a70 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,8 @@ Based on the combination of VirtualFlow and multiple TextFlow controls, the Rich
- Emoji support
- … (and more to come)
+![rta_editor.png](.github/assets/rta_editor.png)
+
### License
The RichTextArea control is available for free under the GPLv3 license. If you create an Open Source application, you can use our software for free.
@@ -34,41 +36,29 @@ The RichTextArea control is available for free under the GPLv3 license. If you c
To use the RichTextArea control in your project add the following dependency:
```
-
-
- com.gluonhq
- rich-text-area
- 1.0.0
-
-
+
+ com.gluonhq
+ rich-text-area
+ 1.0.0
+
```
and then simply create an instance and add it to your JavaFX application:
```
- @Override
- public void start(Stage stage) {
- RichTextArea editor = new RichTextArea();
- BorderPane root = new BorderPane(editor);
- Scene scene = new Scene(root, 800, 600);
- stage.setScene(scene);
- stage.show();
- }
+@Override
+public void start(Stage stage) {
+ RichTextArea editor = new RichTextArea();
+ BorderPane root = new BorderPane(editor);
+ Scene scene = new Scene(root, 800, 600);
+ stage.setScene(scene);
+ stage.show();
+}
```
### Samples
-#### RichTextEditor sample
-
-One possible use of the RichTextArea control is as a RichTextEditor. The sample includes a number of menus and toolbars that allow the user apply actions over selections or at the caret location.
-
-#### Instructions
-
-To run this sample, using Java 17+, do as follows:
-
- mvn javafx:run -fsamples
-
-![rta_editor.png](.github/assets/rta_editor.png)
+Multiple samples, from basic to full-featured can be found in the [samples folder](samples).
## Issues and Contributions
diff --git a/samples/README.md b/samples/README.md
new file mode 100644
index 00000000..e469349e
--- /dev/null
+++ b/samples/README.md
@@ -0,0 +1,147 @@
+# The RichTextArea Samples
+
+> Note: all instructions assume you changed into the samples folder:
+> ```shell
+> cd samples
+> ```
+
+## BasicDemo
+
+The [BasicDemo](/samples/src/main/java/com/gluonhq/richtextarea/samples/BasicDemo.java) is the most simple use case of
+the RichTextArea control: it shows a prompt message, and the user can add text.
+
+While all the control features are available, there are no menus or toolbars included, so user interaction is limited
+to shortcuts or the context menu.
+
+For instance, after typing some text, select all (Ctrl/Cmd + A) or part of it (with mouse or keyboard) and press
+Ctrl/Cmd + I for italic or Ctrl/Cmd + B for bold.
+
+Undo/Redo, Cut/Copy/Paste options work as usual. You can also copy text with emoji unicode, and paste it on the editor.
+For instance, while running this sample, copy this text:
+
+```
+Hello Rich Text Area 👋🏼
+```
+
+and paste it inside the RichTextArea control, you should see the waving hand emoji and some text. Also copying from
+the control and pasting it on the control itself or on any other application will work
+too, keeping the rich content when possible.
+
+Right click to display a context menu with different options, like inserting a
+2x1 table.
+
+To apply the rest of the control features, some UI is needed for user interaction. See
+the [FullFeaturedDemo](#fullfeatureddemo) sample for a complete and advanced showcase.
+
+### Usage
+
+To run this sample, using Java 17+, do as follows:
+
+```
+mvn javafx:run -Dmain.class=com.gluonhq.richtextarea.samples.BasicDemo
+```
+
+![rta_editor.png](../.github/assets/BasicDemo.png)
+
+## HighlightDemo
+
+The [HighlightDemo](/samples/src/main/java/com/gluonhq/richtextarea/samples/HighlightDemo.java) shows how to use the
+RichTextArea control to add a document with some decorations, that are generated by searching some keywords over a
+given text.
+
+### Usage
+
+To run this sample, using Java 17+, do as follows:
+
+```
+mvn javafx:run -Dmain.class=com.gluonhq.richtextarea.samples.HighlightDemo
+```
+
+![rta_editor.png](../.github/assets/HighlightDemo.png)
+
+## ListsDemo
+
+The [ListsDemo](/samples/src/main/java/com/gluonhq/richtextarea/samples/ListsDemo.java) shows a list, generated with
+custom numbered and bulleted decorations created via `RichTextArea::paragraphGraphicFactoryProperty`.
+
+### Usage
+
+To run this sample, using Java 17+, do as follows:
+
+```
+mvn javafx:run -Dmain.class=com.gluonhq.richtextarea.samples.ListsDemo
+```
+
+![rta_editor.png](../.github/assets/ListsDemo.png)
+
+## ActionsDemo
+
+The [ActionsDemo](/samples/src/main/java/com/gluonhq/richtextarea/samples/ActionsDemo.java) shows how to use the
+RichTextArea control to render some text and interact with it in a basic way via three toggle buttons.
+
+Run the sample and select some or all text, via mouse or keyboard, and then press the toggles to see how the decoration
+of the selection changes accordingly.
+
+Note that when you move the caret over the text, the toggles update their state
+(enabled means bold/italic/underline active), showing at any time the current decoration at the caret.
+
+### Usage
+
+To run this sample, using Java 17+, do as follows:
+
+```
+mvn javafx:run -Dmain.class=com.gluonhq.richtextarea.samples.ActionsDemo
+```
+
+![rta_editor.png](../.github/assets/ActionsDemo.png)
+
+## EmojiDemo
+
+The [EmojiDemo](/samples/src/main/java/com/gluonhq/richtextarea/samples/EmojiDemo.java) shows how to use the
+RichTextArea control to render text and emojis.
+
+This sample doesn't include a control to select interactively emojis (See [EmojiPopupDemo](#emojipopupdemo) for that).
+
+### Usage
+
+To run this sample, using Java 17+, do as follows:
+
+```
+mvn javafx:run -Dmain.class=com.gluonhq.richtextarea.samples.EmojiDemo
+```
+
+![rta_editor.png](../.github/assets/EmojiDemo.png)
+
+## EmojiPopupDemo
+
+The [EmojiPopupDemo](/samples/src/main/java/com/gluonhq/richtextarea/samples/EmojiPopupDemo.java) shows how to use the
+RichTextArea control to render text and emojis, including a popup control to select emojis interactively.
+
+### Usage
+
+To run this sample, using Java 17+, do as follows:
+
+```
+mvn javafx:run -Dmain.class=com.gluonhq.richtextarea.samples.EmojiPopupDemo
+```
+
+![rta_editor.png](../.github/assets/EmojiPopupDemo.png)
+
+## FullFeaturedDemo
+
+The [FullFeaturedDemo](/samples/src/main/java/com/gluonhq/richtextarea/samples/FullFeaturedDemo.java) shows a complete
+use case of the RichTextArea control as a rich text editor.
+
+This is an advance sample that shows how to create a rich text editor, by using the RichTextArea control and adding
+actions for the user interaction, via toolbars and menus, and most of the features of the control are showcased in this
+sample.
+
+### Usage
+
+To run this sample, using Java 17+, do as follows:
+
+```
+mvn javafx:run
+```
+
+![rta_editor.png](../.github/assets/rta_editor.png)
diff --git a/samples/pom.xml b/samples/pom.xml
index 1d9cc572..e428d9a3 100644
--- a/samples/pom.xml
+++ b/samples/pom.xml
@@ -14,7 +14,7 @@
11
20
0.0.8
- com.gluonhq.richtextarea.samples.RichTextEditorDemo
+ com.gluonhq.richtextarea.samples.FullFeaturedDemo
2022.1.0
1.1.0-SNAPSHOT
diff --git a/samples/src/main/java/com/gluonhq/richtextarea/samples/ActionsDemo.java b/samples/src/main/java/com/gluonhq/richtextarea/samples/ActionsDemo.java
new file mode 100644
index 00000000..1408f2f3
--- /dev/null
+++ b/samples/src/main/java/com/gluonhq/richtextarea/samples/ActionsDemo.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2023, Gluon
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL GLUON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.gluonhq.richtextarea.samples;
+
+import com.gluonhq.richtextarea.RichTextArea;
+import com.gluonhq.richtextarea.Selection;
+import com.gluonhq.richtextarea.action.TextDecorateAction;
+import com.gluonhq.richtextarea.model.DecorationModel;
+import com.gluonhq.richtextarea.model.Document;
+import com.gluonhq.richtextarea.model.ParagraphDecoration;
+import com.gluonhq.richtextarea.model.TextDecoration;
+import javafx.application.Application;
+import javafx.event.ActionEvent;
+import javafx.scene.Scene;
+import javafx.scene.control.ToggleButton;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.HBox;
+import javafx.stage.Stage;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static javafx.scene.text.FontPosture.ITALIC;
+import static javafx.scene.text.FontPosture.REGULAR;
+import static javafx.scene.text.FontWeight.BOLD;
+import static javafx.scene.text.FontWeight.NORMAL;
+
+/**
+ * This sample shows how to use the RichTextArea control to render some text and
+ * interact with it in a basic way via three toggle buttons.
+ *
+ * Run the sample and select some or all text, via mouse or keyboard, and then
+ * press the toggles to see how the decoration of the selection changes accordingly.
+ *
+ * Note that when you move the caret over the text, the toggles update their state
+ * (enabled means bold/italic/underline active), showing at any time the current
+ * decoration at the caret.
+ */
+public class ActionsDemo extends Application {
+
+ private static final String text =
+ "Document is the basic model that contains all the information required for the RichTextArea control, " +
+ "in order to render all the rich content, including decorated text, images and other non-text objects.\n" +
+ "A document is basically a string with the full text, and a list of DecorationModel that contain the text and paragraph decorations for one or more fragments of the text, " +
+ "where a fragment can be defined as the longest substring of the text that shares the same text and paragraph decorations.\n" +
+ "Any change to the document invalidates the undo/redo stack, forces the RichTextAreaSkin to recreate the PieceTable and sets it on the RichTextAreaViewModel.";
+
+ private static final TextDecoration preset =
+ TextDecoration.builder().presets().fontFamily("Arial").fontSize(14).build();
+ private static final ParagraphDecoration parPreset =
+ ParagraphDecoration.builder().presets().build();
+
+ @Override
+ public void start(Stage stage) {
+ List decorationList = getDecorations();
+ Document document = new Document(text, decorationList, text.length());
+
+ RichTextArea editor = new RichTextArea();
+ editor.setDocument(document);
+
+ BorderPane root = new BorderPane(editor);
+
+ // decorate actions
+ ToggleButton fontBoldToggle = new ToggleButton("Bold");
+ new TextDecorateAction<>(editor, fontBoldToggle.selectedProperty().asObject(),
+ d -> d.getFontWeight() == BOLD,
+ (builder, a) -> builder.fontWeight(a ? BOLD : NORMAL).build());
+ ToggleButton fontItalicToggle = new ToggleButton("Italic");
+ new TextDecorateAction<>(editor, fontItalicToggle.selectedProperty().asObject(),
+ d -> d.getFontPosture() == ITALIC,
+ (builder, a) -> builder.fontPosture(a ? ITALIC : REGULAR).build());
+ ToggleButton fontUnderlinedToggle = new ToggleButton("Underline");
+ new TextDecorateAction<>(editor, fontUnderlinedToggle.selectedProperty().asObject(),
+ TextDecoration::isUnderline, (builder, a) -> builder.underline(a).build());
+ HBox actionsBox = new HBox(fontBoldToggle, fontItalicToggle, fontUnderlinedToggle);
+ actionsBox.getStyleClass().add("actions-box");
+ root.setTop(actionsBox);
+
+ Scene scene = new Scene(root, 800, 300);
+ scene.getStylesheets().add(ActionsDemo.class.getResource("actionsDemo.css").toExternalForm());
+ stage.setScene(scene);
+ stage.setTitle("RTA: Actions");
+ stage.show();
+
+ // select some text and change its decoration;
+ editor.getActionFactory().selectAndDecorate(new Selection(12, 27),
+ TextDecoration.builder().presets().fontFamily("Arial")
+ .fontWeight(BOLD).underline(true)
+ .build()).execute(new ActionEvent());
+ }
+
+ private List getDecorations() {
+ List decorations = new ArrayList<>();
+ // decoration for text
+ decorations.add(new DecorationModel(0, text.length(), preset, parPreset));
+ return decorations;
+ }
+
+}
diff --git a/samples/src/main/java/com/gluonhq/richtextarea/samples/BasicDemo.java b/samples/src/main/java/com/gluonhq/richtextarea/samples/BasicDemo.java
new file mode 100644
index 00000000..466c2979
--- /dev/null
+++ b/samples/src/main/java/com/gluonhq/richtextarea/samples/BasicDemo.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2023, Gluon
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL GLUON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.gluonhq.richtextarea.samples;
+
+import com.gluonhq.richtextarea.RichTextArea;
+import com.gluonhq.richtextarea.model.DecorationModel;
+import com.gluonhq.richtextarea.model.Document;
+import com.gluonhq.richtextarea.model.ParagraphDecoration;
+import com.gluonhq.richtextarea.model.TextDecoration;
+import javafx.application.Application;
+import javafx.geometry.Insets;
+import javafx.scene.Scene;
+import javafx.scene.layout.BorderPane;
+import javafx.stage.Stage;
+
+import java.util.List;
+
+/**
+ * Basic sample with the RichTextArea control, showing a prompt message.
+ *
+ * While all the control features are available, but there are no menus or
+ * toolbars included, so user interaction is limited to shorcuts or context
+ * menu.
+ *
+ * For instance, after typing some text, select all (Ctrl/Cmd + A) or part of it
+ * (with mouse or keyboard) and press Ctrl/Cmd + I for italic or Ctrl/Cmd + B for bold.
+ *
+ * Undo/Redo, Cut/Copy/Paste options work as usual.
+ * You can copy text with emoji unicode, and paste it on the editor.
+ * For instance, while running this sample, copy this text:
+ *
+ * {@code Hello 👋🏼}
+ *
+ * and paste it, you should see the waving hand emoji and some text. Also copying from
+ * the control and pasting it on the control itself or on any other application will work
+ * too, keeping the rich content when possible.
+ *
+ * Right click to display a context menu with different options, like inserting a
+ * 2x1 table.
+ *
+ * To apply the rest of the control features, some UI is needed for user interaction. See
+ * the {@link FullFeaturedDemo} sample for a complete and advanced showcase.
+ */
+public class BasicDemo extends Application {
+
+ /**
+ * Defines the text and paragraph decorations, based on the default presets,
+ * but with Arial font
+ */
+ private static final List decorations = List.of(
+ new DecorationModel(0, 0,
+ TextDecoration.builder().presets().fontFamily("Arial").build(),
+ ParagraphDecoration.builder().presets().build()));
+
+ /**
+ * Creates an empty document with the new decorations
+ */
+ private static final Document emptyDocument =
+ new Document("", decorations, 0);
+
+ @Override
+ public void start(Stage stage) {
+ RichTextArea editor = new RichTextArea();
+ editor.setDocument(emptyDocument);
+ editor.setPromptText("Type something!");
+ editor.setPadding(new Insets(20));
+
+ BorderPane root = new BorderPane(editor);
+ Scene scene = new Scene(root, 800, 600);
+ stage.setScene(scene);
+ stage.setTitle("RichTextArea");
+ stage.show();
+ }
+
+}
diff --git a/samples/src/main/java/com/gluonhq/richtextarea/samples/EmojiDemo.java b/samples/src/main/java/com/gluonhq/richtextarea/samples/EmojiDemo.java
new file mode 100644
index 00000000..79012266
--- /dev/null
+++ b/samples/src/main/java/com/gluonhq/richtextarea/samples/EmojiDemo.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2023, Gluon
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL GLUON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.gluonhq.richtextarea.samples;
+
+import com.gluonhq.emoji.Emoji;
+import com.gluonhq.emoji.EmojiData;
+import com.gluonhq.emoji.EmojiSkinTone;
+import com.gluonhq.richtextarea.RichTextArea;
+import com.gluonhq.richtextarea.model.DecorationModel;
+import com.gluonhq.richtextarea.model.Document;
+import com.gluonhq.richtextarea.model.ParagraphDecoration;
+import com.gluonhq.richtextarea.model.TextDecoration;
+import javafx.application.Application;
+import javafx.event.ActionEvent;
+import javafx.scene.Scene;
+import javafx.scene.layout.BorderPane;
+import javafx.stage.Stage;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * This basic sample shows how to use the RichTextArea control to render text and
+ * emojis.
+ *
+ * This sample doesn't include a control to select interactively emojis (See
+ * {@link EmojiPopupDemo} for that).
+ */
+public class EmojiDemo extends Application {
+
+ private static final String text =
+ "Document with emojis \ud83d\ude03!\n" +
+ "\uD83D\uDC4B\uD83C\uDFFC, this is some random text with some emojis " +
+ "like \uD83E\uDDD1\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1\uD83C\uDFFD or " +
+ "\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74\uDB40\uDC7F.\n" +
+ "These are emojis with skin tone or hair style, like:\n";
+
+ private static final StringBuilder fullText = new StringBuilder(text);
+ private static final TextDecoration preset =
+ TextDecoration.builder().presets().fontFamily("Arial").fontSize(14).build();
+ private static final ParagraphDecoration parPreset =
+ ParagraphDecoration.builder().presets().build();
+
+ @Override
+ public void start(Stage stage) {
+ String personText = EmojiData.search("person").stream()
+ .limit(10)
+ .map(Emoji::character)
+ .collect(Collectors.joining(", "));
+ fullText.append(personText).append(".\nAnd this is another emoji with skin tone: ");
+
+ List decorationList = getDecorations();
+ Document document = new Document(fullText.toString(), decorationList, fullText.length());
+
+ RichTextArea editor = new RichTextArea();
+ editor.setDocument(document);
+
+ BorderPane root = new BorderPane(editor);
+ Scene scene = new Scene(root, 800, 300);
+ scene.getStylesheets().add(EmojiDemo.class.getResource("emojiDemo.css").toExternalForm());
+ stage.setScene(scene);
+ stage.setTitle("RTA: Text and emojis");
+ stage.show();
+
+ editor.setSkinTone(EmojiSkinTone.MEDIUM_SKIN_TONE);
+
+ // Use ActionFactory to insert emojis
+ EmojiData.emojiFromShortName("runner")
+ .ifPresent(emoji -> {
+ Emoji emojiWithTone = emoji.getSkinVariationMap().get(editor.getSkinTone().getUnicode());
+ editor.getActionFactory().insertEmoji(emojiWithTone).execute(new ActionEvent());
+ });
+ }
+
+ private List getDecorations() {
+ List decorations = new ArrayList<>();
+ // decoration for text
+ decorations.add(new DecorationModel(0, fullText.length(), preset, parPreset));
+ return decorations;
+ }
+
+}
diff --git a/samples/src/main/java/com/gluonhq/richtextarea/samples/EmojiPopupDemo.java b/samples/src/main/java/com/gluonhq/richtextarea/samples/EmojiPopupDemo.java
new file mode 100644
index 00000000..fbcaa981
--- /dev/null
+++ b/samples/src/main/java/com/gluonhq/richtextarea/samples/EmojiPopupDemo.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2023, Gluon
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL GLUON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.gluonhq.richtextarea.samples;
+
+import com.gluonhq.emoji.Emoji;
+import com.gluonhq.emoji.EmojiSkinTone;
+import com.gluonhq.richtextarea.RichTextArea;
+import com.gluonhq.richtextarea.model.DecorationModel;
+import com.gluonhq.richtextarea.model.Document;
+import com.gluonhq.richtextarea.model.ParagraphDecoration;
+import com.gluonhq.richtextarea.model.TextDecoration;
+import com.gluonhq.richtextarea.samples.popup.EmojiPopup;
+import javafx.application.Application;
+import javafx.event.ActionEvent;
+import javafx.geometry.Insets;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Priority;
+import javafx.scene.layout.Region;
+import javafx.stage.Stage;
+
+import java.util.List;
+
+/**
+ * This basic sample shows how to use the RichTextArea control to render text and
+ * emojis, including a popup control to select interactively emojis.
+ */
+public class EmojiPopupDemo extends Application {
+
+ /**
+ * Defines the text and paragraph decorations, based on the default presets,
+ * but with Arial font
+ */
+ private static final List decorations = List.of(
+ new DecorationModel(0, 0,
+ TextDecoration.builder().presets().fontFamily("Arial").build(),
+ ParagraphDecoration.builder().presets().build()));
+
+ /**
+ * Creates an empty document with the new decorations
+ */
+ private static final Document emptyDocument =
+ new Document("", decorations, 0);
+
+ @Override
+ public void start(Stage stage) {
+ RichTextArea editor = new RichTextArea();
+ editor.setDocument(emptyDocument);
+ editor.setPromptText("Type something or insert emojis!");
+ editor.setSkinTone(EmojiSkinTone.MEDIUM_SKIN_TONE);
+ editor.setPadding(new Insets(20));
+
+ Region region = new Region();
+ region.getStyleClass().addAll("icon", "emoji-outline");
+ Button emojiButton = new Button(null, region);
+ emojiButton.getStyleClass().add("emoji-button");
+ emojiButton.setOnAction(e -> {
+ EmojiPopup emojiPopup = new EmojiPopup();
+ emojiPopup.setSkinTone(editor.getSkinTone());
+ editor.skinToneProperty().bindBidirectional(emojiPopup.skinToneProperty());
+ emojiPopup.setOnAction(ev -> {
+ Emoji emoji = (Emoji) ev.getSource();
+ editor.getActionFactory().insertEmoji(emoji).execute(new ActionEvent());
+ });
+ emojiPopup.show(emojiButton);
+ });
+
+ HBox root = new HBox(20, editor, emojiButton);
+ HBox.setHgrow(editor, Priority.ALWAYS);
+ root.getStyleClass().add("root-box");
+
+ Scene scene = new Scene(root, 600, 300);
+ scene.getStylesheets().add(EmojiPopupDemo.class.getResource("emojiPopupDemo.css").toExternalForm());
+ stage.setScene(scene);
+ stage.setTitle("RTA: Text and emoji popup");
+ stage.show();
+
+ editor.requestFocus();
+ }
+
+}
diff --git a/samples/src/main/java/com/gluonhq/richtextarea/samples/RichTextEditorDemo.java b/samples/src/main/java/com/gluonhq/richtextarea/samples/FullFeaturedDemo.java
similarity index 97%
rename from samples/src/main/java/com/gluonhq/richtextarea/samples/RichTextEditorDemo.java
rename to samples/src/main/java/com/gluonhq/richtextarea/samples/FullFeaturedDemo.java
index 3673069c..3ced74d0 100644
--- a/samples/src/main/java/com/gluonhq/richtextarea/samples/RichTextEditorDemo.java
+++ b/samples/src/main/java/com/gluonhq/richtextarea/samples/FullFeaturedDemo.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, Gluon
+ * Copyright (c) 2022, 2023, Gluon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -105,15 +105,22 @@
import static javafx.scene.text.FontWeight.BOLD;
import static javafx.scene.text.FontWeight.NORMAL;
-public class RichTextEditorDemo extends Application {
+/**
+ * This is an advance sample that shows how to create a rich text editor, by using
+ * the RichTextArea control and adding actions for the user interaction, via toolbars and
+ * menus, and most of the features of the control are showcased in this sample.
+ *
+ * For more basic test cases with single features, check the rest of the samples.
+ */
+public class FullFeaturedDemo extends Application {
static {
- try (InputStream resourceAsStream = RichTextEditorDemo.class.getResourceAsStream("/logging.properties")) {
+ try (InputStream resourceAsStream = FullFeaturedDemo.class.getResourceAsStream("/logging.properties")) {
if (resourceAsStream != null) {
LogManager.getLogManager().readConfiguration(resourceAsStream);
}
} catch (IOException ex) {
- Logger.getLogger(RichTextEditorDemo.class.getName()).log(Level.SEVERE, "Error opening logging.properties file", ex);
+ Logger.getLogger(FullFeaturedDemo.class.getName()).log(Level.SEVERE, "Error opening logging.properties file", ex);
}
}
@@ -311,7 +318,7 @@ public Double fromString(String s) {
root.setBottom(statusBar);
Scene scene = new Scene(root, 960, 580);
- scene.getStylesheets().add(RichTextEditorDemo.class.getResource("richtexteditordemo.css").toExternalForm());
+ scene.getStylesheets().add(FullFeaturedDemo.class.getResource("fullFeaturedDemo.css").toExternalForm());
stage.titleProperty().bind(Bindings.createStringBinding(() -> "Rich Text Editor Demo" + (editor.isModified() ? " *" : ""), editor.modifiedProperty()));
stage.setScene(scene);
stage.show();
diff --git a/samples/src/main/java/com/gluonhq/richtextarea/samples/HighlightDemo.java b/samples/src/main/java/com/gluonhq/richtextarea/samples/HighlightDemo.java
new file mode 100644
index 00000000..9d84eda8
--- /dev/null
+++ b/samples/src/main/java/com/gluonhq/richtextarea/samples/HighlightDemo.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2023, Gluon
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL GLUON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.gluonhq.richtextarea.samples;
+
+import com.gluonhq.richtextarea.RichTextArea;
+import com.gluonhq.richtextarea.model.DecorationModel;
+import com.gluonhq.richtextarea.model.Document;
+import com.gluonhq.richtextarea.model.ParagraphDecoration;
+import com.gluonhq.richtextarea.model.TextDecoration;
+import javafx.application.Application;
+import javafx.scene.Scene;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.paint.Color;
+import javafx.stage.Stage;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static javafx.scene.text.FontWeight.BOLD;
+import static javafx.scene.text.FontPosture.ITALIC;
+
+/**
+ * This sample shows how to use the RichTextArea control to add a document
+ * with some decorations, that are generated by searching some keywords over
+ * a given text.
+ */
+public class HighlightDemo extends Application {
+
+ private static final String title = "The Document\n";
+ private static final String text =
+ "Document is the basic model that contains all the information required for the RichTextArea control, " +
+ "in order to render all the rich content, including decorated text, images and other non-text objects.\n" +
+ "A document is basically a string with the full text, and a list of DecorationModel that contain the text and paragraph decorations for one or more fragments of the text, " +
+ "where a fragment can be defined as the longest substring of the text that shares the same text and paragraph decorations.\n" +
+ "Any change to the document invalidates the undo/redo stack, forces the RichTextAreaSkin to recreate the PieceTable and sets it on the RichTextAreaViewModel.";
+ private static final String fullText = title + text;
+
+ private static final List keywords = List.of(
+ "Document", "RichTextArea", "DecorationModel", "RichTextAreaSkin",
+ "PieceTable", "RichTextAreaViewModel"
+ );
+ private static final TextDecoration preset =
+ TextDecoration.builder().presets().fontFamily("Arial").build();
+ private static final TextDecoration bold16 =
+ TextDecoration.builder().presets().fontFamily("Arial").fontWeight(BOLD).fontSize(16).build();
+ private static final TextDecoration mono =
+ TextDecoration.builder().presets().fontFamily("Monospaced").fontWeight(BOLD)
+ .fontPosture(ITALIC).background(Color.CORNFLOWERBLUE).build();
+ private static final ParagraphDecoration parPreset =
+ ParagraphDecoration.builder().presets().build();
+
+ @Override
+ public void start(Stage stage) {
+ List decorationList = getDecorations();
+ Document document = new Document(fullText, decorationList, fullText.length());
+
+ RichTextArea editor = new RichTextArea();
+ editor.setDocument(document);
+
+ BorderPane root = new BorderPane(editor);
+ Scene scene = new Scene(root, 800, 300);
+ scene.getStylesheets().add(HighlightDemo.class.getResource("highlightDemo.css").toExternalForm());
+ stage.setScene(scene);
+ stage.setTitle("RTA: Text and highlighted keywords");
+ stage.show();
+ }
+
+ private List getDecorations() {
+ List decorations = new ArrayList<>();
+ // decoration for title
+ decorations.add(new DecorationModel(0, title.length(), bold16, parPreset));
+
+ // search keywords in text
+ AtomicInteger counter = new AtomicInteger(title.length());
+ keywords.forEach(key -> {
+ int i = fullText.substring(counter.get()).indexOf(key);
+ if (i > 0) {
+ // decoration for regular text
+ decorations.add(new DecorationModel(counter.getAndAdd(i), i, preset, parPreset));
+ }
+ // decoration for keyword
+ decorations.add(new DecorationModel(counter.getAndAdd(key.length()), key.length(), mono, parPreset));
+ });
+ // decoration for regular text
+ decorations.add(new DecorationModel(counter.get(), fullText.length() - counter.get(), preset, parPreset));
+ return decorations;
+ }
+
+}
diff --git a/samples/src/main/java/com/gluonhq/richtextarea/samples/ListsDemo.java b/samples/src/main/java/com/gluonhq/richtextarea/samples/ListsDemo.java
new file mode 100644
index 00000000..9d10dc12
--- /dev/null
+++ b/samples/src/main/java/com/gluonhq/richtextarea/samples/ListsDemo.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2023, Gluon
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL GLUON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.gluonhq.richtextarea.samples;
+
+import com.gluonhq.richtextarea.RichTextArea;
+import com.gluonhq.richtextarea.model.DecorationModel;
+import com.gluonhq.richtextarea.model.Document;
+import com.gluonhq.richtextarea.model.ParagraphDecoration;
+import com.gluonhq.richtextarea.model.TextDecoration;
+import javafx.application.Application;
+import javafx.scene.Scene;
+import javafx.scene.control.Label;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.shape.Rectangle;
+import javafx.scene.text.FontWeight;
+import javafx.stage.Stage;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Sample with a list generated with custom numbered and bulleted decorations
+ * created via {@link RichTextArea#paragraphGraphicFactoryProperty()}.
+ */
+public class ListsDemo extends Application {
+
+ private static final String title = "This is a list\n";
+ private static final StringBuilder fullText = new StringBuilder(title);
+
+ private static final TextDecoration presetSection =
+ TextDecoration.builder().presets().fontFamily("Arial")
+ .fontWeight(FontWeight.BOLD).underline(true).build();
+
+ private static final TextDecoration preset =
+ TextDecoration.builder().presets().fontFamily("Arial").build();
+
+ private static final ParagraphDecoration parPreset =
+ ParagraphDecoration.builder().presets().build();
+
+
+ @Override
+ public void start(Stage stage) {
+ List decorationList = getDecorations();
+ Document document = new Document(fullText.toString(), decorationList, fullText.toString().length());
+
+ RichTextArea editor = new RichTextArea();
+ editor.setParagraphGraphicFactory((i, t) -> {
+ if (i < 1) {
+ return null;
+ } else if (i == 1) {
+ return new Rectangle(5, 5);
+ }
+ Label label = new Label("#.-");
+ label.setMouseTransparent(true);
+ label.getStyleClass().add("numbered-list-label");
+ return label;
+ });
+ editor.setDocument(document);
+
+ BorderPane root = new BorderPane(editor);
+ Scene scene = new Scene(root, 600, 500);
+ scene.getStylesheets().add(HighlightDemo.class.getResource("listsDemo.css").toExternalForm());
+ stage.setScene(scene);
+ stage.setTitle("RTA: Numbered and bulleted list");
+ stage.show();
+ }
+
+ private List getDecorations() {
+ List decorations = new ArrayList<>();
+
+ int counter = fullText.length();
+ decorations.add(new DecorationModel(0, counter, preset, parPreset));
+
+ for (int i = 0; i < 4; i++) {
+ String text = "Section " + (i + 1) + "\n";
+ fullText.append(text);
+ decorations.add(new DecorationModel(counter, text.length(), presetSection,
+ ParagraphDecoration.builder().presets()
+ .graphicType(ParagraphDecoration.GraphicType.BULLETED_LIST)
+ .indentationLevel(1)
+ .build()));
+ counter += text.length();
+ for (int j = 0; j < 3; j++) {
+ String subtext = "Item " + (i + 1) + "." + (j + 1) + "\n";
+ fullText.append(subtext);
+ decorations.add(new DecorationModel(counter, subtext.length(), preset,
+ ParagraphDecoration.builder().presets()
+ .graphicType(ParagraphDecoration.GraphicType.NUMBERED_LIST)
+ .indentationLevel(2)
+ .build()));
+ counter += subtext.length();
+ }
+ }
+ fullText.append("End of list\n");
+ decorations.add(new DecorationModel(counter, 4, preset, parPreset));
+ return decorations;
+ }
+}
diff --git a/samples/src/main/resources/com/gluonhq/richtextarea/samples/actionsDemo.css b/samples/src/main/resources/com/gluonhq/richtextarea/samples/actionsDemo.css
new file mode 100644
index 00000000..a16a6d4c
--- /dev/null
+++ b/samples/src/main/resources/com/gluonhq/richtextarea/samples/actionsDemo.css
@@ -0,0 +1,22 @@
+.rich-text-area {
+ -fx-background-color: whitesmoke;
+ -fx-padding: 20;
+}
+
+.rich-text-area > .paragraph-list-view {
+ -fx-padding: 10;
+ -fx-background-color: lightgray;
+ -fx-border-color: darkgray;
+}
+
+.rich-text-area > .paragraph-list-view > .virtual-flow > .clipped-container > .sheet > .list-cell,
+.rich-text-area > .paragraph-list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:odd {
+ -fx-background: transparent;
+ -fx-background-color: transparent;
+}
+
+.actions-box {
+ -fx-alignment: center;
+ -fx-spacing: 20;
+ -fx-padding: 10 0 0 0;
+}
\ No newline at end of file
diff --git a/samples/src/main/resources/com/gluonhq/richtextarea/samples/emojiDemo.css b/samples/src/main/resources/com/gluonhq/richtextarea/samples/emojiDemo.css
new file mode 100644
index 00000000..a5aad8a5
--- /dev/null
+++ b/samples/src/main/resources/com/gluonhq/richtextarea/samples/emojiDemo.css
@@ -0,0 +1,16 @@
+.rich-text-area {
+ -fx-background-color: whitesmoke;
+ -fx-padding: 20;
+}
+
+.rich-text-area > .paragraph-list-view {
+ -fx-padding: 10;
+ -fx-background-color: lightgreen;
+ -fx-border-color: darkgreen;
+}
+
+.rich-text-area > .paragraph-list-view > .virtual-flow > .clipped-container > .sheet > .list-cell,
+.rich-text-area > .paragraph-list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:odd {
+ -fx-background: transparent;
+ -fx-background-color: transparent;
+}
diff --git a/samples/src/main/resources/com/gluonhq/richtextarea/samples/emojiPopupDemo.css b/samples/src/main/resources/com/gluonhq/richtextarea/samples/emojiPopupDemo.css
new file mode 100644
index 00000000..cd7c179f
--- /dev/null
+++ b/samples/src/main/resources/com/gluonhq/richtextarea/samples/emojiPopupDemo.css
@@ -0,0 +1,36 @@
+.root-box {
+ -fx-alignment: bottom-right;
+ -fx-padding: 20;
+}
+
+.root-box > .rich-text-area {
+ -fx-background-color: whitesmoke;
+ -fx-padding: 0;
+}
+
+.root-box > .rich-text-area > .paragraph-list-view {
+ -fx-padding: 10;
+ -fx-background-color: lightgreen;
+ -fx-border-color: darkgreen;
+}
+
+.root-box > .rich-text-area > .paragraph-list-view > .virtual-flow > .clipped-container > .sheet > .list-cell,
+.root-box > .rich-text-area > .paragraph-list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:odd {
+ -fx-background: transparent;
+ -fx-background-color: transparent;
+}
+
+.root-box > .emoji-button {
+ -fx-padding: 5.5;
+}
+
+.root-box > .emoji-button > .icon.emoji-outline {
+ -fx-shape: "m12 18.663a6.817 6.817 0 0 1 -5.491-2.7.75.75 0 1 1 1.207-.89 5.31 5.31 0 0 0 4.284 2.09 5.3 5.3 0 0 0 4.3-2.105.75.75 0 1 1 1.211.884 6.8 6.8 0 0 1 -5.511 2.721zm0-16.163a9.188 9.188 0 0 0 -9.5 9.5 9.188 9.188 0 0 0 9.5 9.5 9.188 9.188 0 0 0 9.5-9.5 9.188 9.188 0 0 0 -9.5-9.5m0-1.5a10.7 10.7 0 0 1 11 11 10.7 10.7 0 0 1 -11 11 10.7 10.7 0 0 1 -11-11 10.7 10.7 0 0 1 11-11zm-3.5 7c-.828 0-1.5.9-1.5 2s.672 2 1.5 2 1.5-.895 1.5-2-.672-2-1.5-2zm7 0c-.828 0-1.5.9-1.5 2s.672 2 1.5 2 1.5-.895 1.5-2-.672-2-1.5-2z";
+ -fx-background-color: black;
+ -fx-min-width: 18px;
+ -fx-pref-width: 18px;
+ -fx-max-width: 18px;
+ -fx-min-height: 18px;
+ -fx-pref-height: 18px;
+ -fx-max-height: 18px;
+}
\ No newline at end of file
diff --git a/samples/src/main/resources/com/gluonhq/richtextarea/samples/richtexteditordemo.css b/samples/src/main/resources/com/gluonhq/richtextarea/samples/fullFeaturedDemo.css
similarity index 100%
rename from samples/src/main/resources/com/gluonhq/richtextarea/samples/richtexteditordemo.css
rename to samples/src/main/resources/com/gluonhq/richtextarea/samples/fullFeaturedDemo.css
diff --git a/samples/src/main/resources/com/gluonhq/richtextarea/samples/highlightDemo.css b/samples/src/main/resources/com/gluonhq/richtextarea/samples/highlightDemo.css
new file mode 100644
index 00000000..9c2d6bde
--- /dev/null
+++ b/samples/src/main/resources/com/gluonhq/richtextarea/samples/highlightDemo.css
@@ -0,0 +1,16 @@
+.rich-text-area {
+ -fx-background-color: whitesmoke;
+ -fx-padding: 20;
+}
+
+.rich-text-area > .paragraph-list-view {
+ -fx-padding: 10;
+ -fx-background-color: lightblue;
+ -fx-border-color: darkgray;
+}
+
+.rich-text-area > .paragraph-list-view > .virtual-flow > .clipped-container > .sheet > .list-cell,
+.rich-text-area > .paragraph-list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:odd {
+ -fx-background: transparent;
+ -fx-background-color: transparent;
+}
diff --git a/samples/src/main/resources/com/gluonhq/richtextarea/samples/listsDemo.css b/samples/src/main/resources/com/gluonhq/richtextarea/samples/listsDemo.css
new file mode 100644
index 00000000..cfffd124
--- /dev/null
+++ b/samples/src/main/resources/com/gluonhq/richtextarea/samples/listsDemo.css
@@ -0,0 +1,22 @@
+.rich-text-area {
+ -fx-background-color: whitesmoke;
+ -fx-padding: 20;
+}
+
+.rich-text-area > .paragraph-list-view {
+ -fx-padding: 10;
+ -fx-background-color: lightyellow;
+ -fx-border-color: darkred;
+}
+
+.rich-text-area > .paragraph-list-view > .virtual-flow > .clipped-container > .sheet > .list-cell > .paragraph-tile > .graphic-box {
+ -fx-padding: 0 4 0 0;
+}
+
+.rich-text-area > .paragraph-list-view > .virtual-flow > .clipped-container > .sheet > .list-cell,
+.rich-text-area > .paragraph-list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:odd,
+.rich-text-area > .paragraph-list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:odd,
+.rich-text-area > .paragraph-list-view:focused > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected {
+ -fx-background: transparent;
+ -fx-background-color: transparent;
+}
\ No newline at end of file