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