org.fxmisc.richtext.CaretSelectionBind Maven / Gradle / Ivy
package org.fxmisc.richtext;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.IndexRange;
import org.fxmisc.richtext.model.StyledDocument;
import java.text.BreakIterator;
/**
* An object for encapsulating a caret and a selection in a given area where the caret and selection are bound
* to one another.
*
* In other words, selecting some range in the area (via {@link #selectRangeExpl(int, int)},
* {@link #selectRange(int, int, int, int)}) will move the caret in the same call, except if one uses
* {@link #displaceSelection(int, int)}. Updating the selection via any method with an "update" prefix from
* {@link Selection} will also move the caret if it has been {@link #displaceCaret(int) displaced} or the anchor is
* not the boundary (i.e. the selection's the start or end position) that is being updated.
*
* Likewise, moving the caret via the "move" prefix methods from {@link Caret}
* will clear the selection in the same call, except if one uses {@link #displaceCaret(int)}.
*
*
* "Position" refers to the place in-between characters. In other words, every {@code "|"} in
* {@code "|t|e|x|t|"} is a valid position. There are two kinds of positions used here:
*
* -
* {@link #getStartPosition()}/{@link #getEndPosition()}, which refers to a position somewhere in the
* entire area's content. It's bounds are {@code 0 <= x <= area.getLength()}.
*
* -
* {@link #getStartColumnPosition()}/{@link #getEndColumnPosition()}, which refers to a position
* somewhere in the current paragraph. It's bounds are {@code 0 <= x <= area.getParagraphLength(index)}.
*
*
*
* Note: when parameter names are "position" without the "column" prefix, they refer to the position in the entire area.
*
*
* The selection is typically made using the {@link #getAnchorPosition() anchor's position} and
* the underlying {@link Caret#getPosition() caret's position}. Hence, {@link #selectRangeExpl(int, int)}
* is the typical method to use, although {@link #selectRange(int, int)} can also be used.
*
*
*
* For type safety, {@link #getSelectedDocument()} requires the same generic types from {@link StyledDocument}.
* This means that one must write a lot of boilerplate for the generics:
* {@code CaretSelectionBind, StyledText>, Collection> selection}.
* However, this is only necessary if one is using {@link #getSelectedDocument()} or
* {@link #selectedDocumentProperty()}. If you are not going to use the "selectedDocument" getter or property,
* then just write the much simpler {@code CaretSelectionBind, ?, ?> selection}.
*
*
* @see Caret
* @see Selection
*
* @param type for {@link StyledDocument}'s paragraph style; only necessary when using the "selectedDocument"
* getter or property
* @param type for {@link StyledDocument}'s segment type; only necessary when using the "selectedDocument"
* getter or property
* @param type for {@link StyledDocument}'s segment style; only necessary when using the "selectedDocument"
* getter or property
*/
public interface CaretSelectionBind extends Selection, Caret {
CaretNode getUnderlyingCaret();
Selection getUnderlyingSelection();
/* ********************************************************************** *
* *
* Observables *
* *
* Observables are "dynamic" (i.e. changing) characteristics of this *
* control. They are not directly settable by the client code, but change *
* in response to user input and/or API actions. *
* *
* ********************************************************************** */
int getAnchorPosition();
ObservableValue anchorPositionProperty();
int getAnchorParIndex();
ObservableValue anchorParIndexProperty();
int getAnchorColPosition();
ObservableValue anchorColPositionProperty();
/* ********************************************************************** *
* *
* Actions *
* *
* Actions change the state of this control. They typically cause a *
* change of one or more observables and/or produce an event. *
* *
* ********************************************************************** */
// caret
/**
* Moves the caret to the given position in the text and clears any selection.
*/
@Override
default void moveTo(int position) {
moveTo(position, NavigationActions.SelectionPolicy.CLEAR);
}
/**
* Moves the caret to the position returned from
* {@link TextEditingArea#getAbsolutePosition(int, int)}
* and clears any selection.
*/
@Override
default void moveTo(int paragraphIndex, int columnPosition) {
moveTo(paragraphIndex, columnPosition, NavigationActions.SelectionPolicy.CLEAR);
}
/**
* Moves the caret to the beginning of the current paragraph and clears any selection
*/
@Override
default void moveToParStart() {
moveToParStart(NavigationActions.SelectionPolicy.CLEAR);
}
/**
* Moves the caret to the end of the current paragraph and clears any selection
*/
@Override
default void moveToParEnd() {
moveToParEnd(NavigationActions.SelectionPolicy.CLEAR);
}
/**
* Moves the caret to the beginning of the area and clears any selection
*/
@Override
default void moveToAreaStart() {
moveToAreaStart(NavigationActions.SelectionPolicy.CLEAR);
}
/**
* Moves the caret to the end of the area and clears any selection
*/
@Override
default void moveToAreaEnd() {
moveToAreaEnd(NavigationActions.SelectionPolicy.CLEAR);
}
/**
* Moves the caret backward one char in the text and clears any selection
*/
@Override
default void moveToPrevChar() {
moveToPrevChar(NavigationActions.SelectionPolicy.CLEAR);
}
/**
* Moves the caret forward one char in the text and clears any selection
*/
@Override
default void moveToNextChar() {
moveToNextChar(NavigationActions.SelectionPolicy.CLEAR);
}
/**
* Moves the caret forwards by the number of breaks and clears any selection
*/
@Override
void moveBreaksForwards(int numOfBreaks, BreakIterator breakIterator);
/**
* Moves the caret backwards by the number of breaks and clears any selection
*/
@Override
void moveBreaksBackwards(int numOfBreaks, BreakIterator breakIterator);
// selection
/**
* Selects the given range and moves the caret to either the start of the new selection if the anchor was equal to
* the original start or the end if the anchor was equal to the original end
*
* Caution: see {@link TextEditingArea#getAbsolutePosition(int, int)} to
* know how the column index argument can affect the returned position.
*/
@Override
void selectRange(int startParagraphIndex, int startColPosition, int endParagraphIndex, int endColPosition);
/**
* Selects the given range and moves the caret to either the start of the new selection if the anchor was equal to
* the original start or the end if the anchor was equal to the original end
*/
@Override
void selectRange(int startPosition, int endPosition);
@Override
void updateStartBy(int amount, Direction direction);
@Override
void updateEndBy(int amount, Direction direction);
@Override
void updateStartTo(int position);
@Override
void updateStartTo(int paragraphIndex, int columnPosition);
@Override
void updateStartByBreaksForward(int numOfBreaks, BreakIterator breakIterator);
@Override
void updateStartByBreaksBackward(int numOfBreaks, BreakIterator breakIterator);
@Override
void updateEndTo(int position);
@Override
void updateEndTo(int paragraphIndex, int columnPosition);
@Override
void updateEndByBreaksForward(int numOfBreaks, BreakIterator breakIterator);
@Override
void updateEndByBreaksBackward(int numOfBreaks, BreakIterator breakIterator);
/**
* Selects everything in the area and moves the caret to either the start of the new selection if the anchor was
* equal to the original start or the end if the anchor was equal to the original end
*/
@Override
void selectAll();
/**
* Selects the given paragraph and moves the caret to either the start of the new selection if the anchor was
* equal to the original start or the end if the anchor was equal to the original end
*/
@Override
void selectParagraph(int paragraphIndex);
/**
* Selects the closest word to the given position in the area and moves the caret to either the start of the new
* selection if the anchor was equal to the original start or the end if the anchor was equal to the original end
*/
@Override
void selectWord(int wordPositionInArea);
/**
* Clears the selection while keeping the caret position the same.
*/
@Override
void deselect();
// caret selection bind
/**
* Positions the anchor and caretPosition explicitly,
* effectively creating a selection.
*/
void selectRangeExpl(int anchorParagraph, int anchorColumn, int caretParagraph, int caretColumn);
/**
* Positions the anchor and caretPosition explicitly,
* effectively creating a selection.
*/
void selectRangeExpl(int anchorPosition, int caretPosition);
/**
* 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.
*/
default void moveTo(int pos, NavigationActions.SelectionPolicy selectionPolicy) {
switch(selectionPolicy) {
case CLEAR:
selectRange(pos, pos);
break;
case ADJUST:
selectRange(getAnchorPosition(), pos);
break;
case EXTEND:
IndexRange sel = getRange();
int anchor;
if(pos <= sel.getStart())
anchor = sel.getEnd();
else if(pos >= sel.getEnd())
anchor = sel.getStart();
else
anchor = getAnchorPosition();
selectRangeExpl(anchor, pos);
break;
}
}
/**
* Moves the caret to the position returned from
* {@link TextEditingArea#getAbsolutePosition(int, int)}.
*/
void moveTo(int paragraphIndex, int columnIndex, NavigationActions.SelectionPolicy selectionPolicy);
/**
* 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.
*/
void moveToPrevChar(NavigationActions.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.
*/
void moveToNextChar(NavigationActions.SelectionPolicy selectionPolicy);
void moveToParStart(NavigationActions.SelectionPolicy selectionPolicy);
void moveToParEnd(NavigationActions.SelectionPolicy selectionPolicy);
void moveToAreaStart(NavigationActions.SelectionPolicy selectionPolicy);
void moveToAreaEnd(NavigationActions.SelectionPolicy selectionPolicy);
default void selectParagraph() {
int parStartPosition = getPosition() - getColumnPosition();
int parEndPosition = parStartPosition + getArea().getParagraphLength(getParagraphIndex());
selectRange(parStartPosition, parEndPosition);
}
/**
* Selects the word closest to the caret
*/
default void selectWord() {
selectWord(getPosition());
};
/**
* Displaces the caret from the selection by positioning only the caret to the new location without
* also affecting the selection's {@link #getAnchorPosition()} anchor} or the {@link #getRange()} selection}.
* Do not confuse this method with {@link #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.
*/
void displaceCaret(int position);
/**
* Displaces the caret from the selection by positioning only the selection to the new location without
* also affecting the {@link #getPosition() caret's position}.
* This method can be used to achieve the special case of selecting some range in the area without affecting the
* caret's position. Use with care.
*/
void displaceSelection(int startPosition, int endPosition);
}