All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fxmisc.richtext.TextEditingArea Maven / Gradle / Ivy

The newest version!
package org.fxmisc.richtext;

import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.geometry.Bounds;
import javafx.scene.control.IndexRange;

import org.fxmisc.richtext.model.EditableStyledDocument;
import org.fxmisc.richtext.model.Paragraph;
import org.fxmisc.richtext.model.PlainTextChange;
import org.fxmisc.richtext.model.RichTextChange;
import org.fxmisc.richtext.model.SegmentOps;
import org.fxmisc.richtext.model.StyledDocument;
import org.reactfx.EventStream;
import org.reactfx.SuspendableNo;
import org.reactfx.value.Var;

import java.util.List;
import java.util.Locale;
import java.util.Optional;

/**
 * Interface for a text editing control.
 *
 * Defines the core methods. Other interfaces define default
 * higher-level methods implemented on top of the core methods.
 *
 * @param  type of style that can be applied to text.
 */
public interface TextEditingArea {

    /*******************
     *                 *
     *   Observables   *
     *                 *
     *******************/

    /**
     * The number of Unicode code units in this text-editing area.
     */
    default int getLength() { return lengthProperty().getValue(); }
    ObservableValue lengthProperty();

    /**
     * This is used to determine word and sentence breaks while navigating or selecting.
     * Override this method if your paragraph or text style accommodates Locales as well.
     */
    default Locale getLocale() { return Locale.getDefault(); }

    /**
     * Text content of this text-editing area.
     */
    default String getText() { return textProperty().getValue(); }
    ObservableValue textProperty();

    /**
     * Rich-text content of this text-editing area.
     * The returned document is immutable, it does not reflect
     * subsequent edits of this text-editing area.
     */
    StyledDocument getDocument();

    /**
     * The underlying document of this area that can be displayed by multiple {@code StyledTextArea}s.
     */
    EditableStyledDocument getContent();

    /**
     * Returns the object used for operating over {@link SEG segments} and their styles
     */
    SegmentOps getSegOps();

    /**
     * The current position of the caret, as a character offset in the text.
     *
     * Most of the time, caret is at the boundary of the selection (if there
     * is any selection). However, there are circumstances when the caret is
     * positioned inside or outside the selected text. For example, when the
     * user is dragging the selected text, the caret moves with the cursor
     * to point at the position where the selected text moves upon release.
     */
    default int getCaretPosition() { return getCaretSelectionBind().getPosition(); }
    default ObservableValue caretPositionProperty() { return getCaretSelectionBind().positionProperty(); }

    /**
     * Index of the current paragraph, i.e. the paragraph with the caret.
     */
    default int getCurrentParagraph() { return getCaretSelectionBind().getParagraphIndex(); }
    default ObservableValue currentParagraphProperty() { return getCaretSelectionBind().paragraphIndexProperty(); }

    /**
     * The caret position within the current paragraph.
     */
    default int getCaretColumn() { return getCaretSelectionBind().getColumnPosition(); }
    default ObservableValue caretColumnProperty() { return getCaretSelectionBind().columnPositionProperty(); }

    /**
     * Gets the bounds of the caret in the Screen's coordinate system or {@link Optional#empty()}
     * if caret is not visible in the viewport.
     */
    default Optional getCaretBounds() { return getCaretSelectionBind().getCaretBounds(); }
    default ObservableValue> caretBoundsProperty() { return getCaretSelectionBind().caretBoundsProperty(); }

    /**
     * Indicates when this text area should display a caret.
     */
    default Caret.CaretVisibility getShowCaret() { return getCaretSelectionBind().getShowCaret(); }
    default void setShowCaret(Caret.CaretVisibility value) { getCaretSelectionBind().setShowCaret(value); }
    default Var showCaretProperty() { return getCaretSelectionBind().showCaretProperty(); }

    /**
     * Gets the area's main {@link CaretSelectionBind}.
     */
    CaretSelectionBind getCaretSelectionBind();

    /**
     * The anchor of the selection.
     * If there is no selection, this is the same as caret position.
     */
    default int getAnchor() { return getCaretSelectionBind().getAnchorPosition(); }
    default ObservableValue anchorProperty() { return getCaretSelectionBind().anchorPositionProperty(); }

    /**
     * The selection range.
     *
     * One boundary is always equal to anchor, and the other one is most
     * of the time equal to caret position.
     */
    default IndexRange getSelection() { return getCaretSelectionBind().getRange(); }
    default ObservableValue selectionProperty() { return getCaretSelectionBind().rangeProperty(); }

    /**
     * The selected text.
     */
    default String getSelectedText() { return getCaretSelectionBind().getSelectedText(); }
    default ObservableValue selectedTextProperty() { return getCaretSelectionBind().selectedTextProperty(); }

    /**
     * Gets the bounds of the selection in the Screen's coordinate system if something is selected and visible in the
     * viewport or {@link Optional#empty()} if selection is not visible in the viewport.
     */
    default Optional getSelectionBounds() { return getCaretSelectionBind().getSelectionBounds(); }
    default ObservableValue> selectionBoundsProperty() { return getCaretSelectionBind().selectionBoundsProperty(); }

    /**
     * Unmodifiable observable list of paragraphs in this text area.
     */
    ObservableList> getParagraphs();
    default Paragraph getParagraph(int index) { return getParagraphs().get(index); }
    default int getParagraphLength(int index) { return getParagraph(index).length(); }

    /**
     * True when an update to the area's {@link #getContent() underling editable document} is still occurring
     * or the viewport is being updated.
     */
    SuspendableNo beingUpdatedProperty();
    default boolean isBeingUpdated() { return beingUpdatedProperty().get(); }

    /*********************
     *                   *
     *   Event streams   *
     *                   *
     *********************/

    /**
     * See {@link org.fxmisc.richtext.model.EditableStyledDocument#multiPlainChanges()}
     */
    EventStream> multiPlainChanges();

    /**
     * See {@link org.fxmisc.richtext.model.EditableStyledDocument#plainChanges()}
     */
    EventStream plainTextChanges();

    /**
     * See {@link org.fxmisc.richtext.model.EditableStyledDocument#multiRichChanges()}
     */
    EventStream>> multiRichChanges();

    /**
     * See {@link org.fxmisc.richtext.model.EditableStyledDocument#richChanges()}
     */
    EventStream> richChanges();


    /***************
     *             *
     *   Queries   *
     *             *
     ***************/

    /**
     * Returns text content of the given paragraph.
     */
    String getText(int paragraphIndex);

    /**
     * Returns text content of the given character range.
     */
    String getText(int start, int end);

    /**
     * Returns text content of the given character range.
     */
    String getText(IndexRange range);

    /**
     * Returns text content of the given character range.
     *
     * 

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

*/ default String getText(int startParagraph, int startColumn, int endParagraph, int endColumn) { int start = getAbsolutePosition(startParagraph, startColumn); int end = getAbsolutePosition(endParagraph, endColumn); return getText(start, end); } /** * Returns rich-text content of the given paragraph. */ StyledDocument subDocument(int paragraphIndex); /** * Returns rich-text content of the given character range. */ default StyledDocument subDocument(IndexRange range) { return subDocument(range.getStart(), range.getEnd()); }; /** * Returns rich-text content of the given character range. */ StyledDocument subDocument(int start, int end); /** * Returns rich-text content of the given character range. * *

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

*/ default StyledDocument subDocument(int startParagraph, int startColumn, int endParagraph, int endColumn) { int start = getAbsolutePosition(startParagraph, startColumn); int end = getAbsolutePosition(endParagraph, endColumn); return subDocument(start, end); } /** * Returns the selection range in the given paragraph. Note: this method will return * {@code IndexRange(start, paragraph.length() + 1)} when the selection includes a newline character. */ default IndexRange getParagraphSelection(int paragraph) { return getParagraphSelection(getCaretSelectionBind(), paragraph); } public IndexRange getParagraphSelection(Selection selection, int paragraph); /*************** * * * Actions * * * ***************/ /** * Positions the anchor and caretPosition explicitly, * effectively creating a selection. */ default void selectRange(int anchor, int caretPosition) { getCaretSelectionBind().selectRangeExpl(anchor, caretPosition); } /** * Positions the anchor and caretPosition explicitly, * effectively creating a selection. * *

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

*/ default void selectRange(int anchorParagraph, int anchorColumn, int caretPositionParagraph, int caretPositionColumn) { int anchor = getAbsolutePosition(anchorParagraph, anchorColumn); int caretPosition = getAbsolutePosition(caretPositionParagraph, caretPositionColumn); selectRange(anchor, caretPosition); } /** * Displaces the caret from the selection by positioning only the caret to the new location without * also affecting the selection's {@link #getAnchor() anchor} or the {@link #getSelection() selection}. * Do not confuse this method with {@link NavigationActions#moveTo(int)}, which is the normal way of moving the * caret. This method can be used to achieve the special case of positioning the caret outside or inside the * selection, as opposed to always being at the boundary. Use with care. */ public void displaceCaret(int pos); /** * Replaces a range of characters with the given text. * * It must hold {@code 0 <= start <= end <= getLength()}. * * @param start Start index of the range to replace, inclusive. * @param end End index of the range to replace, exclusive. * @param text The text to put in place of the deleted range. * It must not be null. */ void replaceText(int start, int end, String text); /** * Replaces a range of characters with the given text. * * It must hold {@code 0 <= start <= end <= getLength()} where * {@code start = getAbsolutePosition(startParagraph, startColumn);} and is inclusive, and * {@code int end = getAbsolutePosition(endParagraph, endColumn);} and is exclusive. * *

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

* * @param text The text to put in place of the deleted range. * It must not be null. */ default void replaceText(int startParagraph, int startColumn, int endParagraph, int endColumn, String text) { int start = getAbsolutePosition(startParagraph, startColumn); int end = getAbsolutePosition(endParagraph, endColumn); replaceText(start, end, text); } /** * Replaces a range of characters with the given rich-text document. */ void replace(int start, int end, StyledDocument replacement); /** * Starts building a list of changes to be used to update multiple portions of the underlying document * in one call. To execute the changes, call {@link MultiChangeBuilder#commit()}. If the number of * changes are known at compile time, use {@link #createMultiChange(int)} for better memory efficiency. */ MultiChangeBuilder createMultiChange(); /** * Same as {@link #createMultiChange()} but the number of changes are specified to be more memory efficient. */ MultiChangeBuilder createMultiChange(int numOfChanges); /** * Replaces a range of characters with the given segment. * * It must hold {@code 0 <= start <= end <= getLength()}. * * @param start Start index of the range to replace, inclusive. * @param end End index of the range to replace, exclusive. * @param seg The seg to put in place of the deleted range. * It must not be null. */ void replace(int start, int end, SEG seg, S style); /** * Replaces a range of characters with the given segment. * * It must hold {@code 0 <= start <= end <= getLength()} where * {@code start = getAbsolutePosition(startParagraph, startColumn);} and is inclusive, and * {@code int end = getAbsolutePosition(endParagraph, endColumn);} and is exclusive. * *

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

* * @param seg The segment to put in place of the deleted range. * It must not be null. */ default void replace(int startParagraph, int startColumn, int endParagraph, int endColumn, SEG seg, S style) { int start = getAbsolutePosition(startParagraph, startColumn); int end = getAbsolutePosition(endParagraph, endColumn); replace(start, end, seg, style); } /** * Replaces a range of characters with the given rich-text document. * *

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

*/ default void replace(int startParagraph, int startColumn, int endParagraph, int endColumn, StyledDocument replacement) { int start = getAbsolutePosition(startParagraph, startColumn); int end = getAbsolutePosition(endParagraph, endColumn); replace(start, end, replacement); } /** * Replaces a range of characters with the given text. * * @param range The range to replace. It must not be null. * @param text The text to put in place of the deleted range. * It must not be null. * * @see #replaceText(int, int, String) */ default void replaceText(IndexRange range, String text) { replaceText(range.getStart(), range.getEnd(), text); } /** * Replaces a range of characters with the given seg. * * @param range The range to replace. It must not be null. * @param seg The segment to put in place of the deleted range. * It must not be null. * * @see #replace(int, int, Object, Object) */ default void replace(IndexRange range, SEG seg, S style) { replace(range.getStart(), range.getEnd(), seg, style); } /** * Equivalent to * {@code replace(range.getStart(), range.getEnd(), replacement)}. */ default void replace(IndexRange range, StyledDocument replacement) { replace(range.getStart(), range.getEnd(), replacement); } /** * Returns the absolute position (i.e. the spot in-between characters) to the left of the given column in the given paragraph. * *

For example, given a text with only one line {@code "text"} and the columnIndex value of {@code 1}, "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 columnIndex 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) */ int getAbsolutePosition(int paragraphIndex, int columnIndex); /** * Disposes this area, preventing memory leaks. */ public void dispose(); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy