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

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

The newest version!
package org.fxmisc.richtext;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.control.ContextMenu;
import javafx.scene.input.MouseEvent;
import javafx.scene.text.TextFlow;

import org.fxmisc.richtext.model.Paragraph;
import org.fxmisc.richtext.event.MouseOverTextEvent;
import org.reactfx.EventStream;
import org.reactfx.collection.LiveList;
import org.reactfx.value.Val;
import org.reactfx.value.Var;

import java.time.Duration;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.IntFunction;

/**
 * Specifies view-related API for a {@link TextEditingArea}
 *
 * @param  paragraph style type
 * @param  segment type
 * @param  segment style type
 */
public interface ViewActions {

    /**
     * Indicates whether this text area can be edited by the user.
     * Note that this property doesn't affect editing through the API.
     */
    default boolean isEditable() { return editableProperty().get(); }
    default void setEditable(boolean value) { editableProperty().set(value); }
    BooleanProperty editableProperty();

    /**
     * When a run of text exceeds the width of the text region,
     * then this property indicates whether the text should wrap
     * onto another line.
     */
    default boolean isWrapText() { return wrapTextProperty().get(); }
    default void setWrapText(boolean value) { wrapTextProperty().set(value); }
    BooleanProperty wrapTextProperty();

    /**
     * Defines how long the mouse has to stay still over the text before a
     * {@link MouseOverTextEvent} of type {@code MOUSE_OVER_TEXT_BEGIN} is
     * fired on this text area. When set to {@code null}, no
     * {@code MouseOverTextEvent}s are fired on this text area.
     *
     * 

Default value is {@code null}. */ default Duration getMouseOverTextDelay() { return mouseOverTextDelayProperty().get(); } default void setMouseOverTextDelay(Duration delay) { mouseOverTextDelayProperty().set(delay); } ObjectProperty mouseOverTextDelayProperty(); /** * Indicates whether area should auto scroll towards a {@link MouseEvent#MOUSE_DRAGGED} event. This can be * used when additional drag behavior is added on top of the area's default drag behavior and one does not * want this auto scroll feature to occur. This flag should be set to the correct value before the end of * the process InputMap. */ default boolean isAutoScrollOnDragDesired() { return autoScrollOnDragDesiredProperty().get(); } default void setAutoScrollOnDragDesired(boolean val) { autoScrollOnDragDesiredProperty().set(val); } BooleanProperty autoScrollOnDragDesiredProperty(); /** * Runs the EventHandler when the user pressed the mouse over unselected text within the area. * *

By default, this will {@link NavigationActions#moveTo(int) move the caret} to the position where * the mouse was pressed and clear out any selection via the code: *


     *     e -> {
     *         CharacterHit hit = hit(e.getX(), e.getY());
     *         moveTo(hit.getInsertionIndex(), SelectionPolicy.CLEAR);
     *     }
     * 
. */ void setOnOutsideSelectionMousePressed(EventHandler handler); ObjectProperty> onOutsideSelectionMousePressedProperty(); EventHandler getOnOutsideSelectionMousePressed(); /** * Runs the EventHandler when the mouse is released in this scenario: the user has selected some text and then * "clicked" the mouse somewhere in that selection (the use pressed the mouse, did not drag it, * and released the mouse). Note: this consumer is run on {@link MouseEvent#MOUSE_RELEASED}, * not {@link MouseEvent#MOUSE_CLICKED}. * *

By default, this will {@link NavigationActions#moveTo(int) move the caret} to the position where * the mouse was clicked and clear out any selection via the code: *


     *     e -> {
     *         CharacterHit hit = hit(e.getX(), e.getY());
     *         moveTo(hit.getInsertionIndex(), SelectionPolicy.CLEAR);
     *     }
     * 
. */ void setOnInsideSelectionMousePressReleased(EventHandler handler); ObjectProperty> onInsideSelectionMousePressReleasedProperty(); EventHandler getOnInsideSelectionMousePressReleased(); /** * Runs the consumer when the mouse is dragged in this scenario: the user has pressed the mouse over some * unselected text, and dragged the mouse to a new location within the area, but has not yet released the mouse. * Each time the user drags the mouse without releasing it, this hook's consumer gets called. * *

By default, this will create a new selection or * {@link NavigationActions.SelectionPolicy#ADJUST} the current one to be bigger or * smaller via the code: *


     *     e -> {
     *         CharacterHit hit = hit(e.getX(), e.getY());
     *         moveTo(hit.getInsertionIndex(), SelectionPolicy.ADJUST);
     *     }
     * 
. */ default Consumer getOnNewSelectionDrag() { return onNewSelectionDragProperty().get(); } default void setOnNewSelectionDrag(Consumer consumer) { onNewSelectionDragProperty().set(consumer); } ObjectProperty> onNewSelectionDragProperty(); /** * Runs the EventHandler when the mouse is released in this scenario: the user has pressed the mouse over some * unselected text, and dragged the mouse to a new location within the area, and released the mouse. * *

By default, this will {@link NavigationActions.SelectionPolicy#ADJUST} the * current selection to be bigger or smaller via the code: *


     *     e -> {
     *         CharacterHit hit = hit(e.getX(), e.getY());
     *         moveTo(hit.getInsertionIndex(), SelectionPolicy.ADJUST);
     *     }
     * 
. */ void setOnNewSelectionDragFinished(EventHandler handler); ObjectProperty> onNewSelectionDragFinishedProperty(); EventHandler getOnNewSelectionDragFinished(); /** * Runs the consumer when the mouse is dragged in this scenario: the user has selected some text, * pressed the mouse on top of the selection, dragged it to a new location within the area, * but has not yet released the mouse. Each time the user drags the mouse without releasing it, * this hook's consumer gets called. * *

By default, this will {@link GenericStyledArea#displaceCaret(int) displace the caret} to that position * within the area via the code: *


     *     p -> {
     *         CharacterHit hit = hit(p.getX(), p.getY());
     *         displaceCaret(hit.getInsertionIndex());
     *     }
     * 
. */ default Consumer getOnSelectionDrag() { return onSelectionDragProperty().get(); } default void setOnSelectionDrag(Consumer consumer) { onSelectionDragProperty().set(consumer); } ObjectProperty> onSelectionDragProperty(); /** * Runs the EventHandler when the mouse is released in this scenario: the user has selected some text, * pressed the mouse on top of the selection, dragged it to a new location within the area, * and released the mouse within the area. * *

By default, this will relocate the selected text to the character index where the mouse * was released via the code: *


     *     e -> {
     *         CharacterHit hit = hit(e.getX(), e.getY());
     *         moveSelectedText(hit.getInsertionIndex());
     *     }
     * 
. */ void setOnSelectionDropped(EventHandler handler); ObjectProperty> onSelectionDroppedProperty(); EventHandler getOnSelectionDropped(); /** * Gets the function that maps a line index to a node that is positioned to the left of the first character * in a paragraph's text. Useful for toggle points or indicating the line's number via {@link LineNumberFactory}. */ default IntFunction getParagraphGraphicFactory() { return paragraphGraphicFactoryProperty().get(); } default void setParagraphGraphicFactory(IntFunction factory) { paragraphGraphicFactoryProperty().set(factory); } ObjectProperty> paragraphGraphicFactoryProperty(); /** Gets the {@link ContextMenu} for the area, which is by default null. */ default ContextMenu getContextMenu() { return contextMenuObjectProperty().get(); } default void setContextMenu(ContextMenu menu) { contextMenuObjectProperty().set(menu); } ObjectProperty contextMenuObjectProperty(); /** * Gets the horizontal amount in pixels by which to offset the {@link #getContextMenu()} when it is shown, * which has a default value of 2. */ default double getContextMenuXOffset() { return contextMenuXOffsetProperty().get(); } default void setContextMenuXOffset(double offset) { contextMenuXOffsetProperty().set(offset); } DoubleProperty contextMenuXOffsetProperty(); /** * Gets the vertical amount in pixels by which to offset the {@link #getContextMenu()} when it is shown, * which has a default value of 2. */ default double getContextMenuYOffset() { return contextMenuYOffsetProperty().get(); } default void setContextMenuYOffset(double offset) { contextMenuYOffsetProperty().set(offset); } DoubleProperty contextMenuYOffsetProperty(); /** * The estimated scrollX value. This can be set in order to scroll the content. * Value is only accurate when area does not wrap lines and uses the same font size * throughout the entire area. */ Var estimatedScrollXProperty(); /** * The estimated scrollY value. This can be set in order to scroll the content. * Value is only accurate when area does not wrap lines and uses the same font size * throughout the entire area. */ Var estimatedScrollYProperty(); /** * The estimated width of the entire document. Accurate when area does not wrap lines and * uses the same font size throughout the entire area. Value is only supposed to be set by * the skin, not the user. */ Val totalWidthEstimateProperty(); /** * The estimated height of the entire document. Accurate when area does not wrap lines and * uses the same font size throughout the entire area. Value is only supposed to be set by * the skin, not the user. */ Val totalHeightEstimateProperty(); /** * Returns an {@link EventStream} that emits a {@code null} value every time the viewport becomes dirty (e.g. * the viewport's width, height, scaleX, scaleY, estimatedScrollX, or estimatedScrollY values change) */ public EventStream viewportDirtyEvents(); /** * Gets the visible paragraphs, even the ones that are barely displayed. */ LiveList> getVisibleParagraphs(); /** * Gets the style applicator. */ BiConsumer getApplyParagraphStyle(); /* ********************************************************************** * * * * Queries * * * * Queries are parameterized observables. * * * * ********************************************************************** */ /** * Gets the height of the viewport and ignores the padding values added to the area. */ public double getViewportHeight(); /** * Maps a paragraph index from {@link TextEditingArea#getParagraphs()} into the index system of * {@link #getVisibleParagraphs()}. */ public Optional allParToVisibleParIndex(int allParIndex); /** * Maps a paragraph index from {@link #getVisibleParagraphs()} into the index system of * {@link TextEditingArea#getParagraphs()}. */ public int visibleParToAllParIndex(int visibleParIndex); /** * Returns the index of the first visible paragraph in the index system of {@link TextEditingArea#getParagraphs()}. */ default int firstVisibleParToAllParIndex() { return visibleParToAllParIndex(0); } /** * Returns the index of the last visible paragraph in the index system of {@link TextEditingArea#getParagraphs()}. */ default int lastVisibleParToAllParIndex() { return visibleParToAllParIndex(getVisibleParagraphs().size() - 1); } /** * Helpful for determining which letter is at point x, y: *
     *     {@code
     *     StyledTextArea area = // creation code
     *     area.addEventHandler(MouseEvent.MOUSE_PRESSED, (MouseEvent e) -> {
     *         CharacterHit hit = area.hit(e.getX(), e.getY());
     *         int characterPosition = hit.getInsertionIndex();
     *
     *         // move the caret to that character's position
     *         area.moveTo(characterPosition, SelectionPolicy.CLEAR);
     *     }}
     * 
*/ CharacterHit hit(double x, double y); /** * 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 int lineIndex(int paragraphIndex, int columnPosition); /** * Gets the number of lines a paragraph spans when {@link #isWrapText()} is true, or otherwise returns 1. * CAUTION: the underlying TextFlow does not immediately account for changes in the stage's width when the * paragraph in question is a multi-line paragraph and {@link #isWrapText() text wrap is on}. After calling * {@link javafx.stage.Stage#setWidth(double)}, it may take anywhere between 150-300 milliseconds for TextFlow * to account for this and return the correct line count for the given paragraph. Otherwise, it may return a * skewed number, such as the total number of characters on the line. */ int getParagraphLinesCount(int paragraphIndex); /** * Using the paragraph index of the "all paragraph" index system, returns the bounds of a caret on the * given paragraph or {@link Optional#empty()} if no caret is on that paragraph or the pragraph is not visible. */ public Optional getCaretBoundsOnScreen(T caret); /** * Gets the character bounds on screen * * @param from the start position * @param to the end position * @return the bounds or {@link Optional#empty()} if line is not visible or the range is only a newline character. */ Optional getCharacterBoundsOnScreen(int from, int to); /** * Returns the bounds of the paragraph if it is visible or {@link Optional#empty()} if it's not. * * The returned bounds object will always be within the bounds of the area. In other words, it takes * scrolling into account. Note: the bound's width will always equal the area's width, not necessarily * the paragraph's real width (if it's short and doesn't take up all of the area's provided horizontal space * or if it's long and spans outside of the area's width). * * @param visibleParagraphIndex the index in area's list of visible paragraphs. */ public Bounds getVisibleParagraphBoundsOnScreen(int visibleParagraphIndex); /** * Returns the bounds of the paragraph if it is visible or {@link Optional#empty()} if it's not. * * The returned bounds object will always be within the bounds of the area. In other words, it takes * scrolling into account. Note: the bound's width will always equal the area's width, not necessarily * the paragraph's real width (if it's short and doesn't take up all of the area's provided horizontal space * or if it's long and spans outside of the area's width). * * @param paragraphIndex the index in area's list of paragraphs (visible and invisible). */ public Optional getParagraphBoundsOnScreen(int paragraphIndex); /* ********************************************************************** * * * * Actions * * * * Actions change the state of this control. They typically cause a * * change of one or more observables and/or produce an event. * * * * ********************************************************************** */ /** * Shows the paragraph somewhere in the viewport. If the line is already visible, no noticeable change occurs. * If line is above the current view, it appears at the top of the viewport. If the line is below the current * view, it appears at the bottom of the viewport. */ void showParagraphInViewport(int paragraphIndex); /** * Lays out the viewport so that the paragraph is the first line (top) displayed in the viewport. Note: if * the given area does not have enough lines that follow the given line to span its entire height, the paragraph * may not appear at the very top of the viewport. Instead, it may simply be shown in the viewport. For example, * given an unwrapped area whose height could show 10 lines but whose content only has 3 lines, calling * {@code showParagraphAtTop(3)} would be no different than {@code showParagraphAtTop(1)}. */ void showParagraphAtTop(int paragraphIndex); /** * Lays out the viewport so that the paragraph is the last line (bottom) displayed in the viewport. Note: if * the given area does not have enough lines preceding the given line to span its entire height, the paragraph * may not appear at the very bottom of the viewport. Instead, it may appear towards the bottom of the viewport * with some extra space following it. For example, given an unwrapped area whose height could show 10 lines but * whose content only has 7 lines, calling {@code showParagraphAtBottom(1)} would be no different than calling * {@code showParagraphAtBottom(7)}. */ void showParagraphAtBottom(int paragraphIndex); /** * Lays out the viewport so that the given bounds (according to the paragraph's coordinate system) within * the given paragraph is visible in the viewport. */ void showParagraphRegion(int paragraphIndex, Bounds region); /** * If the caret is not visible within the area's view, the area will scroll so that caret * is visible in the next layout pass. Use this method when you wish to "follow the caret" * (i.e. auto-scroll to caret) after making a change (add/remove/modify area's segments). */ void requestFollowCaret(); /** * Move the caret to the start of either the line in a multi-line wrapped paragraph or the paragraph * in a single-line / non-wrapped paragraph * * @param policy use {@link NavigationActions.SelectionPolicy#CLEAR} when no selection is desired an * {@link NavigationActions.SelectionPolicy#ADJUST} when a selection from starting point * to the place to where the caret is moved is desired. */ void lineStart(NavigationActions.SelectionPolicy policy); /** * Move the caret to the end of either the line in a multi-line wrapped paragraph or the paragraph * in a single-line / non-wrapped paragraph * * @param policy use {@link NavigationActions.SelectionPolicy#CLEAR} when no selection is desired an * {@link NavigationActions.SelectionPolicy#ADJUST} when a selection from starting point * to the place to where the caret is moved is desired. */ void lineEnd(NavigationActions.SelectionPolicy policy); /** * Selects the current line of a multi-line paragraph. */ default void selectLine() { lineStart(NavigationActions.SelectionPolicy.CLEAR); lineEnd(NavigationActions.SelectionPolicy.ADJUST); }; /** * Moves caret to the previous page (i.e. page up) * * @param selectionPolicy use {@link NavigationActions.SelectionPolicy#CLEAR} when no selection is desired and * {@link NavigationActions.SelectionPolicy#ADJUST} when a selection from starting point * to the place to where the caret is moved is desired. */ void prevPage(NavigationActions.SelectionPolicy selectionPolicy); /** * Moves caret to the next page (i.e. page down) * * @param selectionPolicy use {@link NavigationActions.SelectionPolicy#CLEAR} when no selection is desired and * {@link NavigationActions.SelectionPolicy#ADJUST} when a selection from starting point * to the place to where the caret is moved is desired. */ void nextPage(NavigationActions.SelectionPolicy selectionPolicy); /** * Hides the area's context menu if it is not {@code null} and it is {@link ContextMenu#isShowing() showing}. */ default void hideContextMenu() { ContextMenu menu = getContextMenu(); if (menu != null && menu.isShowing()) { menu.hide(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy