diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/BackgroundPath.java b/richtextfx/src/main/java/org/fxmisc/richtext/BackgroundPath.java index 32f860a1f..1b0e637b8 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/BackgroundPath.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/BackgroundPath.java @@ -4,7 +4,6 @@ /** * A path which describes a background shape in the Scene graph. - * */ public class BackgroundPath extends Path { } diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/BorderPath.java b/richtextfx/src/main/java/org/fxmisc/richtext/BorderPath.java index 417278398..55a3d0157 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/BorderPath.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/BorderPath.java @@ -4,7 +4,6 @@ /** * A path which describes a border in the Scene graph. - * */ public class BorderPath extends Path { } diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/Caret.java b/richtextfx/src/main/java/org/fxmisc/richtext/Caret.java index cc51456c7..57a5f9948 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/Caret.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/Caret.java @@ -34,6 +34,11 @@ */ public interface Caret { + /** + * Determines whether the caret is visible. Those who wish to use the default configuartion should use + * {@link #AUTO} while those who want a more custom configuration should make a caret's {@code CaretVisibility} + * value oscilate between {@link #ON} and {@link #OFF}. + */ public static enum CaretVisibility { /** Caret is displayed. */ ON, diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/CaretPath.java b/richtextfx/src/main/java/org/fxmisc/richtext/CaretPath.java index 5c5141b49..80b40324b 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/CaretPath.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/CaretPath.java @@ -4,7 +4,6 @@ /** * A path which describes a caret shape in the Scene graph. - * */ public class CaretPath extends Path { } diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/CharacterHit.java b/richtextfx/src/main/java/org/fxmisc/richtext/CharacterHit.java index 50a547da8..a9734c3bb 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/CharacterHit.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/CharacterHit.java @@ -2,16 +2,32 @@ import java.util.OptionalInt; +/** + * Object that stores information relating to the position in an area's content that corresponds to a given position + * in some visible entity (e.g. the area, a paragraph in the area, a line on a paragraph). + */ public class CharacterHit { + /** + * Returns a {@link CharacterHit} for cases where the insertion occurs outside the bounds of some visible entity + * (e.g. the area, the paragraph in an area, the line in a paragraph) + */ public static CharacterHit insertionAt(int insertionIndex) { return new CharacterHit(OptionalInt.empty(), insertionIndex); } + /** + * Returns a {@link CharacterHit} for cases where the hit occurs inside the bounds of some visible entity + * (e.g. the area, the paragraph in an area, the line in a paragraph) and the character is leading. + */ public static CharacterHit leadingHalfOf(int charIdx) { return new CharacterHit(OptionalInt.of(charIdx), charIdx); } + /** + * Returns a {@link CharacterHit} for cases where the hit occurs inside the bounds of some visible entity + * (e.g. the area, the paragraph in an area, the line in a paragraph) and the character is trailing. + */ public static CharacterHit trailingHalfOf(int charIdx) { return new CharacterHit(OptionalInt.of(charIdx), charIdx + 1); } @@ -25,19 +41,32 @@ private CharacterHit(OptionalInt charIdx, int insertionIndex) { this.insertionIndex = insertionIndex; } + /** + * Returns an {@link OptionalInt} whose value is present when the hit occurs within the visible + * entity and is the index of the closest character to the give position on the screen. + */ public OptionalInt getCharacterIndex() { return charIdx; } + /** + * When {@link #getCharacterIndex()} is present, returns either the same value as {@link #getCharacterIndex()} when + * the character index is leading and {@code getCharacterIndex() + 1} when the index is trailing. + * When {@link #getCharacterIndex()} is absent, returns the bounds of that visible entity (either {@code 0} or + * the length of the area, the length of a paragraph, or the character count of a line). + */ public int getInsertionIndex() { return insertionIndex; } - public CharacterHit offset(int offset) { + /** + * Offsets the values stored in this {@link CharacterHit} by the given amount + */ + public CharacterHit offset(int offsetAmount) { return new CharacterHit( charIdx.isPresent() - ? OptionalInt.of(charIdx.getAsInt() + offset) + ? OptionalInt.of(charIdx.getAsInt() + offsetAmount) : charIdx, - insertionIndex + offset); + insertionIndex + offsetAmount); } } diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/CodeArea.java b/richtextfx/src/main/java/org/fxmisc/richtext/CodeArea.java index c2ea4a45e..04c107562 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/CodeArea.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/CodeArea.java @@ -7,9 +7,8 @@ import org.fxmisc.richtext.model.EditableStyledDocument; /** - * A convenience subclass of {@link StyleClassedTextArea} - * with fixed-width font and an undo manager that observes - * only plain text changes (not styled changes). + * A convenience subclass of {@link StyleClassedTextArea} with fixed-width font and an undo manager that observes + * only plain text changes (not styled changes). It's style class is {@code code-area}. */ public class CodeArea extends StyleClassedTextArea { @@ -23,10 +22,16 @@ public class CodeArea extends StyleClassedTextArea { setUseInitialStyleForInsertion(true); } + /** + * Creates an area that can render and edit the same {@link EditableStyledDocument} as another {@link CodeArea}. + */ public CodeArea(@NamedArg("document") EditableStyledDocument, String, Collection> document) { super(document, false); } + /** + * Creates an area with no text. + */ public CodeArea() { super(false); } diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/CustomCssMetaData.java b/richtextfx/src/main/java/org/fxmisc/richtext/CustomCssMetaData.java index e4f49c803..d2531a88a 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/CustomCssMetaData.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/CustomCssMetaData.java @@ -8,6 +8,9 @@ import java.util.function.Function; +/** + * Reduces boilerplate when creating a custom {@link CssMetaData} object + */ public class CustomCssMetaData extends CssMetaData { private final Function> property; diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/CustomStyleableProperty.java b/richtextfx/src/main/java/org/fxmisc/richtext/CustomStyleableProperty.java index e63e5a637..d9ee32c52 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/CustomStyleableProperty.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/CustomStyleableProperty.java @@ -4,6 +4,9 @@ import javafx.css.Styleable; import javafx.css.StyleableObjectProperty; +/** + * Reduces the boilerplate when creating a custom CSS property (i.e. {@link javafx.css.StyleableProperty}). + */ public class CustomStyleableProperty extends StyleableObjectProperty { private final Object bean; diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java b/richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java index ccc7de8ce..6c0af3a88 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java @@ -90,38 +90,53 @@ import org.reactfx.value.Var; /** - * Text editing control. Accepts user input (keyboard, mouse) and - * provides API to assign style to text ranges. It is suitable for + * Text editing control that renders and edits a {@link EditableStyledDocument}. + * + * Accepts user input (keyboard, mouse) and provides API to assign style to text ranges. It is suitable for * syntax highlighting and rich-text editors. * *

Adding Scrollbars to the Area

* - *

The scroll bars no longer appear when the content spans outside - * of the viewport. To add scroll bars, the area needs to be wrapped in - * a {@link VirtualizedScrollPane}. For example,

- *
- * {@code
+ * 

By default, scroll bars do not appear when the content spans outside of the viewport. + * To add scroll bars, the area needs to be wrapped in a {@link VirtualizedScrollPane}. For example,

+ *

  * // shows area without scroll bars
  * InlineCssTextArea area = new InlineCssTextArea();
  *
  * // add scroll bars that will display as needed
- * VirtualizedScrollPane vsPane = new VirtualizedScrollPane(area);
+ * VirtualizedScrollPane<InlineCssTextArea> vsPane = new VirtualizedScrollPane<>(area);
  *
- * Parent parent = //;
+ * Parent parent = // creation code
  * parent.getChildren().add(vsPane)
- * }
- * 
+ *
* *

Auto-Scrolling to the Caret

* *

Every time the underlying {@link EditableStyledDocument} changes via user interaction (e.g. typing) through - * the {@code StyledTextArea}, the area will scroll to insure the caret is kept in view. However, this does not + * the {@code GenericStyledArea}, the area will scroll to insure the caret is kept in view. However, this does not * occur if changes are done programmatically. For example, let's say the area is displaying the bottom part * of the area's {@link EditableStyledDocument} and some code changes something in the top part of the document * that is not currently visible. If there is no call to {@link #requestFollowCaret()} at the end of that code, * the area will not auto-scroll to that section of the document. The change will occur, and the user will continue * to see the bottom part of the document as before. If such a call is there, then the area will scroll * to the top of the document and no longer display the bottom part of it.

+ *

For example...

+ *

+ * // assuming the user is currently seeing the top of the area
+ *
+ * // then changing the bottom, currently not visible part of the area...
+ * int startParIdx = 40;
+ * int startColPosition = 2;
+ * int endParIdx = 42;
+ * int endColPosition = 10;
+ *
+ * // ...by itself will not scroll the viewport to where the change occurs
+ * area.replaceText(startParIdx, startColPosition, endParIdx, endColPosition, "replacement text");
+ *
+ * // adding this line after the last modification to the area will cause the viewport to scroll to that change
+ * // leaving the following line out will leave the viewport unaffected and the user will not notice any difference
+ * area.requestFollowCaret();
+ * 
* *

Additionally, when overriding the default user-interaction behavior, remember to include a call * to {@link #requestFollowCaret()}.

@@ -136,15 +151,16 @@ * *

Overriding default keyboard behavior

* - * {@code StyledTextArea} uses {@code KEY_TYPED} handler to handle ordinary - * character input and {@code KEY_PRESSED} handler to handle control key + * {@code GenericStyledArea} uses {@link javafx.scene.input.KeyEvent#KEY_TYPED KEY_TYPED} to handle ordinary + * character input and {@link javafx.scene.input.KeyEvent#KEY_PRESSED KEY_PRESSED} to handle control key * combinations (including Enter and Tab). To add or override some keyboard * shortcuts, while keeping the rest in place, you would combine the default * event handler with a new one that adds or overrides some of the default - * key combinations. This is how to bind {@code Ctrl+S} to the {@code save()} - * operation: - *
- * {@code
+ * key combinations.
+ * 

+ * For example, this is how to bind {@code Ctrl+S} to the {@code save()} operation: + *

+ *

  * import static javafx.scene.input.KeyCode.*;
  * import static javafx.scene.input.KeyCombination.*;
  * import static org.fxmisc.wellbehaved.event.EventPattern.*;
@@ -152,18 +168,19 @@
  *
  * import org.fxmisc.wellbehaved.event.Nodes;
  *
- * Nodes.addInputMap(area, consume(keyPressed(S, CONTROL_DOWN), event -> save()));
- * }
- * 
+ * // installs the following consume InputMap, + * // so that a CTRL+S event saves the document and consumes the event + * Nodes.addInputMap(area, consume(keyPressed(S, CONTROL_DOWN), event -> save())); + *
* *

Overriding default mouse behavior

* * The area's default mouse behavior properly handles auto-scrolling and dragging the selected text to a new location. * As such, some parts cannot be partially overridden without it affecting other behavior. * - *

The following lists either {@link org.fxmisc.wellbehaved.event.EventPattern}s that cannot be overridden without - * negatively affecting the default mouse behavior or describe how to safely override things in a special way without - * disrupting the auto scroll behavior.

+ *

The following lists either {@link org.fxmisc.wellbehaved.event.EventPattern}s that cannot be + * overridden without negatively affecting the default mouse behavior or describe how to safely override things + * in a special way without disrupting the auto scroll behavior.

*
    *
  • * First (1 click count) Primary Button Mouse Pressed Events: @@ -227,6 +244,36 @@ *
  • *
* + *

CSS, Style Classes, and Pseudo Classes

+ *

+ * Refer to the + * RichTextFX CSS Reference Guide + * . + *

+ * + *

Area Actions and Other Operations

+ *

+ * To distinguish the actual operations one can do on this area from the boilerplate methods + * within this area (e.g. properties and their getters/setters, etc.), look at the interfaces + * this area implements. Each lists and documents methods that fall under that category. + *

+ * + *

Calculating a Position Within the Area

+ *

+ * To calculate a position or index within the area, read through the javadoc of + * {@link org.fxmisc.richtext.model.TwoDimensional} and {@link org.fxmisc.richtext.model.TwoDimensional.Bias}. + * Also, read the difference between "position" and "index" in + * {@link org.fxmisc.richtext.model.StyledDocument#getAbsolutePosition(int, int)}. + *

+ * + * @see EditableStyledDocument + * @see TwoDimensional + * @see org.fxmisc.richtext.model.TwoDimensional.Bias + * @see VirtualFlow + * @see VirtualizedScrollPane + * @see Caret + * @see Selection + * @see CaretSelectionBind * * @param type of style that can be applied to paragraphs (e.g. {@link TextFlow}. * @param type of segment used in {@link Paragraph}. Can be only text (plain or styled) or @@ -586,6 +633,20 @@ public GenericStyledArea(@NamedArg("initialParagraphStyle") PS initialParagraphS this(initialParagraphStyle, applyParagraphStyle, initialTextStyle, segmentOps, true, nodeFactory); } + /** + * Same as {@link #GenericStyledArea(Object, BiConsumer, Object, TextOps, Function)} but also allows one + * to specify whether the undo manager should be a plain or rich undo manager via {@code preserveStyle}. + * + * @param initialParagraphStyle style to use in places where no other style is specified (yet). + * @param applyParagraphStyle function that, given a {@link TextFlow} node and + * a style, applies the style to the paragraph node. This function is + * used by the default skin to apply style to paragraph nodes. + * @param initialTextStyle style to use in places where no other style is specified (yet). + * @param segmentOps The operations which are defined on the text segment objects. + * @param preserveStyle whether to use an undo manager that can undo/redo {@link RichTextChange}s or + * {@link PlainTextChange}s + * @param nodeFactory A function which is used to create the JavaFX scene node for a particular segment. + */ public GenericStyledArea(@NamedArg("initialParagraphStyle") PS initialParagraphStyle, @NamedArg("applyParagraphStyle") BiConsumer applyParagraphStyle, @NamedArg("initialTextStyle") S initialTextStyle, @@ -598,8 +659,8 @@ public GenericStyledArea(@NamedArg("initialParagraphStyle") PS initialParagraphS /** * The same as {@link #GenericStyledArea(Object, BiConsumer, Object, TextOps, Function)} except that - * this constructor can be used to create another {@code GenericStyledArea} object that - * shares the same {@link EditableStyledDocument}. + * this constructor can be used to create another {@code GenericStyledArea} that renders and edits the same + * {@link EditableStyledDocument} or when one wants to use a custom {@link EditableStyledDocument} implementation. */ public GenericStyledArea( @NamedArg("initialParagraphStyle") PS initialParagraphStyle, @@ -612,6 +673,20 @@ public GenericStyledArea( } + /** + * Creates an area with flexibility in all of its options. + * + * @param initialParagraphStyle style to use in places where no other style is specified (yet). + * @param applyParagraphStyle function that, given a {@link TextFlow} node and + * a style, applies the style to the paragraph node. This function is + * used by the default skin to apply style to paragraph nodes. + * @param initialTextStyle style to use in places where no other style is specified (yet). + * @param document the document to render and edit + * @param segmentOps The operations which are defined on the text segment objects. + * @param preserveStyle whether to use an undo manager that can undo/redo {@link RichTextChange}s or + * {@link PlainTextChange}s + * @param nodeFactory A function which is used to create the JavaFX scene node for a particular segment. + */ public GenericStyledArea( @NamedArg("initialParagraphStyle") PS initialParagraphStyle, @NamedArg("applyParagraphStyle") BiConsumer applyParagraphStyle, @@ -835,9 +910,13 @@ TwoDimensional.Position currentLine() { return _position(parIdx, lineIdx); } - public final int lineIndex(int paragraphIndex, int column) { + /** + * Returns 0 if the given paragraph displays its content across only one line, or returns the index + * of the line on which the given column position appears if the paragraph spans multiple lines. + */ + public final int lineIndex(int paragraphIndex, int columnPosition) { Cell, ParagraphBox> cell = virtualFlow.getCell(paragraphIndex); - return cell.getNode().getCurrentLineIndex(column); + return cell.getNode().getCurrentLineIndex(columnPosition); } TwoDimensional.Position _position(int par, int line) { @@ -1211,6 +1290,9 @@ public void replace(int start, int end, StyledDocument replacement) * * * ********************************************************************** */ + /** + * Disposes this area, preventing memory leaks. + */ public void dispose() { subscriptions.unsubscribe(); virtualFlow.dispose(); diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/InlineCssTextArea.java b/richtextfx/src/main/java/org/fxmisc/richtext/InlineCssTextArea.java index 8e787051a..2ae2dc161 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/InlineCssTextArea.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/InlineCssTextArea.java @@ -11,14 +11,21 @@ import static org.fxmisc.richtext.model.Codec.styledTextCodec; /** - * Text area that uses inline css to define style of text segments and paragraph segments. + * Text area that uses inline css to define style of text segments and paragraphs. */ public class InlineCssTextArea extends StyledTextArea { + /** + * Creates a blank area + */ public InlineCssTextArea() { this(new SimpleEditableStyledDocument<>("", "")); } + /** + * Creates an area that can render and edit another area's {@link EditableStyledDocument} or a developer's + * custom implementation of {@link EditableStyledDocument}. + */ public InlineCssTextArea(@NamedArg("document") EditableStyledDocument document) { super( "", TextFlow::setStyle, diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/JavaFXCompatibility.java b/richtextfx/src/main/java/org/fxmisc/richtext/JavaFXCompatibility.java index 4dd14ab75..adb43a95b 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/JavaFXCompatibility.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/JavaFXCompatibility.java @@ -8,6 +8,9 @@ import javafx.scene.paint.Paint; import javafx.scene.text.Text; +/** + * Uses reflection to make this project's code work on Java 8 and Java 9 in a single jar + */ class JavaFXCompatibility { private static boolean isJava9orLater; diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/MouseOverTextEvent.java b/richtextfx/src/main/java/org/fxmisc/richtext/MouseOverTextEvent.java index 241f619cd..803c59b59 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/MouseOverTextEvent.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/MouseOverTextEvent.java @@ -6,6 +6,10 @@ import javafx.scene.Node; import javafx.scene.Scene; +/** + * Defines an event where the mouse becomes stationary over some part of an area ({@link #MOUSE_OVER_TEXT_BEGIN}) + * and when it moves from that position ({@link #MOUSE_OVER_TEXT_END}). + */ public abstract class MouseOverTextEvent extends Event { private static final long serialVersionUID = 1L; diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/ParagraphBox.java b/richtextfx/src/main/java/org/fxmisc/richtext/ParagraphBox.java index 036024bcd..1f8b31192 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/ParagraphBox.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/ParagraphBox.java @@ -34,6 +34,16 @@ import org.reactfx.value.Val; import org.reactfx.value.Var; +/** + * Node responsible for rendering a single paragraph in the viewport, which may include a paragraph graphic factory + * (an {@link IntFunction} that takes the paragraph's index as an argument and returns a node), and definitely + * includes the segments of the paragraph itself. The paragraph graphic factory is often used to display + * the paragraph's line number. + * + * @param paragraph style type + * @param segment type + * @param segment style type + */ class ParagraphBox extends Region { /** diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/ParagraphText.java b/richtextfx/src/main/java/org/fxmisc/richtext/ParagraphText.java index 9a1b4ead6..110f4b53a 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/ParagraphText.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/ParagraphText.java @@ -35,6 +35,14 @@ import org.reactfx.value.Val; import org.reactfx.value.Var; +/** + * The class responsible for rendering the segments in an paragraph. It also renders additional RichTextFX-specific + * CSS found in {@link TextExt} as well as the selection and caret shapes. + * + * @param paragraph style type + * @param segment type + * @param segment style type + */ class ParagraphText extends TextFlowExt { // FIXME: changing it currently has not effect, because diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/Selection.java b/richtextfx/src/main/java/org/fxmisc/richtext/Selection.java index 64fc185f0..92dce866d 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/Selection.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/Selection.java @@ -48,6 +48,9 @@ */ public interface Selection { + /** + * Specifies whether to update the start/end value of a selection to the left (towards 0) or right (away from 0) + */ public static enum Direction { LEFT, RIGHT diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/SelectionPath.java b/richtextfx/src/main/java/org/fxmisc/richtext/SelectionPath.java index 8d81087c5..955c10dbb 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/SelectionPath.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/SelectionPath.java @@ -4,7 +4,6 @@ /** * A path which describes a selection shape in the Scene graph. - * */ public class SelectionPath extends Path { } diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/StyledTextArea.java b/richtextfx/src/main/java/org/fxmisc/richtext/StyledTextArea.java index 84024ea4e..c0d1600fe 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/StyledTextArea.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/StyledTextArea.java @@ -14,51 +14,11 @@ import org.fxmisc.richtext.model.TextOps; /** - * Text editing control. Accepts user input (keyboard, mouse) and - * provides API to assign style to text ranges. It is suitable for - * syntax highlighting and rich-text editors. - * - *

Subclassing is allowed to define the type of style, e.g. inline - * style or style classes.

- * - *

Note: Scroll bars no longer appear when the content spans outside - * of the viewport. To add scroll bars, the area needs to be wrapped in - * a {@link org.fxmisc.flowless.VirtualizedScrollPane}. For example,

- *
- * {@code
- * // shows area without scroll bars
- * InlineCssTextArea area = new InlineCssTextArea();
- *
- * // add scroll bars that will display as needed
- * VirtualizedScrollPane vsPane = new VirtualizedScrollPane(area);
- *
- * Parent parent = //;
- * parent.getChildren().add(vsPane)
- * }
- * 
- * - *

Overriding keyboard shortcuts

- * - * {@code StyledTextArea} uses {@code KEY_TYPED} handler to handle ordinary - * character input and {@code KEY_PRESSED} handler to handle control key - * combinations (including Enter and Tab). To add or override some keyboard - * shortcuts, while keeping the rest in place, you would combine the default - * event handler with a new one that adds or overrides some of the default - * key combinations. This is how to bind {@code Ctrl+S} to the {@code save()} - * operation: - *
- * {@code
- * import static javafx.scene.input.KeyCode.*;
- * import static javafx.scene.input.KeyCombination.*;
- * import static org.fxmisc.wellbehaved.event.EventPattern.*;
- * import static org.fxmisc.wellbehaved.event.InputMap.*;
- *
- * import org.fxmisc.wellbehaved.event.Nodes;
- *
- * Nodes.addInputMap(area, consume(keyPressed(S, CONTROL_DOWN), event -> save()));
- * }
- * 
+ * A {@link GenericStyledArea} whose segment generic has been specified to be a {@link String}. How the text + * will be styled is not yet specified in this class, but use {@link StyleClassedTextArea} for a style class + * approach to styling the text and {@link InlineCssTextArea} for an inline css approach to styling the text. * + * @param type of paragraph style * @param type of style that can be applied to text. */ public class StyledTextArea extends GenericStyledArea { @@ -118,11 +78,17 @@ public StyledTextArea(@NamedArg("initialParagraphStyle") PS initialParagraphStyl initialTextStyle, applyStyle, true); } + /** + * Creates a {@link TextExt} node using the given styled text. + */ public static Node createStyledTextNode(StyledSegment seg, BiConsumer applyStyle) { return createStyledTextNode(seg.getSegment(), seg.getStyle(), applyStyle); } + /** + * Creates a {@link TextExt} node using the given styled text. + */ public static Node createStyledTextNode(String text, S style, BiConsumer applyStyle) { diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/TextExt.java b/richtextfx/src/main/java/org/fxmisc/richtext/TextExt.java index 702553067..4b59967e4 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/TextExt.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/TextExt.java @@ -16,14 +16,14 @@ import javafx.scene.text.Text; /** - * A class which adds some more styleable properties to JavaFX's {@link Text} class. + * A class which adds some more RichTextFX-specific styleable properties to JavaFX's {@link Text} class. * * The extra items can be styled using the properties (and accessors/mutators) or via CSS. - * Each property is documented with its CSS property. Each CSS property begins with the "-rtfx" + * Each property is documented with its CSS property. Each CSS property begins with the "-rtfx" * prefix. * *

Note that the underline properties specified here are orthogonal to the {@link #underlineProperty()} inherited - * from {@link Text}. The underline properties defined here in {@link TextExt} will cause an underline to be + * from {@link Text}. The underline properties defined here in {@link TextExt} will cause an underline to be * drawn if {@link #underlineWidthProperty()} is non-null and greater than zero, regardless of * the value of {@link #underlineProperty()}.

*/ diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/UnderlinePath.java b/richtextfx/src/main/java/org/fxmisc/richtext/UnderlinePath.java index fa7b9fea4..7045728bf 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/UnderlinePath.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/UnderlinePath.java @@ -4,7 +4,6 @@ /** * A path which describes an underline in the Scene graph. - * */ public class UnderlinePath extends Path { } diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/ViewActions.java b/richtextfx/src/main/java/org/fxmisc/richtext/ViewActions.java index d8c3f1748..d3f6f182a 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/ViewActions.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/ViewActions.java @@ -20,6 +20,13 @@ import java.util.function.Consumer; import java.util.function.IntFunction; +/** + * Specifies view-related API for a {@link org.fxmisc.richtext.model.TextEditingArea} + * + * @param paragraph style type + * @param segment type + * @param segment style type + */ public interface ViewActions { /** diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/Codec.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/Codec.java index e132f6685..57a42cb52 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/Codec.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/Codec.java @@ -11,6 +11,11 @@ import org.reactfx.util.Either; +/** + * Specifies a way to serialize an object to/from a data stream + * + * @param the type of object to serialize + */ public interface Codec { String getName(); @@ -85,12 +90,6 @@ public StyledSegment decode(DataInputStream is) throws IOException { }; } - /** - * A codec which allows serialisation of this class to/from a data stream. - * - * Because S may be any type, you must pass a codec for it. If your style - * is String or Color, you can use {@link Codec#STRING_CODEC}/{@link Codec#COLOR_CODEC} respectively. - */ public static Codec> styledTextCodec(Codec styleCodec) { return new Codec>() { @Override diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/EditActions.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/EditActions.java index 2747b154f..d077e824d 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/EditActions.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/EditActions.java @@ -4,7 +4,7 @@ import org.fxmisc.richtext.GenericStyledArea; /** - * Extended edit actions for {@link TextEditingArea}. + * Specifies actions for editing the content of a {@link TextEditingArea}. */ public interface EditActions extends TextEditingArea { @@ -25,55 +25,56 @@ default void append(StyledDocument document) { /** * Inserts the given text at the given position. * - * @param index The location to insert the text. + * @param position The position to insert the text. * @param text The text to insert. */ - default void insertText(int index, String text) { - replaceText(index, index, text); + default void insertText(int position, String text) { + replaceText(position, position, text); } /** * Inserts the given text at the position returned from - * {@code getAbsolutePosition(paragraphIndex, columnIndex)}. + * {@code getAbsolutePosition(paragraphIndex, columnPosition)}. * *

Caution: see {@link #getAbsolutePosition(int, int)} to know how the column index argument * can affect the returned position.

* * @param text The text to insert */ - default void insertText(int paragraphIndex, int columnIndex, String text) { - int index = getAbsolutePosition(paragraphIndex, columnIndex); + default void insertText(int paragraphIndex, int columnPosition, String text) { + int index = getAbsolutePosition(paragraphIndex, columnPosition); replaceText(index, index, text); } /** * Inserts the given rich-text content at the given position. * - * @param index The location to insert the text. + * @param position The position to insert the text. * @param document The rich-text content to insert. */ - default void insert(int index, StyledDocument document) { - replace(index, index, document); + default void insert(int position, StyledDocument document) { + replace(position, position, document); } /** * Inserts the given rich-text content at the position returned from - * {@code getAbsolutePosition(paragraphIndex, columnIndex)}. + * {@code getAbsolutePosition(paragraphIndex, columnPosition)}. * *

Caution: see {@link #getAbsolutePosition(int, int)} to know how the column index argument * can affect the returned position.

* * @param document The rich-text content to insert. */ - default void insert(int paragraphIndex, int columnIndex, StyledDocument document) { - int index = getAbsolutePosition(paragraphIndex, columnIndex); - replace(index, index, document); + default void insert(int paragraphIndex, int columnPosition, StyledDocument document) { + int pos = getAbsolutePosition(paragraphIndex, columnPosition); + replace(pos, pos, document); } /** * Removes a range of text. * - * @param range The range of text to delete. It must not be null. + * @param range The range of text to delete. It must not be null. Its start and end values specify the start + * and end positions within the area. * * @see #deleteText(int, int) */ @@ -86,8 +87,8 @@ default void deleteText(IndexRange range) { * * It must hold {@code 0 <= start <= end <= getLength()}. * - * @param start Start index of the range to remove, inclusive. - * @param end End index of the range to remove, exclusive. + * @param start Start position of the range to remove + * @param end End position of the range to remove */ default void deleteText(int start, int end) { replaceText(start, end, ""); @@ -134,7 +135,7 @@ default void deleteNextChar() { } /** - * Clears the text. + * Clears the area, so that it displays only an empty paragraph. */ default void clear() { replaceText(0, getLength(), ""); @@ -174,21 +175,26 @@ default void replaceSelection(StyledDocument replacement) { replace(getSelection(), replacement); } - default void moveSelectedText(int pos) { + /** + * If something is currently selected and the given position is outside of the selection, moves the selected + * rich-text document to the given position by deleting it from the area and re-inserting it at the given position. + * If nothing is selected, moves the caret ot that position. + */ + default void moveSelectedText(int position) { IndexRange sel = getSelection(); - if((pos >= sel.getStart() && pos <= sel.getEnd()) || sel.equals(GenericStyledArea.EMPTY_RANGE)) { + if((position >= sel.getStart() && position <= sel.getEnd()) || sel.equals(GenericStyledArea.EMPTY_RANGE)) { // no move, just position the caret - selectRange(pos, pos); + selectRange(position, position); } else { StyledDocument text = this.subDocument(sel.getStart(), sel.getEnd()); - if(pos > sel.getEnd()) - pos -= sel.getLength(); + if(position > sel.getEnd()) + position -= sel.getLength(); deleteText(sel); - insert(pos, text); + insert(position, text); // select moved text - selectRange(pos, pos + text.length()); + selectRange(position, position + text.length()); } } } diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/EditableStyledDocument.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/EditableStyledDocument.java index a0ec36eb7..ad6db39c8 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/EditableStyledDocument.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/EditableStyledDocument.java @@ -9,8 +9,13 @@ import org.reactfx.value.Val; /** - * Content model for {@link org.fxmisc.richtext.GenericStyledArea}. Implements edit operations - * on styled text, but not worrying about view aspects. + * Content model for {@link org.fxmisc.richtext.GenericStyledArea}. Specifies edit operations + * on paragraph's styles, segments (like text), and segments' style, but does not worry about view-related aspects + * (e.g. scrolling). + * + * @param the paragraph style type + * @param the segment type + * @param the segment's style */ public interface EditableStyledDocument extends StyledDocument { @@ -43,6 +48,11 @@ public interface EditableStyledDocument extends StyledDocument plainChanges() { return richChanges() .map(RichTextChange::toPlainTextChange) @@ -50,6 +60,10 @@ default EventStream plainChanges() { .filter(pc -> !pc.isIdentity()); } + /** + * Returns an {@link EventStream} that emits a {@link RichTextChange} every time a change is made + * to this document, even when such a change does not modify the underlying document in any way. + */ EventStream> richChanges(); SuspendableNo beingUpdatedProperty(); @@ -64,18 +78,48 @@ default EventStream plainChanges() { * * * ********************************************************************** */ + /** + * Replaces the portion of this document {@code "from..to"} with the given {@code replacement}. + * + * @param start the absolute position in the document that starts the portion to replace + * @param end the absolute position in the document that ends the portion to replace + * @param replacement the document that replaces the removed portion of this document + */ void replace(int start, int end, StyledDocument replacement); + /** + * Sets the style of all segments in the given "from..to" range to the given style. + * + * @param from the absolute position in the document that starts the range to re-style + * @param to the absolute position in the document that ends the range to re-style + */ void setStyle(int from, int to, S style); - void setStyle(int paragraph, S style); + /** + * Sets all segments in the given paragraph to the given style + */ + void setStyle(int paragraphIndex, S style); - void setStyle(int paragraph, int fromCol, int toCol, S style); + /** + * Sets the given range "fromCol..toCol" in the given paragraph to the given style + */ + void setStyle(int paragraphIndex, int fromCol, int toCol, S style); + /** + * Replaces the style spans for the given range {@code "from..(from + styleSpans.length())"} with the given + * style spans + */ void setStyleSpans(int from, StyleSpans styleSpens); - void setStyleSpans(int paragraph, int from, StyleSpans styleSpens); + /** + * Replaces the style spans for the given range {@code "from..(from + styleSpans.length())"} in the given + * paragraph with the given style spans + */ + void setStyleSpans(int paragraphIndex, int from, StyleSpans styleSpens); - void setParagraphStyle(int parIdx, PS style); + /** + * Sets the given paragraph to the given paragraph style + */ + void setParagraphStyle(int paragraphIndex, PS style); } diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/GenericEditableStyledDocument.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/GenericEditableStyledDocument.java index 3baa656aa..bfe555c4b 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/GenericEditableStyledDocument.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/GenericEditableStyledDocument.java @@ -1,12 +1,13 @@ package org.fxmisc.richtext.model; /** - * Provides a basic implementation of {@link EditableStyledDocument}. See {@link SimpleEditableStyledDocument} for - * a version that is specified for {@link String}. + * Provides a basic implementation of {@link EditableStyledDocument} while still allowing a developer to specify its + * generics. See {@link SimpleEditableStyledDocument} for a version that specifies its segment style to {@link String}. * * @param type of style that can be applied to paragraphs (e.g. {@link javafx.scene.text.TextFlow}. * @param type of segment used in {@link Paragraph}. Can be only {@link org.fxmisc.richtext.TextExt text} - * (plain or styled) or a type that combines text and other {@link javafx.scene.Node}s. + * (plain or styled) or a type that combines text and other {@link javafx.scene.Node}s via + * {@code Either}. * @param type of style that can be applied to a segment. */ public final class GenericEditableStyledDocument extends GenericEditableStyledDocumentBase diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/GenericEditableStyledDocumentBase.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/GenericEditableStyledDocumentBase.java index d059b4f7f..0dafd0fed 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/GenericEditableStyledDocumentBase.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/GenericEditableStyledDocumentBase.java @@ -132,17 +132,17 @@ public void setStyle(int from, int to, S style) { } @Override - public void setStyle(int paragraph, S style) { - ensureValidParagraphIndex(paragraph); - doc.replaceParagraph(paragraph, p -> p.restyle(style)).exec(this::update); + public void setStyle(int paragraphIndex, S style) { + ensureValidParagraphIndex(paragraphIndex); + doc.replaceParagraph(paragraphIndex, p -> p.restyle(style)).exec(this::update); } @Override - public void setStyle(int paragraph, int fromCol, int toCol, S style) { - ensureValidParagraphRange(paragraph, fromCol, toCol); + public void setStyle(int paragraphIndex, int fromCol, int toCol, S style) { + ensureValidParagraphRange(paragraphIndex, fromCol, toCol); doc.replace( - new BiIndex(paragraph, fromCol), - new BiIndex(paragraph, toCol), + new BiIndex(paragraphIndex, fromCol), + new BiIndex(paragraphIndex, toCol), d -> d.mapParagraphs(p -> p.restyle(style)) ).exec(this::update); } @@ -165,14 +165,14 @@ public void setStyleSpans(int from, StyleSpans styleSpans) { } @Override - public void setStyleSpans(int paragraph, int from, StyleSpans styleSpans) { - setStyleSpans(doc.position(paragraph, from).toOffset(), styleSpans); + public void setStyleSpans(int paragraphIndex, int from, StyleSpans styleSpans) { + setStyleSpans(doc.position(paragraphIndex, from).toOffset(), styleSpans); } @Override - public void setParagraphStyle(int parIdx, PS style) { - ensureValidParagraphIndex(parIdx); - doc.replaceParagraph(parIdx, p -> p.setParagraphStyle(style)).exec(this::update); + public void setParagraphStyle(int paragraphIndex, PS style) { + ensureValidParagraphIndex(paragraphIndex); + doc.replaceParagraph(paragraphIndex, p -> p.setParagraphStyle(style)).exec(this::update); } @Override diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/NavigationActions.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/NavigationActions.java index 94a038303..482ae3571 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/NavigationActions.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/NavigationActions.java @@ -5,7 +5,7 @@ import javafx.scene.control.IndexRange; /** - * Navigation actions for {@link TextEditingArea}. + * Specifies actions for moving the caret and/or making a selection for a {@link TextEditingArea}. */ public interface NavigationActions extends TextEditingArea { @@ -13,22 +13,36 @@ public interface NavigationActions extends TextEditingAreaUsing the following example, where "||" is the anchor, "|" is the caret" and "_" is the + * new position, EXTEND updates the values to: + *

+         * "A text ||with| a _selection" -> "A text ||with a |selection"
+         * "A _text ||with| a selection" -> "A |text with|| a selection"
+         * 
+ */ EXTEND, } /** - * Moves the caret to the given position in the text - * and clears any selection. + * Moves the caret to the given position in the text and clears any selection. */ default void moveTo(int pos) { getCaretSelectionBind().moveTo(pos); } /** - * Moves the caret to the position returned from - * {@code getAbsolutePosition(paragraphIndex, columnIndex)} + * Moves the caret to the position returned from {@code getAbsolutePosition(paragraphIndex, columnIndex)} * and clears any selection. * *

For example, if "|" represents the caret, "_" represents a newline character, and given the text "Some_Text" @@ -51,28 +65,15 @@ default void moveTo(int paragraphIndex, int columnIndex) { } /** - * Moves the caret to the position indicated by {@code pos}. - * Based on the selection policy, the selection is either cleared - * (i.e. anchor is set to the same position as caret), adjusted - * (i.e. anchor is not moved at all), or extended - * (i.e. {@code pos} becomes the new caret and, if {@code pos} points - * outside the current selection, the far end of the current selection - * becomes the anchor. + * Moves the caret to the given position. */ - default void moveTo(int pos, SelectionPolicy selectionPolicy) { - getCaretSelectionBind().moveTo(pos, selectionPolicy); + default void moveTo(int position, SelectionPolicy selectionPolicy) { + getCaretSelectionBind().moveTo(position, selectionPolicy); } /** * Moves the caret to the position returned from - * {@code getAbsolutePosition(paragraphIndex, columnIndex)}. - * - * Based on the selection policy, the selection is either cleared - * (i.e. anchor is set to the same position as caret), adjusted - * (i.e. anchor is not moved at all), or extended - * (i.e. {@code getAbsolutePosition(paragraphIndex, columnIndex} becomes - * the new caret and, if that returned value points outside the current selection, - * the far end of the current selection becomes the anchor. + * {@code getAbsolutePosition(paragraphIndex, columnIndex)} using the given selection policy. * *

Caution: see {@link #getAbsolutePosition(int, int)} to know how the column index argument * can affect the returned position.

@@ -83,8 +84,6 @@ default void moveTo(int paragraphIndex, int columnIndex, SelectionPolicy selecti /** * Moves the caret backward one char in the text. - * Based on the given selection policy, anchor either moves with - * the caret, stays put, or moves to the former caret position. */ default void previousChar(SelectionPolicy selectionPolicy) { getCaretSelectionBind().moveToPrevChar(selectionPolicy); @@ -92,8 +91,6 @@ default void previousChar(SelectionPolicy selectionPolicy) { /** * Moves the caret forward one char in the text. - * Based on the given selection policy, anchor either moves with - * the caret, stays put, or moves to the former caret position. */ default void nextChar(SelectionPolicy selectionPolicy) { getCaretSelectionBind().moveToNextChar(selectionPolicy); @@ -101,8 +98,6 @@ default void nextChar(SelectionPolicy selectionPolicy) { /** * Skips n number of word boundaries backwards. - * Based on the given selection policy, anchor either moves with - * the caret, stays put, or moves to the former caret position. */ default void wordBreaksBackwards(int n, SelectionPolicy selectionPolicy) { if(getLength() == 0) { @@ -121,8 +116,6 @@ default void wordBreaksBackwards(int n, SelectionPolicy selectionPolicy) { /** * Skips n number of word boundaries forward. - * Based on the given selection policy, anchor either moves with - * the caret, stays put, or moves to the former caret position. */ default void wordBreaksForwards(int n, SelectionPolicy selectionPolicy) { if(getLength() == 0) { @@ -182,7 +175,7 @@ default void selectParagraph() { } /** - * Selects all text in the text input. + * Selects everything in the area. */ default void selectAll() { getCaretSelectionBind().selectAll(); diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/NodeSegmentOpsBase.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/NodeSegmentOpsBase.java index af73710cb..010b82e99 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/NodeSegmentOpsBase.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/NodeSegmentOpsBase.java @@ -3,7 +3,7 @@ import java.util.Optional; /** - * Properly implements {@link SegmentOps} when implementing a non-text custom object (e.g. shape, circle, image) + * Properly implements {@link SegmentOps} when implementing a non-text custom object, such as a shape or image, * and reduces boilerplate. Developers may want to override {@link #joinSeg(Object, Object)} and * {@link #joinStyle(Object, Object)}. * diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/Paragraph.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/Paragraph.java index 7f4b4a659..e3547e1f0 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/Paragraph.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/Paragraph.java @@ -17,16 +17,21 @@ import org.reactfx.util.Tuples; /** - * This is one Paragraph of the document. Depending on whether the text is wrapped, - * it corresponds to a single line or it can also span multiple lines. A Paragraph - * contains of a list of SEG objects which make up the individual segments of the - * Paragraph. By providing a specific segment object and an associated segment - * operations object, all required data and the necessary operations on this data - * for a single segment can be provided. + * One paragraph in the document that can itself be styled and which contains a list of styled segments. + * + *

+ * It corresponds to a single line when the + * text is not wrapped or spans multiple lines when the text is wrapped. A Paragraph + * contains of a list of {@link SEG} objects which make up the individual segments of the + * Paragraph. By providing a specific segment object and an associated + * {@link SegmentOps segment operations} object, all required data and the necessary + * operations on this data for a single segment can be provided. + *

+ * + *

For more complex requirements (for example, when both text and images shall be part + * of the document), a different segment type must be provided. One should use something + * like {@code Either} for their segment type. * - *

For more complex requirements (for example, when images shall be part of the - * document) a different segment type must be provided (which can make use of - * {@code StyledText} for the text part and add another segment type for images). * Note that Paragraph is an immutable class - to modify a Paragraph, a new * Paragraph object must be created. Paragraph itself contains some methods which * take care of this, such as concat(), which appends some Paragraph to the current @@ -34,8 +39,8 @@ * * @param The type of the paragraph style. * @param The type of the content segments in the paragraph (e.g. {@link String}). - * Every paragraph, even an empty paragraph, must have at least one SEG object - * (even if that SEG object itself represents an empty segment). + * Every paragraph, even an empty paragraph, must have at least one {@link SEG} object + * (even if that {@link SEG} object itself represents an empty segment). * @param The type of the style of individual segments. */ public final class Paragraph { @@ -83,6 +88,9 @@ private static List list(T head, T... tail) { private final SegmentOps segmentOps; + /** + * Creates a paragraph using a list of styled segments + */ public Paragraph(PS paragraphStyle, SegmentOps segmentOps, List> styledSegments) { this(paragraphStyle, segmentOps, decompose(styledSegments, segmentOps)); } @@ -91,14 +99,23 @@ private Paragraph(PS paragraphStyle, SegmentOps segmentOps, Tuple2 segmentOps, SEG segment, S style) { this(paragraphStyle, segmentOps, segment, StyleSpans.singleton(style, segmentOps.length(segment))); } + /** + * Creates a paragraph that has only one segment but a number of different styles throughout that segment + */ public Paragraph(PS paragraphStyle, SegmentOps segmentOps, SEG segment, StyleSpans styles) { this(paragraphStyle, segmentOps, Collections.singletonList(segment), styles); } + /** + * Creates a paragraph that has multiple segments with multiple styles throughout those segments + */ public Paragraph(PS paragraphStyle, SegmentOps segmentOps, List segments, StyleSpans styles) { if (segments.isEmpty()) { throw new IllegalArgumentException("Cannot construct a Paragraph with an empty list of segments"); @@ -120,6 +137,11 @@ public Paragraph(PS paragraphStyle, SegmentOps segmentOps, List seg private List> styledSegments = null; + /** + * Since the segments and styles in a paragraph are stored separate from another, combines these two collections + * into a single collection where each segment and its corresponding style are grouped into the same object. + * Essentially, returns {@code List>}. + */ public List> getStyledSegments() { if (styledSegments == null) { if (segments.size() == 1 && styles.getSpanCount() == 1) { diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/PlainTextChange.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/PlainTextChange.java index d659dfa24..ae3432f0e 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/PlainTextChange.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/PlainTextChange.java @@ -1,6 +1,8 @@ package org.fxmisc.richtext.model; - +/** + * An object that specifies where a non-style change occurred in a {@link org.fxmisc.richtext.GenericStyledArea}. + */ public class PlainTextChange extends TextChange { public PlainTextChange(int position, String removed, String inserted) { diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/ReadOnlyStyledDocument.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/ReadOnlyStyledDocument.java index 22f44d180..396844d2b 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/ReadOnlyStyledDocument.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/ReadOnlyStyledDocument.java @@ -25,8 +25,19 @@ import org.reactfx.util.Tuple2; import org.reactfx.util.Tuple3; +/** + * An immutable implementation of {@link StyledDocument} that does not allow editing. For a {@link StyledDocument} + * that can be edited, see {@link EditableStyledDocument}. + * + * @param The type of the paragraph style. + * @param The type of the segments in the paragraph (e.g. {@link String}). + * @param The type of the style of individual segments. + */ public final class ReadOnlyStyledDocument implements StyledDocument { + /** + * Private class used for calculating {@link TwoDimensional.Position}s within this document. + */ private static class Summary { private final int paragraphCount; private final int charCount; @@ -44,6 +55,9 @@ public int length() { } } + /** + * Private method for quickly calculating the length of a portion (subdocument) of this document. + */ private static ToSemigroup, Summary> summaryProvider() { return new ToSemigroup, Summary>() { @@ -67,6 +81,17 @@ public Summary reduce(Summary left, Summary right) { private static final BiFunction> NAVIGATE = (s, i) -> i <= s.length() ? left(i) : right(i - (s.length() + 1)); + /** + * Creates a {@link ReadOnlyStyledDocument} from the given string. + * + * @param str the text to use to create the segments + * @param paragraphStyle the paragraph style to use for each paragraph in the returned document + * @param style the style to use for each segment in the document + * @param segmentOps the operations object that can create a segment froma given text + * @param The type of the paragraph style. + * @param The type of the segments in the paragraph (e.g. {@link String}). + * @param The type of the style of individual segments. + */ public static ReadOnlyStyledDocument fromString(String str, PS paragraphStyle, S style, TextOps segmentOps) { Matcher m = LINE_TERMINATOR.matcher(str); @@ -87,12 +112,30 @@ public static ReadOnlyStyledDocument fromString(String return new ReadOnlyStyledDocument<>(res); } + /** + * Creates a {@link ReadOnlyStyledDocument} from the given segment. + * + * @param segment the only segment in the only paragraph in the document + * @param paragraphStyle the paragraph style to use for each paragraph in the returned document + * @param style the style to use for each segment in the document + * @param segmentOps the operations object that can create a segment froma given text + * @param The type of the paragraph style. + * @param The type of the segments in the paragraph (e.g. {@link String}). + * @param The type of the style of individual segments. + */ public static ReadOnlyStyledDocument fromSegment(SEG segment, PS paragraphStyle, S style, SegmentOps segmentOps) { Paragraph content = new Paragraph(paragraphStyle, segmentOps, segment, style); List> res = Collections.singletonList(content); return new ReadOnlyStyledDocument<>(res); } + /** + * Creates a {@link ReadOnlyStyledDocument} from the given {@link StyledDocument}. + * + * @param The type of the paragraph style. + * @param The type of the segments in the paragraph (e.g. {@link String}). + * @param The type of the style of individual segments. + */ public static ReadOnlyStyledDocument from(StyledDocument doc) { if(doc instanceof ReadOnlyStyledDocument) { return (ReadOnlyStyledDocument) doc; @@ -101,7 +144,17 @@ public static ReadOnlyStyledDocument from(StyledDocumen } } - + /** + * Defines a codec for serializing a {@link ReadOnlyStyledDocument}. + * + * @param pCodec the codec for serializing a {@link Paragraph} + * @param segCodec the codec for serializing a {@link StyledSegment} + * @param segmentOps the operations object for operating on segments + * + * @param The type of the paragraph style. + * @param The type of the segments in the paragraph (e.g. {@link String}). + * @param The type of the style of individual segments. + */ public static Codec> codec(Codec pCodec, Codec> segCodec, SegmentOps segmentOps) { return new Codec>() { @@ -212,15 +265,21 @@ public Position offsetToPosition(int offset, Bias bias) { return position(0, 0).offsetBy(offset, bias); } + /** + * Splits this document into two at the given position and returns both halves. + */ public Tuple2, ReadOnlyStyledDocument> split(int position) { return tree.locate(NAVIGATE, position).map(this::split); } + /** + * Splits this document into two at the given paragraph's column position and returns both halves. + */ public Tuple2, ReadOnlyStyledDocument> split( - int row, int col) { - return tree.splitAt(row).map((l, p, r) -> { - Paragraph p1 = p.trim(col); - Paragraph p2 = p.subSequence(col); + int paragraphIndex, int columnPosition) { + return tree.splitAt(paragraphIndex).map((l, p, r) -> { + Paragraph p1 = p.trim(columnPosition); + Paragraph p2 = p.subSequence(columnPosition); ReadOnlyStyledDocument doc1 = new ReadOnlyStyledDocument<>(l.append(p1)); ReadOnlyStyledDocument doc2 = new ReadOnlyStyledDocument<>(r.prepend(p2)); return t(doc1, doc2); @@ -253,6 +312,12 @@ public StyledDocument subSequence(int start, int end) { return split(end)._1.split(start)._2; } + /** + * Replaces the given portion {@code "from..to"} with the given replacement and returns + * 1) the updated version of this document that includes the replacement, + * 2) the {@link RichTextChange} that represents the change from this document to the returned one, and + * 3) the modification used to update an area's list of visible paragraphs. + */ public Tuple3, RichTextChange, MaterializedListModification>> replace( int from, int to, ReadOnlyStyledDocument replacement) { return replace(from, to, x -> replacement); @@ -379,8 +444,8 @@ public Position clamp() { } @Override - public Position offsetBy(int offset, Bias bias) { - return tree.locateProgressively(s -> s.charCount + s.paragraphCount, toOffset() + offset) + public Position offsetBy(int amount, Bias bias) { + return tree.locateProgressively(s -> s.charCount + s.paragraphCount, toOffset() + amount) .map(Pos::new); } diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/RichTextChange.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/RichTextChange.java index 57645a362..a6129bed7 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/RichTextChange.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/RichTextChange.java @@ -1,5 +1,8 @@ package org.fxmisc.richtext.model; +/** + * An object that specifies where a change occurred in a {@link org.fxmisc.richtext.GenericStyledArea}. + */ public class RichTextChange extends TextChange, RichTextChange> { public RichTextChange(int position, StyledDocument removed, StyledDocument inserted) { diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/SegmentOps.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/SegmentOps.java index 5355b8e3b..fe88f0929 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/SegmentOps.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/SegmentOps.java @@ -12,6 +12,7 @@ * @param The style type for the segment */ public interface SegmentOps { + public int length(SEG seg); public char charAt(SEG seg, int index); @@ -22,18 +23,34 @@ public interface SegmentOps { public SEG subSequence(SEG seg, int start); + /** + * Joins two consecutive segments together into one or {@link Optional#empty()} if they cannot be joined. + */ public Optional joinSeg(SEG currentSeg, SEG nextSeg); + /** + * Joins two consecutive styles together into one or {@link Optional#empty()} if they cannot be joined. By default, + * returns {@link Optional#empty()}. + */ default Optional joinStyle(S currentStyle, S nextStyle) { return Optional.empty(); } + /** + * Creates an empty segment. This method should return the same object for better performance and memory usage. + */ public SEG createEmptySeg(); + /** + * Creates a {@link TextOps} specified for a {@link String} segment that never merges consecutive styles + */ public static TextOps styledTextOps() { return styledTextOps((s1, s2) -> Optional.empty()); } + /** + * Creates a {@link TextOps} specified for a {@link String} + */ public static TextOps styledTextOps(BiFunction> mergeStyle) { return new TextOpsBase("") { @Override @@ -73,28 +90,67 @@ public Optional joinStyle(S currentStyle, S nextStyle) { }; } + /** + * Returns a {@link SegmentOps} that specifies its segment type to be an {@link Either} + * whose {@link Either#left(Object) left} value is this segment type and + * whose {@link Either#right(Object) right} value is {@code rOps}' segment type. + */ public default SegmentOps, S> or(SegmentOps rOps) { return either(this, rOps); } + /** + * Returns a {@link SegmentOps} + * that specifies its segment type to be an {@link Either} + * whose {@link Either#left(Object) left} value is this segment type and + * whose {@link Either#right(Object) right} value is {@code rOps}' segment type, and + * that specifies its style type to be {@link Either} + * whose {@link Either#left(Object) left} value is this style type and + * whose {@link Either#right(Object) right} value is {@code rOps}' style type. + */ public default SegmentOps, Either> orStyled( SegmentOps rOps ) { return eitherStyles(this, rOps); } + /** + * Returns a {@link SegmentOps} + * that specifies its segment type to be an {@link Either} + * whose {@link Either#left(Object) left} value is {@code lOps}' segment type and + * whose {@link Either#right(Object) right} value is {@code rOps}' segment type, and + * that specifies its style type to be {@link Either} + * whose {@link Either#left(Object) left} value is {@code lOps}' style type and + * whose {@link Either#right(Object) right} value is {@code rOps}' style type. + * + * Note: consecutive styles will not be merged. + */ public static SegmentOps, Either> eitherStyles( SegmentOps lOps, SegmentOps rOps) { return new EitherStyledSegmentOps<>(lOps, rOps); } - public static SegmentOps, S> either(SegmentOps lOps, SegmentOps rOps) { + /** + * Returns a {@link SegmentOps} that specifies its segment type to be an {@link Either} + * whose {@link Either#left(Object) left} value is {@code lOps}' segment type and + * whose {@link Either#right(Object) right} value is {@code rOps}' segment type. + * + * Note: consecutive styles will not be merged. + */ + public static SegmentOps, Style> either(SegmentOps lOps, + SegmentOps rOps) { return either(lOps, rOps, (leftStyle, rightStyle) -> Optional.empty()); } - public static SegmentOps, S> either(SegmentOps lOps, SegmentOps rOps, - BiFunction> mergeStyle) { + /** + * Returns a {@link SegmentOps} that specifies its segment type to be an {@link Either} + * whose {@link Either#left(Object) left} value is {@code lOps}' segment type and + * whose {@link Either#right(Object) right} value is {@code rOps}' segment type. + */ + public static SegmentOps, Style> either( + SegmentOps lOps, SegmentOps rOps, + BiFunction> mergeStyle) { return new EitherSegmentOps<>(lOps, rOps, mergeStyle); } } diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/SegmentOpsBase.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/SegmentOpsBase.java index 24bf2e43e..36fae6fb6 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/SegmentOpsBase.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/SegmentOpsBase.java @@ -3,9 +3,9 @@ import java.util.Optional; /** - * Properly implements the {@link SegmentOps} interface and reduces boilerplate, so that developer only needs to - * implement methods for real segments, not empty ones. Optionally, {@link #joinSeg(Object, Object)} and - * {@link #joinStyle(Object, Object)} can be overridden as well. + * Provides a base for properly implementing the {@link SegmentOps} interface and reduces boilerplate, + * so that a developer only needs to implement methods for real segments, not empty ones. Optionally, + * {@link #joinSeg(Object, Object)} and {@link #joinStyle(Object, Object)} can be overridden as well. * * @param the type of segment * @param the type of style @@ -14,6 +14,9 @@ public abstract class SegmentOpsBase implements SegmentOps { private final SEG empty; + /** + * Creates a {@link SegmentOpsBase} that returns {@code emptySeg} every time an empty segment should be returned + */ public SegmentOpsBase(SEG emptySeg) { this.empty = emptySeg; } diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/SimpleEditableStyledDocument.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/SimpleEditableStyledDocument.java index 36e71a422..db587607c 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/SimpleEditableStyledDocument.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/SimpleEditableStyledDocument.java @@ -4,20 +4,30 @@ import java.util.function.BiFunction; /** - * Provides an implementation of {@link EditableStyledDocument} that is specified for {@link String} as its segment. - * See also {@link GenericEditableStyledDocument}. + * Provides an implementation of {@link EditableStyledDocument} that specifies its segment type as {@link String}. + * For a version that can specify the segment's generics, see {@link GenericEditableStyledDocument}. */ public final class SimpleEditableStyledDocument extends GenericEditableStyledDocumentBase { + /** + * Creates a document that does not merge consecutive styles + */ public SimpleEditableStyledDocument(PS initialParagraphStyle, S initialStyle) { this(initialParagraphStyle, initialStyle, (s1, s2) -> Optional.empty()); } + /** + * Creates a document that uses {@link SegmentOps#styledTextOps(BiFunction)} to operate on segments and merge + * consecutive styles. + */ public SimpleEditableStyledDocument(PS initialParagraphStyle, S initialStyle, BiFunction> mergeStyle) { this(initialParagraphStyle, initialStyle, SegmentOps.styledTextOps(mergeStyle)); } + /** + * Creates a document that uses a custom {@link SegmentOps} to operate on segments and merge styles. + */ public SimpleEditableStyledDocument(PS initialParagraphStyle, S initialTextStyle, SegmentOps segOps) { super(initialParagraphStyle, initialTextStyle, segOps); } diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/StyleActions.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/StyleActions.java index 724aa1520..bb8d71e11 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/StyleActions.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/StyleActions.java @@ -3,6 +3,12 @@ import javafx.beans.property.BooleanProperty; import javafx.scene.control.IndexRange; +/** + * Specifies actions related to getting and setting styles throughout a {@link TextEditingArea}. + * + * @param the paragraph style + * @param the segment's style + */ public interface StyleActions { /** diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/StyleSpan.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/StyleSpan.java index d70cd398c..38e4bdd68 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/StyleSpan.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/StyleSpan.java @@ -2,11 +2,19 @@ import java.util.Objects; +/** + * Essentially, a {@link org.reactfx.util.Tuple2} of a given style {@link S} that spans a given length. + * + * @param the style type + */ public class StyleSpan { private final S style; private final int length; + /** + * Creates a style span. Note: length cannot be negative. + */ public StyleSpan(S style, int length) { if(length < 0) { throw new IllegalArgumentException("StyleSpan's length cannot be negative"); diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/StyleSpans.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/StyleSpans.java index 78b1b473b..ce7e4e345 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/StyleSpans.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/StyleSpans.java @@ -11,12 +11,23 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; +/** + * Essentially, a list of {@link StyleSpan} objects. + * + * @param the style type + */ public interface StyleSpans extends Iterable>, TwoDimensional { + /** + * Creates a {@link StyleSpans} object that only contains one {@link StyleSpan} object. + */ static StyleSpans singleton(S style, int length) { return singleton(new StyleSpan<>(style, length)); } + /** + * Creates a {@link StyleSpans} object that only contains one {@link StyleSpan} object. + */ static StyleSpans singleton(StyleSpan span) { return new SingletonSpans<>(span); } @@ -51,10 +62,16 @@ public StyleSpan next() { }; } + /** + * Appends the given style to the end of the list of {@link StyleSpan}. + */ default StyleSpans append(S style, int length) { return append(new StyleSpan<>(style, length)); } + /** + * Appends the given style to the end of the list of {@link StyleSpan}. + */ default StyleSpans append(StyleSpan span) { if(span.getLength() == 0) { return this; @@ -72,10 +89,16 @@ default StyleSpans append(StyleSpan span) { } } + /** + * Prepends the given style to the start of the list of {@link StyleSpan}. + */ default StyleSpans prepend(S style, int length) { return prepend(new StyleSpan<>(style, length)); } + /** + * Prepends the given style to the start of the list of {@link StyleSpan}. + */ default StyleSpans prepend(StyleSpan span) { if(span.getLength() == 0) { return this; @@ -92,6 +115,9 @@ default StyleSpans prepend(StyleSpan span) { } } + /** + * Same as {@link java.util.List#subList(int, int)} + */ default StyleSpans subView(int from, int to) { Position start = offsetToPosition(from, Forward); Position end = to > from @@ -100,6 +126,9 @@ default StyleSpans subView(int from, int to) { return subView(start, end); } + /** + * Same as {@link java.util.List#subList(int, int)}, except that the arguments are two dimensional. + */ default StyleSpans subView(Position from, Position to) { return new SubSpans<>(this, from, to); } @@ -153,14 +182,24 @@ default StyleSpans mapStyles(UnaryOperator mapper) { return builder.create(); } + /** + * Applies the given bifunction {@code f} to this object's {@link StyleSpan} objects and + * {@code that} {@link StyleSpan} objects and stores the result in the returned {@link StyleSpans} object. + */ default StyleSpans overlay(StyleSpans that, BiFunction f) { return StyleSpansBuilder.overlay(this, that, f); } + /** + * Returns a stream of just this list of {@link StyleSpan}'s styles. + */ default Stream styleStream() { return stream().map(StyleSpan::getStyle); } + /** + * Returns a stream of this list' {@link StyleSpan} objects. + */ default Stream> stream() { Spliterator> spliterator = new Spliterator>() { private final Iterator> iterator = iterator(); diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/StyleSpansBuilder.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/StyleSpansBuilder.java index 02f09e693..f9b2b1c71 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/StyleSpansBuilder.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/StyleSpansBuilder.java @@ -7,6 +7,11 @@ import java.util.List; import java.util.function.BiFunction; +/** + * A one-time-use builder that Builds a memory efficient {@link StyleSpans} object. + * + * @param the segment style type + */ public class StyleSpansBuilder { private static class StyleSpansImpl extends StyleSpansBase { diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/StyledDocument.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/StyledDocument.java index be2520764..da0a8fec6 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/StyledDocument.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/StyledDocument.java @@ -8,6 +8,13 @@ import javafx.scene.control.IndexRange; +/** + * An object (document) that is a list of styleable {@link Paragraph} that each contain a list of styleable segments. + * + * @param The type of the paragraph style. + * @param The type of the segments in the paragraph (e.g. {@link String}). + * @param The type of the style of individual segments. + */ public interface StyledDocument extends TwoDimensional { int length(); @@ -140,8 +147,43 @@ default StyleSpans getStyleSpans(int paragraph, int from, int to) { return getParagraphs().get(paragraph).getStyleSpans(from, to); } - default int getAbsolutePosition(int paragraphIndex, int columnIndex) { - int position = position(paragraphIndex, columnIndex).toOffset(); + /** + * Returns the absolute position (i.e. the spot in-between characters) of the given column position in the given + * paragraph. + * + *

For example, given a text with only one line {@code "text"} and a {@code columnPosition} value of {@code 1}, + * the value, {@code 1}, as in "position 1" would be returned:

+ *
+     *  ┌ character index 0
+     *  | ┌ character index 1
+     *  | |   ┌ character index 3
+     *  | |   |
+     *  v v   v
+     *
+     * |t|e|x|t|
+     *
+     * ^ ^     ^
+     * | |     |
+     * | |     └ position 4
+     * | └ position 1
+     * └ position 0
+     * 
+ * + *

Warning: Off-By-One errors can easily occur

+ *

If the column index spans outside of the given paragraph's length, the returned value will + * pass on to the previous/next paragraph. In other words, given a document with two paragraphs + * (where the first paragraph's text is "some" and the second "thing"), then the following statements are true:

+ *
    + *
  • getAbsolutePosition(0, "some".length()) == 4 == getAbsolutePosition(1, -1)
  • + *
  • getAbsolutePosition(0, "some".length() + 1) == 5 == getAbsolutePosition(1, 0)
  • + *
+ * + * @param paragraphIndex The index of the paragraph from which to start. + * @param columnPosition If positive, the index going forward (the given paragraph's line or the next one(s)). + * If negative, the index going backward (the previous paragraph's line(s)) + */ + default int getAbsolutePosition(int paragraphIndex, int columnPosition) { + int position = position(paragraphIndex, columnPosition).toOffset(); if (position < 0) { throw new IndexOutOfBoundsException(String.format("Negative index! Out of bounds by %s.", 0 - position)); } diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/StyledSegment.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/StyledSegment.java index 24f0a3bae..b22f84cb8 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/StyledSegment.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/StyledSegment.java @@ -2,6 +2,13 @@ import java.util.Objects; +/** + * Essentially, an immutable {@link org.reactfx.util.Tuple2} that combines a {@link SEG segment} object and a + * {@link S style} object together. + * + * @param the segment type + * @param the style type + */ public final class StyledSegment { private final SEG segment; @@ -17,7 +24,7 @@ public StyledSegment(SEG segment, S style) { @Override public String toString() { - return String.format("StyledSegment(segment=%s style=%s", segment, style); + return String.format("StyledSegment(segment=%s style=%s)", segment, style); } @Override diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/SuperCodec.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/SuperCodec.java index d84a38f4b..450a0c4f0 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/SuperCodec.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/SuperCodec.java @@ -7,6 +7,12 @@ import java.util.Collection; import java.util.List; +/** + * Codec that can serialize an object and its super class; helpful when using generics and captures. + * + * @param the super class type + * @param the regular class type + */ interface SuperCodec extends Codec { void encodeSuper(DataOutputStream os, S s) throws IOException; @@ -15,6 +21,9 @@ default void encode(DataOutputStream os, T t) throws IOException { encodeSuper(os, t); } + /** + * Returns a codec that can serialize {@code sc}'s type's super class + */ @SuppressWarnings("unchecked") static SuperCodec upCast(SuperCodec sc) { return (SuperCodec) sc; diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/TextChange.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/TextChange.java index f198b4827..c42bbb89b 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/TextChange.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/TextChange.java @@ -3,6 +3,13 @@ import java.util.Objects; import java.util.Optional; +/** + * Base change class for style changes ({@link RichTextChange}) and non-style changes ({@link PlainTextChange}) + * in a {@link TextEditingArea}. + * + * @param type of data that was removed and inserted in the {@link TextEditingArea}. + * @param a subclass of TextChange + */ public abstract class TextChange> { protected final int position; @@ -15,11 +22,24 @@ public TextChange(int position, S removed, S inserted) { this.inserted = inserted; } + /** + * Gets the start position of where the replacement happened + */ public int getPosition() { return position; }; + public S getRemoved() { return removed; } public S getInserted() { return inserted; } + + /** + * Returns a new subclass of {@link TextChange} that makes the {@code inserted} the removed object and + * the {@code removed} the inserted object + */ public Self invert() { return create(position, inserted, removed); } + + /** Returns the position where the removal ends (e.g. {@code position + removedLength())} */ public int getRemovalEnd() { return position + removedLength(); } + + /** Returns the position where the inserted ends (e.g. {@code position + insertedLength())} */ public int getInsertionEnd() { return position + insertedLength(); } /** diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/TextEditingArea.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/TextEditingArea.java index b189e189e..72b31fb78 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/TextEditingArea.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/TextEditingArea.java @@ -47,6 +47,9 @@ public interface TextEditingArea { */ StyledDocument getDocument(); + /** + * Returns the object used for operating over {@link SEG segments} and their styles + */ SegmentOps getSegOps(); /** @@ -88,7 +91,7 @@ public interface TextEditingArea { default Var showCaretProperty() { return getCaretSelectionBind().showCaretProperty(); } /** - * Gets the area's main selection + * Gets the area's main {@link CaretSelectionBind}. */ CaretSelectionBind getCaretSelectionBind(); @@ -134,12 +137,12 @@ public interface TextEditingArea { *********************/ /** - * Stream of text changes. + * See {@link EditableStyledDocument#plainChanges()} */ EventStream plainTextChanges(); /** - * Stream of rich text changes. + * See {@link EditableStyledDocument#richChanges()} */ EventStream> richChanges(); diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/TextOps.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/TextOps.java index e2dac8a7b..ad6b9ed32 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/TextOps.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/TextOps.java @@ -5,18 +5,42 @@ import java.util.Optional; import java.util.function.BiFunction; +/** + * Extends {@link SegmentOps} by adding {@link #create(String)}, which can create a {@link SEG} segment from a given + * {@link String} + * + * @param the type of segment + * @param the type of style + */ public interface TextOps extends SegmentOps { + + /** + * Creates a segment using the given text. One could think of this as a mapping function from {@link String} to + * {@link SEG} + */ public SEG create(String text); + /** + * Same as {@link SegmentOps#either(SegmentOps, SegmentOps, BiFunction)}, except that + * {@link TextOps#create(String)} will use this object's {@code create(String)} method, not {@code rOps}' version. + */ public default TextOps, S> _or(SegmentOps rOps, BiFunction> mergeStyle) { return eitherL(this, rOps, mergeStyle); } + /** + * Same as {@link SegmentOps#either(SegmentOps, SegmentOps, BiFunction)}, except that + * {@link TextOps#create(String)} will use {@code lOps}' {@code create(String)} method, not {@code rOps}' version. + */ public static TextOps, S> eitherL(TextOps lOps, SegmentOps rOps, BiFunction> mergeStyle) { return new LeftTextOps<>(lOps, rOps, mergeStyle); } + /** + * Same as {@link SegmentOps#either(SegmentOps, SegmentOps, BiFunction)}, except that + * {@link TextOps#create(String)} will use {@code rOps}' {@code create(String)} method, not {@code lOps}' version. + */ public static TextOps, S> eitherR(SegmentOps lOps, TextOps rOps, BiFunction> mergeStyle) { return new RightTextOps<>(lOps, rOps, mergeStyle); diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/TextOpsBase.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/TextOpsBase.java index aad4ce611..b05f22b56 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/TextOpsBase.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/TextOpsBase.java @@ -1,5 +1,11 @@ package org.fxmisc.richtext.model; +/** + * Base class for a {@link TextOps} implementation that uses a text-based segment + * + * @param the type of segment + * @param the type of segment style + */ public abstract class TextOpsBase extends SegmentOpsBase implements TextOps { TextOpsBase(SEG empty) { diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/TwoDimensional.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/TwoDimensional.java index be3853d9e..397819c8a 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/TwoDimensional.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/TwoDimensional.java @@ -2,23 +2,63 @@ /** * TwoDimensional is an interface for any item which can be navigated - * in two dimensions. A two dimensional position has a major dimension - * (e.g. paragraph number in a document) and a minor dimension (e.g. - * segment index or column number within a paragraph). Generally, - * documents are not rectangular (paragraphs in a document are of differing lengths), - * so the valid values of the minor dimension depends on the major dimension. + * in two dimensions, such as a List of Lists. In short, it allows one to find a position within this object. + *

+ * There are two basic kinds of two dimensional objects. + *

+*
    +*
  1. +* One has a type of {@code List>}. The {@code major} dimension's value indicates the index of +* the "inner list" within the "outer list" while the {@code minor} dimension's value indicates the index +* of the object within an "inner list." +*
  2. +*
  3. +* One has a type of {@code List}. The {@code major} +* dimension's value indicates the index within the list while the {@code minor} dimension's value +* indicates how far into that length-object a position is (e.g. how many characters into a {@link String} +* is a position). +*
  4. +*
+ * + *

+ * Not all two dimensional objects are rectangular, so the valid values of the minor dimension depends on + * the major dimension. + *

+ * */ public interface TwoDimensional { + /** + * Determines whether to add 1 when the end of an inner list is reached + * + *

For example, given the following two dimensional object (a list of lists of objects) where the inner lists + * values show the absolute index of that specific item within the outer list... + *


+     * [                 // outer list
+     *      [0, 1, 2, 3],   // inner list 1
+     *      [4, 5, 6]       // inner list 2
+     * ]                 // outer list
+     * 
+ * ...navigating to {@code listOfListsObject.offsetTo(3, BACKWARD)} will return {@code 3} whereas + * {@code listOfListsObject.offsetTo(3, FORWARD)} will return {@code 4}, the "next item" in the two + * dimensional object. + * The Bias does not apply if the index is any non-last-index in the inner list. Thus, + * {@code listOfListsObject.offsetTo(1, BACKWARD)} and {@code listOfListsObject.offsetTo(1, FORWARD)} + * will return the same value, {@code 1}, since the position is not at the end of the list. + */ enum Bias { + /** When the returned value would be equal to the last index in an "inner list" or the length of some object + * with length, returns the {@code value + 1}. See {@link Bias} for more clarification. */ Forward, + /** When the returned value would be equal to the last index in an "inner list" or the length of some object + * with length, returns the value unmodified. See {@link Bias} for more clarification. */ Backward, } /** - * A two dimensional position, with a major offset (e.g. paragraph - * number within a document) and a minor dimension (e.g. segment index or column - * number within a paragraph). Major and minor positions begin at 0. + * A two dimensional position, with a major offset (such as a paragraph index within a document) + * and a minor dimension (such as a segment index or column position within a paragraph). Major and minor + * positions begin at 0. */ interface Position { @@ -51,17 +91,31 @@ interface Position { */ public Position clamp(); - public Position offsetBy(int offset, Bias bias); + /** + * Returns a new position that offsets this position by the given amount + */ + public Position offsetBy(int amount, Bias bias); /** * Converts this position to an overall offset within the original - * TwoDimensional item (which getTargetObject refers to) + * TwoDimensional item (to which {@link #getTargetObject} refers). + * For example, moving a caret to a relative position (paragraph 2, column 3) + * might result in the offset value (absolute position) of 28. */ int toOffset(); } + /** + * Creates a two dimensional position in some entity (e.g. area, list of lists, list of some object with length) + * where the {@code major} value is the index within the outer list) and the {@code minor} + * value is either the index within the inner list or some amount of length in a list of objects that have length. + */ Position position(int major, int minor); + /** + * Creates a two dimensional position in some entity (e.g. area, list of lists, list of some object with length) + * where the {@code offset} value is an absolute position in that entity. + */ Position offsetToPosition(int offset, Bias bias); } diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/TwoLevelNavigator.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/TwoLevelNavigator.java index f026f3bd1..976c36b58 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/model/TwoLevelNavigator.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/TwoLevelNavigator.java @@ -5,6 +5,10 @@ import java.util.function.IntSupplier; import java.util.function.IntUnaryOperator; +/** + * Default implementation of {@link TwoDimensional} that makes it trivial to calculate a position within a + * two dimensional object. + */ public class TwoLevelNavigator implements TwoDimensional { private class Pos implements Position { @@ -58,11 +62,11 @@ public Position clamp() { } @Override - public Position offsetBy(int offset, Bias bias) { - if(offset > 0) { - return forward(offset, bias); - } else if(offset < 0) { - return backward(-offset, bias); + public Position offsetBy(int amount, Bias bias) { + if(amount > 0) { + return forward(amount, bias); + } else if(amount < 0) { + return backward(-amount, bias); } else if(minor == 0 && major > 1 && bias == Backward) { return new Pos(major - 1, elemLength.applyAsInt(major - 1)); } else if(minor == elemLength.applyAsInt(major) && major < elemCount.getAsInt() - 1 && bias == Forward){ @@ -127,6 +131,15 @@ private Position backward(int offset, Bias bias) { private final IntSupplier elemCount; private final IntUnaryOperator elemLength; + /** + * Creates a navigator that can be used to find a {@link TwoDimensional.Position} within a two dimensional object. + * + * @param elemCount a supplier that returns the number of "inner lists" within an "outer list" (see + * {@link TwoDimensional} for clarification). For example, + * {@link java.util.List#size() List::size} + * @param elemLength a function that, given the index of an "inner list," returns either the size of that "inner + * list" of the length of that inner object. + */ public TwoLevelNavigator(IntSupplier elemCount, IntUnaryOperator elemLength) { this.elemCount = elemCount; this.elemLength = elemLength; diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/model/package-info.java b/richtextfx/src/main/java/org/fxmisc/richtext/model/package-info.java new file mode 100644 index 000000000..9ee7a43c4 --- /dev/null +++ b/richtextfx/src/main/java/org/fxmisc/richtext/model/package-info.java @@ -0,0 +1,55 @@ +/** + * Defines the support classes and operations related to {@link org.fxmisc.richtext.model.EditableStyledDocument}, + * the immutable model of rich-text content that can be rendered and edited. + * + *

+ * An {@link org.fxmisc.richtext.model.EditableStyledDocument} acts as an immutable model for rich-text content + * that will be rendered by an object implementing the {@link org.fxmisc.richtext.model.TextEditingArea} interface. + * A {@link org.fxmisc.richtext.model.StyledDocument} is composed of a list of + * {@link org.fxmisc.richtext.model.Paragraph}s. Paragraphs are nothing more than an + * object containing a paragraph style (type {@code PS}), a list of a generic segments (type {@code SEG}), and a + * list of generic styles (type {@code S}) that can apply to a segment. Most of the time, either + * {@link org.fxmisc.richtext.model.EditableStyledDocument} or + * {@link org.fxmisc.richtext.model.ReadOnlyStyledDocument} are being used to implement that interface. + *

+ *

+ * The document can include more than just text; thus, the segment generic + * can be specified as regular text ({@link java.lang.String}) or as an {@link org.reactfx.util.Either} (e.g. + * {@code Either} or as a nested Either (e.g. + * {@code Either}) if one wanted to have four different kinds of segments + * (ways to specify a segment generic in a way that still makes the code easy to read are not described here). + *

+ *

+ * To allow these generics, one must supply a {@link org.fxmisc.richtext.model.SegmentOps} object that can + * correctly operate on the generic segments and their generic styles. In addition, a + * {@link org.fxmisc.richtext.model.TextOps} adds one more method to its base interface by adding a method + * that maps a {@link java.lang.String} to a given segment. For text-based custom segments, one should use + * {@link org.fxmisc.richtext.model.SegmentOpsBase} and for node-based custom segments, one should use + * {@link org.fxmisc.richtext.model.NodeSegmentOpsBase}. + *

+ *

+ * The document also uses {@link org.fxmisc.richtext.model.StyleSpans} to store styles in a memory-efficient way. + * To construct one, use {@link org.fxmisc.richtext.model.StyleSpans#singleton(org.fxmisc.richtext.model.StyleSpan)} + * or {@link org.fxmisc.richtext.model.StyleSpansBuilder}. + *

+ *

+ * To navigate throughout the document, read through the javadoc of + * {@link org.fxmisc.richtext.model.TwoDimensional} and {@link org.fxmisc.richtext.model.TwoDimensional.Bias}. + * Also, read the difference between "position" and "index" in + * {@link org.fxmisc.richtext.model.StyledDocument#getAbsolutePosition(int, int)}. + *

+ *

To serialize things correctly, see {@link org.fxmisc.richtext.model.Codec} and its static factory methods. + *

+ *

+ * Lastly, the {@link org.fxmisc.richtext.model.EditableStyledDocument} can emit + * {@link org.fxmisc.richtext.model.PlainTextChange}s or {@link org.fxmisc.richtext.model.RichTextChange}s + * that can be used to undo/redo various changes. + *

+ * + * @see org.fxmisc.richtext.model.EditableStyledDocument + * @see org.fxmisc.richtext.model.Paragraph + * @see org.fxmisc.richtext.model.SegmentOps + * @see org.fxmisc.richtext.model.TwoDimensional + * @see org.fxmisc.richtext.model.TwoDimensional.Bias + */ +package org.fxmisc.richtext.model; \ No newline at end of file diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/package-info.java b/richtextfx/src/main/java/org/fxmisc/richtext/package-info.java new file mode 100644 index 000000000..70f843e38 --- /dev/null +++ b/richtextfx/src/main/java/org/fxmisc/richtext/package-info.java @@ -0,0 +1,22 @@ +/** + * Defines the view-related classes for rendering and editing an + * {@link org.fxmisc.richtext.model.EditableStyledDocument EditableStyledDocument}. + * + *

+ * The base area is {@link org.fxmisc.richtext.GenericStyledArea}. Those unfamiliar with this + * project should read through its javadoc. This class should be used for custom segments (e.g. text and images + * in the same area). {@link org.fxmisc.richtext.StyledTextArea} uses {@link java.lang.String}-only segments, + * and styling them are already supported in the two most common ways via + * {@link org.fxmisc.richtext.StyleClassedTextArea} and {@link org.fxmisc.richtext.InlineCssTextArea}. + * For those looking to use a base for a code editor, see {@link org.fxmisc.richtext.CodeArea}. + *

+ * + * @see org.fxmisc.richtext.model.EditableStyledDocument + * @see org.fxmisc.richtext.model.TwoDimensional + * @see org.fxmisc.richtext.model.TwoDimensional.Bias + * @see org.fxmisc.richtext.GenericStyledArea + * @see org.fxmisc.richtext.model.TextEditingArea + * @see org.fxmisc.richtext.Caret + * @see org.fxmisc.richtext.Selection + */ +package org.fxmisc.richtext; \ No newline at end of file diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/util/MouseStationaryEvent.java b/richtextfx/src/main/java/org/fxmisc/richtext/util/MouseStationaryEvent.java index c83e97f75..9318120aa 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/util/MouseStationaryEvent.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/util/MouseStationaryEvent.java @@ -6,6 +6,11 @@ import javafx.scene.Scene; import javafx.scene.input.InputEvent; +/** + * An event indicating that the mouse has stopped moving and remained stationary for time + * ({@link #MOUSE_STATIONARY_BEGIN}), or that the stationary mouse has once again moved + * ({@link #MOUSE_STATIONARY_END}). + */ public abstract class MouseStationaryEvent extends InputEvent { private static final long serialVersionUID = 1L; diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/util/MouseStationaryHelper.java b/richtextfx/src/main/java/org/fxmisc/richtext/util/MouseStationaryHelper.java index 96ccae912..d9031680f 100644 --- a/richtextfx/src/main/java/org/fxmisc/richtext/util/MouseStationaryHelper.java +++ b/richtextfx/src/main/java/org/fxmisc/richtext/util/MouseStationaryHelper.java @@ -14,15 +14,27 @@ import org.reactfx.Subscription; import org.reactfx.util.Either; +/** + * Helper class for setting up the code that will fire both kinds of {@link MouseStationaryEvent} when + * these events occur. + */ public class MouseStationaryHelper { private final Node node; private Subscription installed = null; + /** + * Creates a helper class that can install/uninstall the code needed to fire events when the mouse becomes + * stationary over the given node. + */ public MouseStationaryHelper(Node node) { this.node = node; } + /** + * Returns an {@link EventStream} that emits a {@link Point2D} whenever the mouse becomes stationary + * over the helper's node and emits a {@code null} value whenever the mouse moves after being stationary. + */ public EventStream> events(Duration delay) { EventStream mouseEvents = eventsOf(node, MouseEvent.ANY); EventStream stationaryPositions = mouseEvents @@ -33,6 +45,11 @@ public EventStream> events(Duration delay) { return stationaryPositions.or(stoppers).distinct(); } + /** + * Sets up the code to fire a {@code BEGIN} event when the mouse becomes stationary over the node and has not + * moved for the given amount of time ({@code delay}), and to fire a {@code END} event when the stationary + * mouse moves again. Note: any previously installed delays will be removed without creating memory leaks. + */ public void install(Duration delay) { if(installed != null) { installed.unsubscribe(); @@ -43,6 +60,10 @@ public void install(Duration delay) { .subscribe(evt -> Event.fireEvent(node, evt)); } + /** + * Removes uninstalls the code that would fire {@code BEGIN} and {@code END} events when the mouse became + * stationary over this helper's node. + */ public void uninstall() { if(installed != null) { installed.unsubscribe(); diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/util/package-info.java b/richtextfx/src/main/java/org/fxmisc/richtext/util/package-info.java new file mode 100644 index 000000000..ca64d5866 --- /dev/null +++ b/richtextfx/src/main/java/org/fxmisc/richtext/util/package-info.java @@ -0,0 +1,4 @@ +/** + * RichTextFX utilities package. + */ +package org.fxmisc.richtext.util; \ No newline at end of file