org.reactfx.Subscription - java examples

Here are the examples of the java api org.reactfx.Subscription taken from open source projects. By voting up you can indicate which examples are most useful and appropriate.

45 Examples 7

19 View Complete Implementation : TextEditor.java
Copyright GNU General Public License v3.0
Author : andrescv
/**
 * Jupiter RISC-V code editor.
 */
public final clreplaced TextEditor extends CodeArea {

    /**
     * Last editor text
     */
    private String lastText;

    /**
     * Editor tab size
     */
    private String tabSize;

    /**
     * Auto indent option
     */
    private boolean autoIndent;

    /**
     * Highlighting subscription
     */
    private Subscription subscription;

    /**
     * tab size change listener
     */
    private ChangeListener<Number> tabListener;

    /**
     * font size change listener
     */
    private ChangeListener<Number> fszListener;

    /**
     * dark mode listener
     */
    private ChangeListener<Boolean> darkListener;

    /**
     * Creates a new text editor with a default text content.
     *
     * @param text text content
     */
    public TextEditor(String text) {
        super();
        // set tab size
        setTabSize();
        // set font size
        setFontSize();
        // set dark mode
        setDarkMode();
        // set text
        lastText = text;
        replaceText(0, 0, text);
        getUndoManager().forgetHistory();
        setStyleSpans(0, computeHighlighting());
        moveTo(0);
        requestFollowCaret();
        requestFocus();
        // line number
        setParagraphGraphicFactory(LineNumberFactory.get(this));
        // syntax highlighting
        subscription = multiPlainChanges().successionEnds(Duration.ofMillis(50)).subscribe(ignore -> setStyleSpans(0, computeHighlighting()));
        // set input maps
        Nodes.addInputMap(this, InputMap.sequence(// left arrow
        InputMap.consumeWhen(EventPattern.keyPressed(KeyCode.LEFT), () -> canMoveByTab(false), (e) -> moveTo(getCaretPosition() - tabSize.length())), // left keypad
        InputMap.consumeWhen(EventPattern.keyPressed(KeyCode.KP_LEFT), () -> canMoveByTab(false), (e) -> moveTo(getCaretPosition() - tabSize.length())), // right arrow
        InputMap.consumeWhen(EventPattern.keyPressed(KeyCode.RIGHT), () -> canMoveByTab(true), (e) -> moveTo(this.getCaretPosition() + tabSize.length())), // right keypad
        InputMap.consumeWhen(EventPattern.keyPressed(KeyCode.KP_RIGHT), () -> canMoveByTab(true), (e) -> moveTo(this.getCaretPosition() + tabSize.length())), // enter for auto-indent
        InputMap.consume(EventPattern.keyPressed(KeyCode.ENTER), (e) -> enter()), // backspace
        InputMap.consumeWhen(EventPattern.keyPressed(KeyCode.BACK_SPACE), () -> canMoveByTab(false), (e) -> backspace()), // tabs
        InputMap.consume(EventPattern.keyPressed(KeyCode.TAB), e -> replaceSelection(tabSize))));
        // scroll listener
        addEventFilter(ScrollEvent.SCROLL, e -> {
            if (e.isControlDown()) {
                if (e.getDeltaY() > 0)
                    Settings.incCodeFontSize();
                else if (e.getDeltaY() < 0)
                    Settings.decCodeFontSize();
                e.consume();
            }
        });
        // add bindings
        tabListener = (e, o, n) -> setTabSize();
        fszListener = (e, o, n) -> setFontSize();
        darkListener = (e, o, n) -> setDarkMode();
        Settings.CODE_FONT_SIZE.addListener(fszListener);
        Settings.TAB_SIZE.addListener(tabListener);
        Settings.DARK_MODE.addListener(darkListener);
        // undo option
        MenuItem undo = new MenuItem("Undo");
        undo.setOnAction(e -> undo());
        undo.setGraphic(Icons.get("undo"));
        // redo option
        MenuItem redo = new MenuItem("Redo");
        redo.setOnAction(e -> redo());
        redo.setGraphic(Icons.get("redo"));
        // cut
        MenuItem cut = new MenuItem("Cut");
        cut.setOnAction(e -> cut());
        cut.setGraphic(Icons.get("cut"));
        // copy
        MenuItem copy = new MenuItem("Copy");
        copy.setOnAction(e -> copy());
        copy.setGraphic(Icons.get("copy"));
        // paste
        MenuItem paste = new MenuItem("Paste");
        paste.setOnAction(e -> paste());
        paste.setGraphic(Icons.get("paste"));
        // select all
        MenuItem selectAll = new MenuItem("Select All");
        selectAll.setOnAction(e -> selectAll());
        selectAll.setGraphic(Icons.get("select"));
        ContextMenu menu = new ContextMenu();
        menu.gereplacedems().addAll(undo, redo, cut, copy, paste, selectAll);
        setContextMenu(menu);
    }

    /**
     * Creates a new empty text editor.
     */
    public TextEditor() {
        this("");
    }

    /**
     * Sets editor text and forgets undo/redo history.
     *
     * @param text editor text
     */
    public void load(String text) {
        lastText = text;
        replaceText(0, getText().length(), text);
        setStyleSpans(0, computeHighlighting());
        getUndoManager().forgetHistory();
        moveTo(0);
        requestFollowCaret();
        requestFocus();
    }

    /**
     * Mark current text as last text.
     */
    public void save() {
        lastText = getText();
    }

    /**
     * Closes text editor.
     */
    public void close() {
        subscription.unsubscribe();
        Settings.TAB_SIZE.removeListener(tabListener);
        Settings.CODE_FONT_SIZE.removeListener(fszListener);
        Settings.DARK_MODE.removeListener(darkListener);
    }

    /**
     * Verifies if the content of the text editor has changed.
     *
     * @return {@code true} if has changed, {@code false} if not
     */
    public boolean modified() {
        return !getText().equals(lastText);
    }

    /**
     * Handles enter event.
     */
    private void enter() {
        insertText(getCurrentParagraph(), getCaretColumn(), Data.EOL);
        if (Settings.AUTO_INDENT.get()) {
            String line = getParagraph(getCurrentParagraph() - 1).getText();
            String spaces = "";
            for (int i = 0; i < line.length(); i++) {
                if (line.charAt(i) == ' ') {
                    spaces += " ";
                } else {
                    break;
                }
            }
            insertText(getCurrentParagraph(), getCaretColumn(), spaces);
        }
        requestFollowCaret();
    }

    /**
     * Handles backspace event.
     */
    private void backspace() {
        int pos = getCaretPosition();
        replaceText(pos - tabSize.length(), pos, "");
    }

    /**
     * Sets editor tab size.
     */
    private void setTabSize() {
        tabSize = "";
        for (int i = 0; i < Settings.TAB_SIZE.get(); i++) {
            tabSize += " ";
        }
    }

    /**
     * Sets editor font size.
     */
    private void setFontSize() {
        setStyle(String.format("-fx-font-size: %d;", Settings.CODE_FONT_SIZE.get()));
    }

    /**
     * Sets editor dark mode.
     */
    private void setDarkMode() {
        pseudoClreplacedStateChanged(PseudoClreplaced.getPseudoClreplaced("dark"), Settings.DARK_MODE.get());
    }

    /**
     * Verifies if caret position can move by tab length.
     *
     * @param right if moving to the right
     * @return true if can move, false if not
     */
    private boolean canMoveByTab(boolean right) {
        int col = getCaretColumn();
        String line = getParagraph(getCurrentParagraph()).getText();
        int pos = 0;
        for (pos = 0; pos < line.length(); pos++) {
            if (line.charAt(pos) != ' ') {
                break;
            }
        }
        return (((!right && col > 0) || right) && ((right && col <= (pos - tabSize.length())) || (!right && col <= pos)) && (col % tabSize.length() == 0));
    }

    /**
     * Computes syntax highlighting.
     *
     * @return style spans of syntax theme clreplacedes
     */
    private StyleSpans<Collection<String>> computeHighlighting() {
        Token token;
        int current = 0;
        String text = getText();
        Lexer lexer = new Lexer(new StringReader(text));
        StyleSpansBuilder<Collection<String>> spansBuilder = new StyleSpansBuilder<>();
        try {
            while (!(token = lexer.yylex()).getStyle().equals("eof")) {
                current += token.getLength();
                spansBuilder.add(Collections.singleton(token.getStyle()), token.getLength());
            }
        } catch (Exception e) {
        }
        spansBuilder.add(Collections.emptyList(), this.getText().length() - current);
        return spansBuilder.create();
    }
}

19 View Complete Implementation : CellListManager.java
Copyright BSD 2-Clause "Simplified" License
Author : FXMisc
/**
 * Tracks all of the cells that the viewport can display ({@link #cells}) and which cells the viewport is currently
 * displaying ({@link #presentCells}).
 */
final clreplaced CellListManager<T, C extends Cell<T, ? extends Node>> {

    private final Node owner;

    private final CellPool<T, C> cellPool;

    private final MemoizationList<C> cells;

    private final LiveList<C> presentCells;

    private final LiveList<Node> cellNodes;

    private final Subscription presentCellsSubscription;

    public CellListManager(Node owner, ObservableList<T> items, Function<? super T, ? extends C> cellFactory) {
        this.owner = owner;
        this.cellPool = new CellPool<>(cellFactory);
        this.cells = LiveList.map(items, this::cellForItem).memoize();
        this.presentCells = cells.memoizedItems();
        this.cellNodes = presentCells.map(Cell::getNode);
        this.presentCellsSubscription = presentCells.observeQuasiModifications(this::presentCellsChanged);
    }

    public void dispose() {
        // return present cells to pool *before* unsubscribing,
        // because stopping to observe memoized items may clear memoized items
        presentCells.forEach(cellPool::acceptCell);
        presentCellsSubscription.unsubscribe();
        cellPool.dispose();
    }

    /**
     * Gets the list of nodes that the viewport is displaying
     */
    public ObservableList<Node> getNodes() {
        return cellNodes;
    }

    public MemoizationList<C> getLazyCellList() {
        return cells;
    }

    public boolean isCellPresent(int itemIndex) {
        return cells.isMemoized(itemIndex);
    }

    public C getPresentCell(int itemIndex) {
        // both getIfMemoized() and get() may throw
        return cells.getIfMemoized(itemIndex).get();
    }

    public Optional<C> getCellIfPresent(int itemIndex) {
        // getIfMemoized() may throw
        return cells.getIfMemoized(itemIndex);
    }

    public C getCell(int itemIndex) {
        return cells.get(itemIndex);
    }

    /**
     * Updates the list of cells to display
     *
     * @param fromItem the index of the first item to display
     * @param toItem the index of the last item to display
     */
    public void cropTo(int fromItem, int toItem) {
        fromItem = Math.max(fromItem, 0);
        toItem = Math.min(toItem, cells.size());
        cells.forget(0, fromItem);
        cells.forget(toItem, cells.size());
    }

    private C cellForItem(T item) {
        C cell = cellPool.getCell(item);
        // apply CSS when the cell is first added to the scene
        Node node = cell.getNode();
        EventStreams.nonNullValuesOf(node.sceneProperty()).subscribeForOne(scene -> {
            node.applyCss();
        });
        // Make cell initially invisible.
        // It will be made visible when it is positioned.
        node.setVisible(false);
        if (cell.isReusable()) {
            // if cell is reused i think adding event handler
            // would cause resource leakage.
            node.setOnScroll(this::pushScrollEvent);
            node.setOnScrollStarted(this::pushScrollEvent);
            node.setOnScrollFinished(this::pushScrollEvent);
        } else {
            node.addEventHandler(ScrollEvent.ANY, this::pushScrollEvent);
        }
        return cell;
    }

    /**
     * Push scroll events received by cell nodes directly to
     * the 'owner' Node. (Generally likely to be a VirtualFlow
     * but not required.)
     *
     * Normal bubbling of scroll events gets interrupted during
     * a scroll gesture when the Cell's Node receiving the event
     * has moved out of the viewport and is thus removed from
     * the Navigator's children list. This breaks expected trackpad
     * scrolling behaviour, at least on macOS.
     *
     * So here we take over event-bubbling duties for ScrollEvent
     * and push them ourselves directly to the given owner.
     */
    private void pushScrollEvent(ScrollEvent se) {
        owner.fireEvent(se);
        se.consume();
    }

    private void presentCellsChanged(QuasiListModification<? extends C> mod) {
        // add removed cells back to the pool
        for (C cell : mod.getRemoved()) {
            cellPool.acceptCell(cell);
        }
        // update indices of added cells and cells after the added cells
        for (int i = mod.getFrom(); i < presentCells.size(); ++i) {
            presentCells.get(i).updateIndex(cells.indexOfMemoizedItem(i));
        }
    }
}

19 View Complete Implementation : SizeTracker.java
Copyright BSD 2-Clause "Simplified" License
Author : FXMisc
/**
 * Estimates the size of the entire viewport (if it was actually completely rendered) based on the known sizes of the
 * {@link Cell}s whose nodes are currently displayed in the viewport and an estimated average of
 * {@link Cell}s whose nodes are not displayed in the viewport. The meaning of {@link #breadthForCells} and
 * {@link #totalLengthEstimate} are dependent upon which implementation of {@link OrientationHelper} is used.
 */
final clreplaced SizeTracker {

    private final OrientationHelper orientation;

    private final ObservableObjectValue<Bounds> viewportBounds;

    private final MemoizationList<? extends Cell<?, ?>> cells;

    private final MemoizationList<Double> breadths;

    private final Val<Double> maxKnownMinBreadth;

    /**
     * Stores either the greatest minimum cell's node's breadth or the viewport's breadth
     */
    private final Val<Double> breadthForCells;

    private final MemoizationList<Double> lengths;

    /**
     * Stores either null or the average length of the cells' nodes currently displayed in the viewport
     */
    private final Val<Double> averageLengthEstimate;

    private final Val<Double> totalLengthEstimate;

    private final Val<Double> lengthOffsetEstimate;

    private final Subscription subscription;

    /**
     * Constructs a SizeTracker
     *
     * @param orientation if vertical, breadth = width and length = height;
     *                    if horizontal, breadth = height and length = width
     */
    public SizeTracker(OrientationHelper orientation, ObservableObjectValue<Bounds> viewportBounds, MemoizationList<? extends Cell<?, ?>> lazyCells) {
        this.orientation = orientation;
        this.viewportBounds = viewportBounds;
        this.cells = lazyCells;
        this.breadths = lazyCells.map(orientation::minBreadth).memoize();
        this.maxKnownMinBreadth = breadths.memoizedItems().reduce(Math::max).orElseConst(0.0);
        this.breadthForCells = Val.combine(maxKnownMinBreadth, viewportBounds, (a, b) -> Math.max(a, orientation.breadth(b)));
        Val<Function<Cell<?, ?>, Double>> lengthFn = avoidFalseInvalidations(breadthForCells).map(breadth -> cell -> orientation.prefLength(cell, breadth));
        this.lengths = cells.mapDynamic(lengthFn).memoize();
        LiveList<Double> knownLengths = this.lengths.memoizedItems();
        Val<Double> sumOfKnownLengths = knownLengths.reduce((a, b) -> a + b).orElseConst(0.0);
        Val<Integer> knownLengthCount = knownLengths.sizeProperty();
        this.averageLengthEstimate = Val.create(() -> {
            // make sure to use pref lengths of all present cells
            for (int i = 0; i < cells.getMemoizedCount(); ++i) {
                int j = cells.indexOfMemoizedItem(i);
                lengths.force(j, j + 1);
            }
            int count = knownLengthCount.getValue();
            return count == 0 ? null : sumOfKnownLengths.getValue() / count;
        }, sumOfKnownLengths, knownLengthCount);
        this.totalLengthEstimate = Val.combine(averageLengthEstimate, cells.sizeProperty(), (avg, n) -> n * avg);
        Val<Integer> firstVisibleIndex = Val.create(() -> cells.getMemoizedCount() == 0 ? null : cells.indexOfMemoizedItem(0), cells, // need to observe cells.memoizedItems()
        cells.memoizedItems());
        // as well, because they may change without a change in cells.
        Val<? extends Cell<?, ?>> firstVisibleCell = cells.memoizedItems().collapse(visCells -> visCells.isEmpty() ? null : visCells.get(0));
        Val<Integer> knownLengthCountBeforeFirstVisibleCell = Val.create(() -> {
            return firstVisibleIndex.getOpt().map(i -> lengths.getMemoizedCountBefore(Math.min(i, lengths.size()))).orElse(0);
        }, lengths, firstVisibleIndex);
        Val<Double> totalKnownLengthBeforeFirstVisibleCell = knownLengths.reduceRange(knownLengthCountBeforeFirstVisibleCell.map(n -> new IndexRange(0, n)), (a, b) -> a + b).orElseConst(0.0);
        Val<Double> unknownLengthEstimateBeforeFirstVisibleCell = Val.combine(firstVisibleIndex, knownLengthCountBeforeFirstVisibleCell, averageLengthEstimate, (firstIdx, knownCnt, avgLen) -> (firstIdx - knownCnt) * avgLen);
        Val<Double> firstCellMinY = firstVisibleCell.flatMap(orientation::minYProperty);
        lengthOffsetEstimate = Val.combine(totalKnownLengthBeforeFirstVisibleCell, unknownLengthEstimateBeforeFirstVisibleCell, firstCellMinY, (a, b, minY) -> a + b - minY).orElseConst(0.0);
        // pinning totalLengthEstimate and lengthOffsetEstimate
        // binds it all together and enables memoization
        this.subscription = Subscription.multi(totalLengthEstimate.pin(), lengthOffsetEstimate.pin());
    }

    private static <T> Val<T> avoidFalseInvalidations(Val<T> src) {
        return new ValBase<T>() {

            @Override
            protected Subscription connect() {
                return src.observeChanges((obs, oldVal, newVal) -> invalidate());
            }

            @Override
            protected T computeValue() {
                return src.getValue();
            }
        };
    }

    public void dispose() {
        subscription.unsubscribe();
    }

    public Val<Double> maxCellBreadthProperty() {
        return maxKnownMinBreadth;
    }

    public double getViewportBreadth() {
        return orientation.breadth(viewportBounds.get());
    }

    public double getViewportLength() {
        return orientation.length(viewportBounds.get());
    }

    public Val<Double> averageLengthEstimateProperty() {
        return averageLengthEstimate;
    }

    public Optional<Double> getAverageLengthEstimate() {
        return averageLengthEstimate.getOpt();
    }

    public Val<Double> totalLengthEstimateProperty() {
        return totalLengthEstimate;
    }

    public Val<Double> lengthOffsetEstimateProperty() {
        return lengthOffsetEstimate;
    }

    public double breadthFor(int itemIndex) {
        replacedert cells.isMemoized(itemIndex);
        breadths.force(itemIndex, itemIndex + 1);
        return breadthForCells.getValue();
    }

    public void forgetSizeOf(int itemIndex) {
        breadths.forget(itemIndex, itemIndex + 1);
        lengths.forget(itemIndex, itemIndex + 1);
    }

    public double lengthFor(int itemIndex) {
        return lengths.get(itemIndex);
    }

    public double getCellLayoutBreadth() {
        return breadthForCells.getValue();
    }
}

19 View Complete Implementation : CaretNode.java
Copyright BSD 2-Clause "Simplified" License
Author : FXMisc
private void manageSubscription(Subscription s) {
    subscriptions = subscriptions.and(s);
}

19 View Complete Implementation : MouseStationaryHelper.java
Copyright BSD 2-Clause "Simplified" License
Author : FXMisc
/**
 * Helper clreplaced for setting up the code that will fire both kinds of {@link MouseStationaryEvent} when
 * these events occur.
 */
public clreplaced MouseStationaryHelper {

    private final Node node;

    private Subscription installed = null;

    /**
     * Creates a helper clreplaced 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<Either<Point2D, Void>> events(Duration delay) {
        EventStream<MouseEvent> mouseEvents = eventsOf(node, MouseEvent.ANY);
        EventStream<Point2D> stationaryPositions = mouseEvents.successionEnds(delay).filter(e -> e.getEventType() == MOUSE_MOVED).map(e -> new Point2D(e.getX(), e.getY()));
        EventStream<Void> stoppers = mouseEvents.supply((Void) null);
        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();
        }
        installed = events(delay).<Event>map(either -> either.unify(pos -> MouseStationaryEvent.beginAt(node.localToScreen(pos)), stop -> MouseStationaryEvent.end())).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();
            installed = null;
        }
    }
}

19 View Complete Implementation : SelectionImpl.java
Copyright BSD 2-Clause "Simplified" License
Author : FXMisc
private void manageSubscription(Subscription s) {
    subscription = subscription.and(s);
}

19 View Complete Implementation : XPathUpdateSubscriber.java
Copyright BSD 2-Clause "Simplified" License
Author : pmd
public abstract clreplaced XPathUpdateSubscriber implements ApplicationComponent {

    private final DesignerRoot root;

    private Subscription subscription = () -> {
    };

    public XPathUpdateSubscriber(DesignerRoot root) {
        this.root = root;
    }

    public Subscription init(ASTManager astManager) {
        MessageChannel<VersionedXPathQuery> service = root.getService(DesignerRoot.LATEST_XPATH);
        EventStream<?> merged = EventStreams.merge(astManager.compilationUnitProperty().values(), additionalTicks(), service.messageStream(true, this), astManager.ruleProperties().values().withDefaultEvent(Collections.emptyMap()));
        subscription = merged.subscribe(tick -> {
            Node compil = astManager.compilationUnitProperty().getOrElse(null);
            VersionedXPathQuery query = service.latestMessage().getOrElse(null);
            Map<String, String> props = astManager.ruleProperties().getOrElse(Collections.emptyMap());
            if (compil == null) {
                handleNoCompilationUnit();
                return;
            }
            if (query == null || StringUtils.isBlank(query.getExpression())) {
                handleNoXPath();
                return;
            }
            try {
                List<Node> results = XPathEvaluator.evaluateQuery(compil, astManager.languageVersionProperty().getValue(), query.getVersion(), query.getExpression(), props, query.getDefinedProperties());
                handleXPathSuccess(results);
            } catch (XPathEvaluationException e) {
                handleXPathError(e);
            }
        });
        return this::unsubscribe;
    }

    /**
     * Additional refresh ticks. By default, the changes of
     * {@link ASTManager#compilationUnitProperty()}, of the local XPath
     * query (local as in scoped by a {@link DesignerRoot}), and of
     * the {@link ASTManager#ruleProperties()} are taken into account.
     */
    public EventStream<?> additionalTicks() {
        return EventStreams.never();
    }

    @Override
    public DesignerRoot getDesignerRoot() {
        return root;
    }

    public abstract void handleNoXPath();

    public abstract void handleNoCompilationUnit();

    public abstract void handleXPathSuccess(List<Node> results);

    public abstract void handleXPathError(Exception e);

    public void unsubscribe() {
        subscription.unsubscribe();
        subscription = Subscription.EMPTY;
    }
}

19 View Complete Implementation : CheckBoxTreeCell.java
Copyright BSD 2-Clause "Simplified" License
Author : TomasMikula
public clreplaced CheckBoxTreeCell<C extends CheckBoxContent> extends TreeCell<C> {

    private final Recursive<BiConsumer<TreeItem<C>, CheckBoxContent.State>> UPDATE_DOWNWARDS = new Recursive<>();

    {
        UPDATE_DOWNWARDS.f = (item, state) -> {
            item.getValue().setState(state);
            item.getChildren().forEach(child -> UPDATE_DOWNWARDS.f.accept(child, state));
        };
    }

    private final CheckBox checkBox = new CheckBox();

    private final Function<C, String> stringConverter;

    private Var<CheckBoxContent.State> state;

    private Var<Boolean> select;

    private Subscription intermediateState;

    private final InvalidationListener stateInvalidations = (obs) -> {
        TreeItem<C> treeItem = getTreeItem();
        if (treeItem != null) {
            final TreeItem<C> parenreplacedem = treeItem.getParent();
            // do upward call first
            if (parenreplacedem != null) {
                CheckBoxContent value = parenreplacedem.getValue();
                if (value != null && !value.isLocked()) {
                    CheckBoxContent.State[] childrenStates = parenreplacedem.getChildren().stream().map(v -> v.getValue().getState()).distinct().toArray(CheckBoxContent.State[]::new);
                    /*
                        Due to `distinct()`,
                            if length > 1,
                                then children were 2+ of the 3 CheckBoxContent.State enum values
                                Thus, set to UNDEFINED
                            else
                                then children were all UNCHECKED or CHECKED.
                                Thus, set the current value to that State
                     */
                    value.setState(childrenStates.length > 1 ? CheckBoxContent.State.UNDEFINED : childrenStates[0]);
                }
            }
            // then do downward call
            C itemVal = treeItem.getValue();
            // when children's invalidation listeners are called, skip this item's update as it
            // was the one the initiated the call.
            itemVal.lock();
            CheckBoxContent.State state = itemVal.getState();
            if (state != CheckBoxContent.State.UNDEFINED) {
                treeItem.getChildren().forEach(child -> UPDATE_DOWNWARDS.f.accept(child, state));
            }
            // once finished, unlock so updates via one of its children will propogate through the tree
            itemVal.unlock();
        }
    };

    public CheckBoxTreeCell() {
        this((content) -> content.getPath().toString());
    }

    public CheckBoxTreeCell(Function<C, String> stringConverter) {
        super();
        this.stringConverter = stringConverter;
    }

    @Override
    protected void updateItem(C item, boolean empty) {
        super.updateItem(item, empty);
        if (empty || item == null) {
            setText(null);
            checkBox.setGraphic(null);
            setGraphic(null);
        } else {
            // update the text
            setText(stringConverter.apply(gereplacedem()));
            // update the graphic
            TreeItem<C> treeItem = getTreeItem();
            Node graphic = treeItem.getGraphic();
            checkBox.setGraphic(graphic != null ? graphic : null);
            setGraphic(checkBox);
            // unbind properties
            if (state != null) {
                checkBox.selectedProperty().unbindBidirectional(select);
                intermediateState.unsubscribe();
                state.removeListener(stateInvalidations);
            }
            // rebind properties
            state = treeItem.getValue().stateProperty();
            select = state.mapBidirectional(s -> s == CheckBoxContent.State.CHECKED, val -> val ? CheckBoxContent.State.CHECKED : CheckBoxContent.State.UNCHECKED);
            checkBox.selectedProperty().bindBidirectional(select);
            // using checkBox.intermediateProperty().bind(state.map(s -> s == UNDEFINED)); results in a
            // RunTimeException: a bounded property cannot be set
            // So, get around it by feeding state values into it.
            intermediateState = state.values().map(s -> s == CheckBoxContent.State.UNDEFINED).feedTo(checkBox.indeterminateProperty());
            state.addListener(stateInvalidations);
        }
    }
}

19 View Complete Implementation : FlatMapped.java
Copyright BSD 2-Clause "Simplified" License
Author : TomasMikula
abstract clreplaced FlatMapped<T, U, O extends ObservableValue<U>> extends ValBase<U> {

    final Val<O> src;

    // irrelevant when not connected
    private Subscription selectedSubscription = null;

    public FlatMapped(ObservableValue<T> src, Function<? super T, O> f) {
        this.src = Val.map(src, f);
    }

    @Override
    protected final Subscription connect() {
        return Val.observeInvalidations(src, obs -> srcInvalidated()).and(this::stopObservingSelected);
    }

    @Override
    protected final U computeValue() {
        if (isObservingInputs()) {
            startObservingSelected();
        }
        return src.getOpt().map(O::getValue).orElse(null);
    }

    private void startObservingSelected() {
        replacedert isObservingInputs();
        if (selectedSubscription == null) {
            src.ifPresent(sel -> {
                selectedSubscription = Val.observeInvalidations(sel, obs -> selectedInvalidated());
            });
        }
    }

    private void stopObservingSelected() {
        if (selectedSubscription != null) {
            selectedSubscription.unsubscribe();
            selectedSubscription = null;
        }
    }

    private void selectedInvalidated() {
        invalidate();
    }

    private void srcInvalidated() {
        stopObservingSelected();
        invalidate();
    }
}

19 View Complete Implementation : OrElse.java
Copyright BSD 2-Clause "Simplified" License
Author : TomasMikula
@Override
protected Subscription connect() {
    trySrc = true;
    Subscription sub1 = Val.observeInvalidations(src, obs -> {
        trySrc = true;
        invalidate();
    });
    Subscription sub2 = Val.observeInvalidations(other, obs -> {
        if (!trySrc) {
            invalidate();
        }
    });
    return sub1.and(sub2);
}

19 View Complete Implementation : FlatMapTest.java
Copyright BSD 2-Clause "Simplified" License
Author : TomasMikula
@Test
public void lazinessTest() {
    SimpleVar<A> base = (SimpleVar<A>) Var.<A>newSimpleVar(null);
    Val<B> flatMapped = base.flatMap(a -> a.b);
    Var<String> selected = flatMapped.selectVar(b -> b.s);
    replacedertFalse(base.isObservingInputs());
    A a = new A();
    B b = new B();
    a.b.setValue(b);
    base.setValue(a);
    replacedertFalse(base.isObservingInputs());
    replacedertFalse(a.b.isObservingInputs());
    replacedertFalse(b.s.isObservingInputs());
    Subscription sub = selected.pin();
    replacedertTrue(base.isObservingInputs());
    replacedertTrue(a.b.isObservingInputs());
    replacedertTrue(b.s.isObservingInputs());
    B b2 = new B();
    a.b.setValue(b2);
    // stopped observing b.s
    replacedertFalse(b.s.isObservingInputs());
    replacedertTrue(base.isObservingInputs());
    replacedertTrue(a.b.isObservingInputs());
    // no need to observe b2.s yet
    replacedertFalse(b2.s.isObservingInputs());
    selected.setValue("Y");
    // still no need to observe b2.s
    replacedertFalse(b2.s.isObservingInputs());
    selected.getValue();
    // now we have to observe b2.s for invalidations
    replacedertTrue(b2.s.isObservingInputs());
    sub.unsubscribe();
    replacedertFalse(base.isObservingInputs());
    replacedertFalse(a.b.isObservingInputs());
    replacedertFalse(b2.s.isObservingInputs());
    replacedertFalse(((ValBase<B>) flatMapped).isObservingInputs());
}

19 View Complete Implementation : OrElseTest.java
Copyright BSD 2-Clause "Simplified" License
Author : TomasMikula
@Test
public void testLaziness() {
    SimpleVar<String> s1 = (SimpleVar<String>) Var.newSimpleVar("a");
    SimpleVar<String> s2 = (SimpleVar<String>) Var.newSimpleVar("b");
    SimpleVar<String> s3 = (SimpleVar<String>) Var.newSimpleVar("c");
    Val<String> firstNonNull = Val.orElse(s1, s2).orElse(s3);
    replacedertFalse(s1.isObservingInputs());
    replacedertFalse(s2.isObservingInputs());
    replacedertFalse(s2.isObservingInputs());
    Subscription sub = firstNonNull.pin();
    replacedertTrue(s1.isObservingInputs());
    replacedertTrue(s2.isObservingInputs());
    replacedertTrue(s2.isObservingInputs());
    sub.unsubscribe();
    replacedertFalse(s1.isObservingInputs());
    replacedertFalse(s2.isObservingInputs());
    replacedertFalse(s2.isObservingInputs());
}

18 View Complete Implementation : PlainTextFxController.java
Copyright Apache License 2.0
Author : exactpro
@ControllerInfo(resourceName = "PlainTextFx.fxml")
public clreplaced PlainTextFxController extends AbstractDoreplacedentController<PlainTextFx> {

    public BorderPane mainPane;

    public ToolBar toolbar;

    public ToggleButton tbFindAndReplace;

    public ComboBox<Highlighter> cbHighlighting;

    public CheckBox cbShowLineNumbers;

    public GridPane findPane;

    public CheckBox cbRegexp;

    public CheckBox cbMatchCase;

    public TextField tfFind;

    public TextField tfReplace;

    public Label lblFindCount;

    public Button btnReplace;

    public Button btnReplaceAndFind;

    private StyleClreplacededTextArea textArea;

    private Subscription lastSubscription;

    private BooleanProperty canReplace = new SimpleBooleanProperty(true);

    private Supplier<StyleSpans<Collection<String>>> lastFindSupplier = () -> null;

    private int lastDifference = 0;

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        super.initialize(url, resourceBundle);
        this.textArea = new StyleClreplacededTextArea();
        this.textArea.setOnKeyPressed(event -> {
            if (event.getCode() == KeyCode.F && event.isControlDown()) {
                this.tbFindAndReplace.selectedProperty().setValue(!this.tbFindAndReplace.isSelected());
            }
        });
        this.textArea.getUndoManager().forgetHistory();
        this.textArea.getUndoManager().mark();
        // position the caret at the beginning
        this.textArea.selectRange(0, 0);
        this.cbHighlighting.gereplacedems().addAll(Highlighter.values());
        this.cbHighlighting.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> subscribeAndSet());
        this.cbHighlighting.getSelectionModel().selectFirst();
        this.cbShowLineNumbers.selectedProperty().addListener((observable, oldValue, newValue) -> this.textArea.setParagraphGraphicFactory(newValue ? LineNumberFactory.get(this.textArea) : null));
        this.tbFindAndReplace.selectedProperty().addListener((observable, oldValue, newValue) -> {
            this.findPane.setVisible(newValue);
            this.findPane.setPrefHeight(newValue ? 60 : 0);
            this.findPane.setMinHeight(newValue ? 60 : 0);
            this.findPane.setMaxHeight(newValue ? 60 : 0);
            subscribeAndSet();
            Common.setFocusedFast(this.tfFind);
            if (!newValue) {
                this.lastFindSupplier = () -> null;
            }
        });
        this.mainPane.setCenter(this.textArea);
        GridPane.setColumnSpan(this.textArea, 2);
        this.cbRegexp.selectedProperty().addListener((observable, oldValue, newValue) -> this.model.resetMatcher(this.tfFind.getText(), this.cbMatchCase.isSelected(), this.cbRegexp.isSelected()));
        this.cbMatchCase.selectedProperty().addListener((observable, oldValue, newValue) -> this.model.resetMatcher(this.tfFind.getText(), this.cbMatchCase.isSelected(), this.cbRegexp.isSelected()));
        this.tfFind.textProperty().addListener((observable, oldValue, newValue) -> this.model.resetMatcher(this.tfFind.getText(), this.cbMatchCase.isSelected(), this.cbRegexp.isSelected()));
        this.btnReplace.disableProperty().bind(canReplace);
        this.btnReplaceAndFind.setDisable(true);
    }

    public void init(Doreplacedent model, CustomTab customTab) {
        super.init(model, customTab);
        this.model.getFindCountProperty().setOnChangeListener((oldValue, newValue) -> {
            if (newValue == null || newValue < 0) {
                this.lblFindCount.setText("");
            } else {
                this.lblFindCount.setText("Found " + newValue);
            }
        });
        this.textArea.replaceText(this.model.getProperty().get());
        this.textArea.setEstimatedScrollY(0.0);
        this.textArea.setEstimatedScrollX(0.0);
        this.textArea.textProperty().addListener((observable, oldValue, newValue) -> {
            if (Objects.equals(newValue, oldValue)) {
                return;
            }
            this.model.getProperty().accept(newValue);
        });
        this.model.getProperty().setOnChangeListener((oldValue, newValue) -> {
            this.model.getChangedProperty().accept(true);
            this.textArea.replaceText(newValue);
        });
        this.model.getHighlighter().setOnChangeListener((o, n) -> this.cbHighlighting.getSelectionModel().select(n));
        Settings.SettingsValue value = model.getFactory().getSettings().getValueOrDefault(Settings.GLOBAL_NS, Settings.SETTINGS, Settings.FONT);
        Font font = Common.fontFromString(value.getValue());
        this.textArea.setStyle("-fx-font-size: " + font.getSize() + "; -fx-font-family: \"" + font.getFamily() + "\";");
    }

    public void findAll(ActionEvent actionEvent) {
        this.lastFindSupplier = () -> {
            List<StyleWithRange> styles = this.model.findAll();
            return Common.convertFromList(styles);
        };
        subscribeAndSet();
    }

    public void replaceAll(ActionEvent actionEvent) {
        if (!this.tfFind.getText().isEmpty()) {
            this.model.replaceAll(this.tfReplace.getText());
        }
    }

    public void findNext(ActionEvent actionEvent) {
        List<StyleWithRange> styles = this.model.findNext();
        this.btnReplaceAndFind.setDisable(true);
        this.lastFindSupplier = () -> Common.convertFromList(this.model.getCurrentStyles(lastDifference));
        if (styles.size() != 1) {
            styles.stream().filter(s -> s.getStyle() == null).findFirst().ifPresent(swr -> {
                this.textArea.moveTo(swr.getRange() + 1);
                this.textArea.selectWord();
                int currentParagraph = this.textArea.getCurrentParagraph();
                this.textArea.setEstimatedScrollY(this.textArea.getTotalHeightEstimate() / this.textArea.getParagraphs().size() * currentParagraph);
                this.btnReplaceAndFind.setDisable(false);
            });
            this.canReplace.setValue(false);
        }
        this.subscribeAndSet();
    }

    public void replaceCurrent(ActionEvent actionEvent) {
        this.model.replaceCurrent(this.tfReplace.getText());
        this.canReplace.setValue(true);
    }

    public void replaceAndFind(ActionEvent actionEvent) {
        this.replaceCurrent(null);
        this.findNext(null);
    }

    // region private methods
    private void subscribeAndSet() {
        Highlighter highlighter = this.cbHighlighting.getSelectionModel().getSelectedItem();
        Optional.ofNullable(this.lastSubscription).ifPresent(Subscription::unsubscribe);
        this.textArea.setStyleSpans(0, union(convert(highlighter), this.lastFindSupplier.get()));
        this.lastSubscription = this.textArea.richChanges().filter(ch -> !ch.getInserted().equals(ch.getRemoved())).subscribe(change -> {
            this.lastDifference = change.getInserted().length() - change.getRemoved().length();
            this.textArea.setStyleSpans(0, union(convert(highlighter), this.lastFindSupplier.get()));
        });
    }

    private StyleSpans<Collection<String>> convert(Highlighter highlighter) {
        return Common.convertFromList(highlighter.getStyles(this.textArea.getText()));
    }

    private static StyleSpans<Collection<String>> union(StyleSpans<Collection<String>> initList, StyleSpans<Collection<String>> findList) {
        if (findList == null) {
            return initList;
        }
        StyleSpansBuilder<Collection<String>> spansBuilder = new StyleSpansBuilder<>();
        for (int i = 0; i < initList.length(); i++) {
            StyleSpan<Collection<String>> find = get(i, findList);
            StyleSpan<Collection<String>> init = get(i, initList);
            boolean b = find == null || (find.getStyle().size() == 1 && find.getStyle().iterator().next().equals("default"));
            Collection<String> strings = b ? init.getStyle() : find.getStyle();
            StyleSpan<Collection<String>> newSpan = new StyleSpan<>(strings, 1);
            spansBuilder.add(newSpan);
        }
        return spansBuilder.create();
    }

    private static StyleSpan<Collection<String>> get(int position, StyleSpans<Collection<String>> list) {
        int curPos = 0;
        for (int i = 0; i < list.getSpanCount(); i++) {
            StyleSpan<Collection<String>> styleSpan = list.getStyleSpan(i);
            if (curPos <= position && (curPos + styleSpan.getLength() - 1) >= position) {
                return styleSpan;
            }
            curPos += styleSpan.getLength();
        }
        // never happens
        return new StyleSpan<>(Collections.singleton("default"), 1);
    }
    // endregion
}

18 View Complete Implementation : Navigator.java
Copyright BSD 2-Clause "Simplified" License
Author : FXMisc
/**
 * Responsible for laying out cells' nodes within the viewport based on a single anchor node. In a layout call,
 * this anchor node is positioned in the viewport before any other node and then nodes are positioned above and
 * below that anchor node sequentially. This sequential layout continues until the viewport's "top" and "bottom" edges
 * are reached or there are no other cells' nodes to render. In this latter case (when there is not enough content to
 * fill up the entire viewport), the displayed cells are repositioned towards the "ground," based on the
 * {@link VirtualFlow}'s {@link Gravity} value, and any remaining unused space counts as the "sky."
 */
final clreplaced Navigator<T, C extends Cell<T, ?>> extends Region implements TargetPositionVisitor {

    private final CellListManager<T, C> cellListManager;

    private final MemoizationList<C> cells;

    private final CellPositioner<T, C> positioner;

    private final OrientationHelper orientation;

    private final ObjectProperty<Gravity> gravity;

    private final SizeTracker sizeTracker;

    private final Subscription itemsSubscription;

    private TargetPosition currentPosition = TargetPosition.BEGINNING;

    private TargetPosition targetPosition = TargetPosition.BEGINNING;

    private int firstVisibleIndex = -1;

    private int lastVisibleIndex = -1;

    public Navigator(CellListManager<T, C> cellListManager, CellPositioner<T, C> positioner, OrientationHelper orientation, ObjectProperty<Gravity> gravity, SizeTracker sizeTracker) {
        this.cellListManager = cellListManager;
        this.cells = cellListManager.getLazyCellList();
        this.positioner = positioner;
        this.orientation = orientation;
        this.gravity = gravity;
        this.sizeTracker = sizeTracker;
        this.itemsSubscription = LiveList.observeQuasiChanges(cellListManager.getLazyCellList(), this::itemsChanged);
        Bindings.bindContent(getChildren(), cellListManager.getNodes());
        // When gravity changes, we must redo our layout:
        gravity.addListener((prop, oldVal, newVal) -> requestLayout());
    }

    public void dispose() {
        itemsSubscription.unsubscribe();
        Bindings.unbindContent(getChildren(), cellListManager.getNodes());
    }

    @Override
    protected void layoutChildren() {
        // invalidate breadth for each cell that has dirty layout
        int n = cells.getMemoizedCount();
        for (int i = 0; i < n; ++i) {
            int j = cells.indexOfMemoizedItem(i);
            Node node = cells.get(j).getNode();
            if (node instanceof Parent && ((Parent) node).isNeedsLayout()) {
                sizeTracker.forgetSizeOf(j);
            }
        }
        if (!cells.isEmpty()) {
            targetPosition.clamp(cells.size()).accept(this);
        }
        currentPosition = getCurrentPosition();
        targetPosition = currentPosition;
    }

    /**
     * Sets the {@link TargetPosition} used to layout the anchor node and re-lays out the viewport
     */
    public void setTargetPosition(TargetPosition targetPosition) {
        this.targetPosition = targetPosition;
        requestLayout();
    }

    /**
     * Sets the {@link TargetPosition} used to layout the anchor node to the current position scrolled by {@code delta}
     * and re-lays out the viewport
     */
    public void scrollCurrentPositionBy(double delta) {
        targetPosition = currentPosition.scrollBy(delta);
        requestLayout();
    }

    private TargetPosition getCurrentPosition() {
        if (cellListManager.getLazyCellList().getMemoizedCount() == 0) {
            return TargetPosition.BEGINNING;
        } else {
            C cell = positioner.getVisibleCell(firstVisibleIndex);
            return new StartOffStart(firstVisibleIndex, orientation.minY(cell));
        }
    }

    private void itemsChanged(QuasiListChange<?> ch) {
        for (QuasiListModification<?> mod : ch) {
            targetPosition = targetPosition.transformByChange(mod.getFrom(), mod.getRemovedSize(), mod.getAddedSize());
        }
        // TODO: could optimize to only request layout if
        requestLayout();
    // target position changed or cells in the viewport
    // are affected
    }

    void showLengthRegion(int itemIndex, double fromY, double toY) {
        setTargetPosition(new MinDistanceTo(itemIndex, Offset.fromStart(fromY), Offset.fromStart(toY)));
    }

    @Override
    public void visit(StartOffStart targetPosition) {
        placeStartAtMayCrop(targetPosition.itemIndex, targetPosition.offsetFromStart);
        fillViewportFrom(targetPosition.itemIndex);
    }

    @Override
    public void visit(EndOffEnd targetPosition) {
        placeEndOffEndMayCrop(targetPosition.itemIndex, targetPosition.offsetFromEnd);
        fillViewportFrom(targetPosition.itemIndex);
    }

    @Override
    public void visit(MinDistanceTo targetPosition) {
        Optional<C> cell = positioner.getCellIfVisible(targetPosition.itemIndex);
        if (cell.isPresent()) {
            placeToViewport(targetPosition.itemIndex, targetPosition.minY, targetPosition.maxY);
        } else {
            OptionalInt prevVisible;
            OptionalInt nextVisible;
            if ((prevVisible = positioner.lastVisibleBefore(targetPosition.itemIndex)).isPresent()) {
                // Try keeping prevVisible in place:
                // fill the viewport, see if the target item appeared.
                fillForwardFrom(prevVisible.getAsInt());
                cell = positioner.getCellIfVisible(targetPosition.itemIndex);
                if (cell.isPresent()) {
                    placeToViewport(targetPosition.itemIndex, targetPosition.minY, targetPosition.maxY);
                } else if (targetPosition.maxY.isFromStart()) {
                    placeStartOffEndMayCrop(targetPosition.itemIndex, -targetPosition.maxY.getValue());
                } else {
                    placeEndOffEndMayCrop(targetPosition.itemIndex, -targetPosition.maxY.getValue());
                }
            } else if ((nextVisible = positioner.firstVisibleAfter(targetPosition.itemIndex + 1)).isPresent()) {
                // Try keeping nextVisible in place:
                // fill the viewport, see if the target item appeared.
                fillBackwardFrom(nextVisible.getAsInt());
                cell = positioner.getCellIfVisible(targetPosition.itemIndex);
                if (cell.isPresent()) {
                    placeToViewport(targetPosition.itemIndex, targetPosition.minY, targetPosition.maxY);
                } else if (targetPosition.minY.isFromStart()) {
                    placeStartAtMayCrop(targetPosition.itemIndex, -targetPosition.minY.getValue());
                } else {
                    placeEndOffStartMayCrop(targetPosition.itemIndex, -targetPosition.minY.getValue());
                }
            } else {
                if (targetPosition.minY.isFromStart()) {
                    placeStartAtMayCrop(targetPosition.itemIndex, -targetPosition.minY.getValue());
                } else {
                    placeEndOffStartMayCrop(targetPosition.itemIndex, -targetPosition.minY.getValue());
                }
            }
        }
        fillViewportFrom(targetPosition.itemIndex);
    }

    /**
     * Get the index of the first visible cell (at the time of the last layout).
     *
     * @return The index of the first visible cell
     */
    public int getFirstVisibleIndex() {
        return firstVisibleIndex;
    }

    /**
     * Get the index of the last visible cell (at the time of the last layout).
     *
     * @return The index of the last visible cell
     */
    public int getLastVisibleIndex() {
        return lastVisibleIndex;
    }

    private void placeToViewport(int itemIndex, Offset from, Offset to) {
        C cell = positioner.getVisibleCell(itemIndex);
        double fromY = from.isFromStart() ? from.getValue() : orientation.length(cell) + to.getValue();
        double toY = to.isFromStart() ? to.getValue() : orientation.length(cell) + to.getValue();
        placeToViewport(itemIndex, fromY, toY);
    }

    private void placeToViewport(int itemIndex, double fromY, double toY) {
        C cell = positioner.getVisibleCell(itemIndex);
        double d = positioner.shortestDeltaToViewport(cell, fromY, toY);
        positioner.placeStartAt(itemIndex, orientation.minY(cell) + d);
    }

    private void placeStartAtMayCrop(int itemIndex, double startOffStart) {
        cropToNeighborhoodOf(itemIndex, startOffStart);
        positioner.placeStartAt(itemIndex, startOffStart);
    }

    private void placeStartOffEndMayCrop(int itemIndex, double startOffEnd) {
        cropToNeighborhoodOf(itemIndex, startOffEnd);
        positioner.placeStartFromEnd(itemIndex, startOffEnd);
    }

    private void placeEndOffStartMayCrop(int itemIndex, double endOffStart) {
        cropToNeighborhoodOf(itemIndex, endOffStart);
        positioner.placeEndFromStart(itemIndex, endOffStart);
    }

    private void placeEndOffEndMayCrop(int itemIndex, double endOffEnd) {
        cropToNeighborhoodOf(itemIndex, endOffEnd);
        positioner.placeEndFromEnd(itemIndex, endOffEnd);
    }

    private void cropToNeighborhoodOf(int itemIndex, double additionalOffset) {
        double spaceBefore = Math.max(0, sizeTracker.getViewportLength() + additionalOffset);
        double spaceAfter = Math.max(0, sizeTracker.getViewportLength() - additionalOffset);
        Optional<Double> avgLen = sizeTracker.getAverageLengthEstimate();
        int itemsBefore = avgLen.map(l -> spaceBefore / l).orElse(5.0).intValue();
        int itemsAfter = avgLen.map(l -> spaceAfter / l).orElse(5.0).intValue();
        positioner.cropTo(itemIndex - itemsBefore, itemIndex + 1 + itemsAfter);
    }

    private int fillForwardFrom(int itemIndex) {
        return fillForwardFrom(itemIndex, sizeTracker.getViewportLength());
    }

    private int fillForwardFrom0(int itemIndex) {
        return fillForwardFrom0(itemIndex, sizeTracker.getViewportLength());
    }

    private int fillForwardFrom(int itemIndex, double upTo) {
        // resize and/or reposition the starting cell
        // in case the preferred or available size changed
        C cell = positioner.getVisibleCell(itemIndex);
        double length0 = orientation.minY(cell);
        positioner.placeStartAt(itemIndex, length0);
        return fillForwardFrom0(itemIndex, upTo);
    }

    int fillForwardFrom0(int itemIndex, double upTo) {
        double max = orientation.maxY(positioner.getVisibleCell(itemIndex));
        int i = itemIndex;
        while (max < upTo && i < cellListManager.getLazyCellList().size() - 1) {
            ++i;
            C c = positioner.placeStartAt(i, max);
            max = orientation.maxY(c);
        }
        return i;
    }

    private int fillBackwardFrom(int itemIndex) {
        return fillBackwardFrom(itemIndex, 0.0);
    }

    private int fillBackwardFrom0(int itemIndex) {
        return fillBackwardFrom0(itemIndex, 0.0);
    }

    private int fillBackwardFrom(int itemIndex, double upTo) {
        // resize and/or reposition the starting cell
        // in case the preferred or available size changed
        C cell = positioner.getVisibleCell(itemIndex);
        double length0 = orientation.minY(cell);
        positioner.placeStartAt(itemIndex, length0);
        return fillBackwardFrom0(itemIndex, upTo);
    }

    // does not re-place the anchor cell
    int fillBackwardFrom0(int itemIndex, double upTo) {
        double min = orientation.minY(positioner.getVisibleCell(itemIndex));
        int i = itemIndex;
        while (min > upTo && i > 0) {
            --i;
            C c = positioner.placeEndFromStart(i, min);
            min = orientation.minY(c);
        }
        return i;
    }

    /**
     * Starting from the anchor cell's node, fills the viewport from the anchor to the "ground" and then from the anchor
     * to the "sky".
     *
     * @param itemIndex the index of the anchor cell
     */
    private void fillViewportFrom(int itemIndex) {
        // cell for itemIndex is replacedumed to be placed correctly
        // fill up to the ground
        int ground = fillTowardsGroundFrom0(itemIndex);
        // if ground not reached, shift cells to the ground
        double gapBefore = distanceFromGround(ground);
        if (gapBefore > 0) {
            shiftCellsTowardsGround(ground, itemIndex, gapBefore);
        }
        // fill up to the sky
        int sky = fillTowardsSkyFrom0(itemIndex);
        // if sky not reached, add more cells under the ground and then shift
        double gapAfter = distanceFromSky(sky);
        if (gapAfter > 0) {
            ground = fillTowardsGroundFrom0(ground, -gapAfter);
            double extraBefore = -distanceFromGround(ground);
            double shift = Math.min(gapAfter, extraBefore);
            shiftCellsTowardsGround(ground, sky, -shift);
        }
        // crop to the visible cells
        int first = Math.min(ground, sky);
        int last = Math.max(ground, sky);
        while (first < last && orientation.maxY(positioner.getVisibleCell(first)) <= 0.0) {
            ++first;
        }
        while (last > first && orientation.minY(positioner.getVisibleCell(last)) >= sizeTracker.getViewportLength()) {
            --last;
        }
        firstVisibleIndex = first;
        lastVisibleIndex = last;
        positioner.cropTo(first, last + 1);
    }

    private int fillTowardsGroundFrom0(int itemIndex) {
        return gravity.get() == Gravity.FRONT ? fillBackwardFrom0(itemIndex) : fillForwardFrom0(itemIndex);
    }

    private int fillTowardsGroundFrom0(int itemIndex, double upTo) {
        return gravity.get() == Gravity.FRONT ? fillBackwardFrom0(itemIndex, upTo) : fillForwardFrom0(itemIndex, sizeTracker.getViewportLength() - upTo);
    }

    private int fillTowardsSkyFrom0(int itemIndex) {
        return gravity.get() == Gravity.FRONT ? fillForwardFrom0(itemIndex) : fillBackwardFrom0(itemIndex);
    }

    private double distanceFromGround(int itemIndex) {
        C cell = positioner.getVisibleCell(itemIndex);
        return gravity.get() == Gravity.FRONT ? orientation.minY(cell) : sizeTracker.getViewportLength() - orientation.maxY(cell);
    }

    private double distanceFromSky(int itemIndex) {
        C cell = positioner.getVisibleCell(itemIndex);
        return gravity.get() == Gravity.FRONT ? sizeTracker.getViewportLength() - orientation.maxY(cell) : orientation.minY(cell);
    }

    private void shiftCellsTowardsGround(int groundCellIndex, int lastCellIndex, double amount) {
        if (gravity.get() == Gravity.FRONT) {
            replacedert groundCellIndex <= lastCellIndex;
            for (int i = groundCellIndex; i <= lastCellIndex; ++i) {
                positioner.shiftCellBy(positioner.getVisibleCell(i), -amount);
            }
        } else {
            replacedert groundCellIndex >= lastCellIndex;
            for (int i = groundCellIndex; i >= lastCellIndex; --i) {
                positioner.shiftCellBy(positioner.getVisibleCell(i), amount);
            }
        }
    }
}

18 View Complete Implementation : CaretSelectionBindImpl.java
Copyright BSD 2-Clause "Simplified" License
Author : FXMisc
final clreplaced CaretSelectionBindImpl<PS, SEG, S> implements CaretSelectionBind<PS, SEG, S> {

    // caret
    @Override
    public Var<CaretVisibility> showCaretProperty() {
        return delegateCaret.showCaretProperty();
    }

    @Override
    public CaretVisibility getShowCaret() {
        return delegateCaret.getShowCaret();
    }

    @Override
    public void setShowCaret(CaretVisibility value) {
        delegateCaret.setShowCaret(value);
    }

    /* ********************************************************************** *
     *                                                                        *
     * 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.                          *
     *                                                                        *
     * ********************************************************************** */
    // caret
    @Override
    public ObservableValue<Integer> positionProperty() {
        return delegateCaret.positionProperty();
    }

    @Override
    public int getPosition() {
        return delegateCaret.getPosition();
    }

    @Override
    public ObservableValue<Integer> paragraphIndexProperty() {
        return delegateCaret.paragraphIndexProperty();
    }

    @Override
    public int getParagraphIndex() {
        return delegateCaret.getParagraphIndex();
    }

    @Override
    public ObservableValue<OptionalInt> lineIndexProperty() {
        return delegateCaret.lineIndexProperty();
    }

    @Override
    public OptionalInt getLineIndex() {
        return delegateCaret.getLineIndex();
    }

    @Override
    public ObservableValue<Integer> columnPositionProperty() {
        return delegateCaret.columnPositionProperty();
    }

    @Override
    public int getColumnPosition() {
        return delegateCaret.getColumnPosition();
    }

    @Override
    public ObservableValue<Boolean> visibleProperty() {
        return delegateCaret.visibleProperty();
    }

    @Override
    public boolean isVisible() {
        return delegateCaret.isVisible();
    }

    @Override
    public ObservableValue<Duration> blinkRateProperty() {
        return delegateCaret.blinkRateProperty();
    }

    @Override
    public Duration getBlinkRate() {
        return delegateCaret.getBlinkRate();
    }

    @Override
    public void setBlinkRate(Duration blinkRate) {
        delegateCaret.setBlinkRate(blinkRate);
    }

    @Override
    public ObservableValue<Optional<Bounds>> caretBoundsProperty() {
        return delegateCaret.caretBoundsProperty();
    }

    @Override
    public Optional<Bounds> getCaretBounds() {
        return delegateCaret.getCaretBounds();
    }

    @Override
    public void clearTargetOffset() {
        delegateCaret.clearTargetOffset();
    }

    @Override
    public ParagraphBox.CaretOffsetX getTargetOffset() {
        return delegateCaret.getTargetOffset();
    }

    // selection
    @Override
    public ObservableValue<IndexRange> rangeProperty() {
        return delegateSelection.rangeProperty();
    }

    @Override
    public IndexRange getRange() {
        return delegateSelection.getRange();
    }

    @Override
    public ObservableValue<Integer> lengthProperty() {
        return delegateSelection.lengthProperty();
    }

    @Override
    public int getLength() {
        return delegateSelection.getLength();
    }

    @Override
    public ObservableValue<Integer> paragraphSpanProperty() {
        return delegateSelection.paragraphSpanProperty();
    }

    @Override
    public int getParagraphSpan() {
        return delegateSelection.getParagraphSpan();
    }

    @Override
    public final ObservableValue<StyledDoreplacedent<PS, SEG, S>> selectedDoreplacedentProperty() {
        return delegateSelection.selectedDoreplacedentProperty();
    }

    @Override
    public final StyledDoreplacedent<PS, SEG, S> getSelectedDoreplacedent() {
        return delegateSelection.getSelectedDoreplacedent();
    }

    @Override
    public ObservableValue<String> selectedTextProperty() {
        return delegateSelection.selectedTextProperty();
    }

    @Override
    public String getSelectedText() {
        return delegateSelection.getSelectedText();
    }

    @Override
    public ObservableValue<Integer> startPositionProperty() {
        return delegateSelection.startPositionProperty();
    }

    @Override
    public int getStartPosition() {
        return delegateSelection.getStartPosition();
    }

    @Override
    public ObservableValue<Integer> startParagraphIndexProperty() {
        return delegateSelection.startParagraphIndexProperty();
    }

    @Override
    public int getStartParagraphIndex() {
        return delegateSelection.getStartParagraphIndex();
    }

    @Override
    public ObservableValue<Integer> startColumnPositionProperty() {
        return delegateSelection.startColumnPositionProperty();
    }

    @Override
    public int getStartColumnPosition() {
        return delegateSelection.getStartColumnPosition();
    }

    @Override
    public ObservableValue<Integer> endPositionProperty() {
        return delegateSelection.endPositionProperty();
    }

    @Override
    public int getEndPosition() {
        return delegateSelection.getEndPosition();
    }

    @Override
    public ObservableValue<Integer> endParagraphIndexProperty() {
        return delegateSelection.endParagraphIndexProperty();
    }

    @Override
    public int getEndParagraphIndex() {
        return delegateSelection.getEndParagraphIndex();
    }

    @Override
    public ObservableValue<Integer> endColumnPositionProperty() {
        return delegateSelection.endColumnPositionProperty();
    }

    @Override
    public int getEndColumnPosition() {
        return delegateSelection.getEndColumnPosition();
    }

    @Override
    public ObservableValue<Optional<Bounds>> selectionBoundsProperty() {
        return delegateSelection.selectionBoundsProperty();
    }

    @Override
    public Optional<Bounds> getSelectionBounds() {
        return delegateSelection.getSelectionBounds();
    }

    private final CaretNode delegateCaret;

    @Override
    public CaretNode getUnderlyingCaret() {
        return delegateCaret;
    }

    @Override
    public String getCaretName() {
        return delegateCaret.getCaretName();
    }

    private final Selection<PS, SEG, S> delegateSelection;

    @Override
    public Selection<PS, SEG, S> getUnderlyingSelection() {
        return delegateSelection;
    }

    @Override
    public String getSelectionName() {
        return delegateSelection.getSelectionName();
    }

    @Override
    public void configureSelectionPath(SelectionPath path) {
        delegateSelection.configureSelectionPath(path);
    }

    @Override
    public GenericStyledArea<PS, SEG, S> getArea() {
        return delegateSelection.getArea();
    }

    // caret selection bind
    private final Val<Integer> anchorPosition;

    @Override
    public int getAnchorPosition() {
        return anchorPosition.getValue();
    }

    @Override
    public ObservableValue<Integer> anchorPositionProperty() {
        return anchorPosition;
    }

    private final Val<Integer> anchorParIndex;

    @Override
    public int getAnchorParIndex() {
        return anchorParIndex.getValue();
    }

    @Override
    public ObservableValue<Integer> anchorParIndexProperty() {
        return anchorParIndex;
    }

    private final Val<Integer> anchorColPosition;

    @Override
    public int getAnchorColPosition() {
        return anchorColPosition.getValue();
    }

    @Override
    public ObservableValue<Integer> anchorColPositionProperty() {
        return anchorColPosition;
    }

    private final SuspendableNo beingUpdated = new SuspendableNo();

    public final boolean isBeingUpdated() {
        return beingUpdated.get();
    }

    public final ObservableValue<Boolean> beingUpdatedProperty() {
        return beingUpdated;
    }

    private final Var<BooleanEvent> internalStartedByAnchor = Var.newSimpleVar(new BooleanEvent(true));

    private final SuspendableVal<BooleanEvent> startedByAnchor = internalStartedByAnchor.suspendable();

    private boolean anchorIsStart() {
        return startedByAnchor.getValue().get();
    }

    private clreplaced BooleanEvent {

        public BooleanEvent(boolean b) {
            value = b;
        }

        public boolean get() {
            return value;
        }

        private final boolean value;
    }

    private Subscription subscription = () -> {
    };

    CaretSelectionBindImpl(String caretName, String selectionName, GenericStyledArea<PS, SEG, S> area) {
        this(caretName, selectionName, area, new IndexRange(0, 0));
    }

    CaretSelectionBindImpl(String caretName, String selectionName, GenericStyledArea<PS, SEG, S> area, IndexRange startingRange) {
        this((updater) -> new CaretNode(caretName, area, updater, startingRange.getStart()), (updater) -> new SelectionImpl<>(selectionName, area, startingRange, updater));
    }

    CaretSelectionBindImpl(Function<SuspendableNo, ? extends CaretNode> createCaret, Function<SuspendableNo, Selection<PS, SEG, S>> createSelection) {
        SuspendableNo delegateUpdater = new SuspendableNo();
        delegateCaret = createCaret.apply(delegateUpdater);
        delegateSelection = createSelection.apply(delegateUpdater);
        if (delegateCaret.getArea() != delegateSelection.getArea()) {
            throw new IllegalArgumentException(String.format("Caret and Selection must be asociated with the same area. Caret area = %s | Selection area = %s", delegateCaret.getArea(), delegateSelection.getArea()));
        }
        Val<Tuple3<Integer, Integer, Integer>> anchorPositions = startedByAnchor.flatMap(b -> b.get() ? Val.constant(Tuples.t(getStartPosition(), getStartParagraphIndex(), getStartColumnPosition())) : Val.constant(Tuples.t(getEndPosition(), getEndParagraphIndex(), getEndColumnPosition())));
        anchorPosition = anchorPositions.map(Tuple3::get1);
        anchorParIndex = anchorPositions.map(Tuple3::get2);
        anchorColPosition = anchorPositions.map(Tuple3::get3);
        Suspendable omniSuspendable = Suspendable.combine(// first, so it's released last
        beingUpdated, startedByAnchor, // last, so it's released before startedByAnchor, so that anchor's values are correct
        delegateUpdater);
        subscription = omniSuspendable.suspendWhen(getArea().beingUpdatedProperty());
    }

    /* ********************************************************************** *
     *                                                                        *
     * Actions                                                                *
     *                                                                        *
     * Actions change the state of this control. They typically cause a       *
     * change of one or more observables and/or produce an event.             *
     *                                                                        *
     * ********************************************************************** */
    // caret
    @Override
    public void moveBreaksForwards(int numOfBreaks, BreakIterator breakIterator) {
        if (getAreaLength() == 0) {
            return;
        }
        breakIterator.setText(getArea().getText());
        int position = calculatePositionViaBreakingForwards(numOfBreaks, breakIterator, getPosition());
        moveTo(position, NavigationActions.SelectionPolicy.CLEAR);
    }

    @Override
    public void moveBreaksBackwards(int numOfBreaks, BreakIterator breakIterator) {
        if (getAreaLength() == 0) {
            return;
        }
        breakIterator.setText(getArea().getText());
        int position = calculatePositionViaBreakingBackwards(numOfBreaks, breakIterator, getPosition());
        moveTo(position, NavigationActions.SelectionPolicy.CLEAR);
    }

    // selection
    @Override
    public void selectRange(int startPosition, int endPosition) {
        doSelect(startPosition, endPosition, anchorIsStart());
    }

    @Override
    public void selectRange(int startParagraphIndex, int startColPosition, int endParagraphIndex, int endColPosition) {
        selectRange(textPosition(startParagraphIndex, startColPosition), textPosition(endParagraphIndex, endColPosition));
    }

    @Override
    public void updateStartBy(int amount, Direction direction) {
        int updatedStart = direction == Direction.LEFT ? getStartPosition() - amount : getStartPosition() + amount;
        selectRange(updatedStart, getEndPosition());
    }

    @Override
    public void updateEndBy(int amount, Direction direction) {
        int updatedEnd = direction == Direction.LEFT ? getEndPosition() - amount : getEndPosition() + amount;
        selectRange(getStartPosition(), updatedEnd);
    }

    @Override
    public void updateStartTo(int position) {
        selectRange(position, getEndPosition());
    }

    @Override
    public void updateStartTo(int paragraphIndex, int columnPosition) {
        updateStartTo(textPosition(paragraphIndex, columnPosition));
    }

    @Override
    public void updateStartByBreaksForward(int numOfBreaks, BreakIterator breakIterator) {
        if (getAreaLength() == 0) {
            return;
        }
        breakIterator.setText(getArea().getText());
        int position = calculatePositionViaBreakingForwards(numOfBreaks, breakIterator, getStartPosition());
        updateStartTo(position);
    }

    @Override
    public void updateStartByBreaksBackward(int numOfBreaks, BreakIterator breakIterator) {
        if (getAreaLength() == 0) {
            return;
        }
        breakIterator.setText(getArea().getText());
        int position = calculatePositionViaBreakingBackwards(numOfBreaks, breakIterator, getStartPosition());
        updateStartTo(position);
    }

    @Override
    public void updateEndTo(int position) {
        selectRange(getStartPosition(), position);
    }

    @Override
    public void updateEndTo(int paragraphIndex, int columnPosition) {
        updateEndTo(textPosition(paragraphIndex, columnPosition));
    }

    @Override
    public void updateEndByBreaksForward(int numOfBreaks, BreakIterator breakIterator) {
        if (getAreaLength() == 0) {
            return;
        }
        breakIterator.setText(getArea().getText());
        int position = calculatePositionViaBreakingForwards(numOfBreaks, breakIterator, getStartPosition());
        updateEndTo(position);
    }

    @Override
    public void updateEndByBreaksBackward(int numOfBreaks, BreakIterator breakIterator) {
        if (getAreaLength() == 0) {
            return;
        }
        breakIterator.setText(getArea().getText());
        int position = calculatePositionViaBreakingBackwards(numOfBreaks, breakIterator, getStartPosition());
        updateEndTo(position);
    }

    @Override
    public void selectAll() {
        selectRange(0, getAreaLength());
    }

    @Override
    public void selectParagraph(int paragraphIndex) {
        int start = textPosition(paragraphIndex, 0);
        int end = getArea().getParagraphLength(paragraphIndex);
        selectRange(start, end);
    }

    @Override
    public void selectWord(int wordPositionInArea) {
        if (getAreaLength() == 0) {
            return;
        }
        BreakIterator breakIterator = BreakIterator.getWordInstance();
        breakIterator.setText(getArea().getText());
        int start = calculatePositionViaBreakingBackwards(1, breakIterator, wordPositionInArea);
        int end = calculatePositionViaBreakingForwards(1, breakIterator, wordPositionInArea);
        selectRange(start, end);
    }

    @Override
    public void deselect() {
        selectRangeExpl(getPosition(), getPosition());
    }

    // caret selection bind
    @Override
    public void selectRangeExpl(int anchorParagraph, int anchorColumn, int caretParagraph, int caretColumn) {
        selectRangeExpl(textPosition(anchorParagraph, anchorColumn), textPosition(caretParagraph, caretColumn));
    }

    @Override
    public void selectRangeExpl(int anchorPosition, int caretPosition) {
        if (anchorPosition <= caretPosition) {
            doSelect(anchorPosition, caretPosition, true);
        } else {
            doSelect(caretPosition, anchorPosition, false);
        }
    }

    @Override
    public void moveTo(int pos, NavigationActions.SelectionPolicy selectionPolicy) {
        switch(selectionPolicy) {
            case CLEAR:
                selectRangeExpl(pos, pos);
                break;
            case ADJUST:
                selectRangeExpl(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;
        }
    }

    @Override
    public void moveTo(int paragraphIndex, int columnIndex, NavigationActions.SelectionPolicy selectionPolicy) {
        moveTo(textPosition(paragraphIndex, columnIndex), selectionPolicy);
    }

    @Override
    public void moveToPrevChar(NavigationActions.SelectionPolicy selectionPolicy) {
        if (getPosition() > 0) {
            int newCaretPos = Character.offsetByCodePoints(getArea().getText(), getPosition(), -1);
            moveTo(newCaretPos, selectionPolicy);
        }
    }

    @Override
    public void moveToNextChar(NavigationActions.SelectionPolicy selectionPolicy) {
        if (getPosition() < getAreaLength()) {
            int newCaretPos = Character.offsetByCodePoints(getArea().getText(), getPosition(), 1);
            moveTo(newCaretPos, selectionPolicy);
        }
    }

    @Override
    public void moveToParStart(NavigationActions.SelectionPolicy selectionPolicy) {
        moveTo(getPosition() - getColumnPosition(), selectionPolicy);
    }

    @Override
    public void moveToParEnd(NavigationActions.SelectionPolicy selectionPolicy) {
        moveTo(getPosition() - getColumnPosition() + getArea().getParagraphLength(getParagraphIndex()), selectionPolicy);
    }

    @Override
    public void moveToAreaStart(NavigationActions.SelectionPolicy selectionPolicy) {
        moveTo(0, selectionPolicy);
    }

    @Override
    public void moveToAreaEnd(NavigationActions.SelectionPolicy selectionPolicy) {
        moveTo(getArea().getLength(), selectionPolicy);
    }

    @Override
    public void displaceCaret(int position) {
        doUpdate(() -> delegateCaret.moveTo(position));
    }

    @Override
    public void displaceSelection(int startPosition, int endPosition) {
        doUpdate(() -> {
            delegateSelection.selectRange(startPosition, endPosition);
            internalStartedByAnchor.setValue(new BooleanEvent(startPosition < endPosition));
        });
    }

    @Override
    public void dispose() {
        subscription.unsubscribe();
    }

    /* ********************************************************************** *
     *                                                                        *
     * Private methods                                                        *
     *                                                                        *
     * ********************************************************************** */
    private void doSelect(int startPosition, int endPosition, boolean anchorIsStart) {
        doUpdate(() -> {
            delegateSelection.selectRange(startPosition, endPosition);
            internalStartedByAnchor.setValue(new BooleanEvent(anchorIsStart));
            delegateCaret.moveTo(anchorIsStart ? endPosition : startPosition);
        });
    }

    private void doUpdate(Runnable updater) {
        if (isBeingUpdated()) {
            updater.run();
        } else {
            getArea().beingUpdatedProperty().suspendWhile(updater);
        }
    }

    private int textPosition(int paragraphIndex, int columnPosition) {
        return getArea().position(paragraphIndex, columnPosition).toOffset();
    }

    private int getAreaLength() {
        return getArea().getLength();
    }

    /**
     * replacedumes that {@code getArea().getLength != 0} is true and {@link BreakIterator#setText(String)} has been called
     */
    private int calculatePositionViaBreakingForwards(int numOfBreaks, BreakIterator breakIterator, int position) {
        breakIterator.following(position);
        for (int i = 1; i < numOfBreaks; i++) {
            breakIterator.next(numOfBreaks);
        }
        return breakIterator.current();
    }

    /**
     * replacedumes that {@code getArea().getLength != 0} is true and {@link BreakIterator#setText(String)} has been called
     */
    private int calculatePositionViaBreakingBackwards(int numOfBreaks, BreakIterator breakIterator, int position) {
        breakIterator.preceding(position);
        for (int i = 1; i < numOfBreaks; i++) {
            breakIterator.previous();
        }
        return breakIterator.current();
    }
}

18 View Complete Implementation : SubscribeableContentsObsSetTest.java
Copyright BSD 2-Clause "Simplified" License
Author : FXMisc
@Test
public void adding_subscriber_and_removing_it_will_not_throw_exception() {
    SubscribeableContentsObsSet<Integer> set = new SubscribeableContentsObsSet<>();
    Subscription removeSubscriber = set.addSubscriber(i -> Subscription.EMPTY);
    removeSubscriber.unsubscribe();
}

18 View Complete Implementation : ExportXPathWizardController.java
Copyright BSD 2-Clause "Simplified" License
Author : pmd
/**
 * Set the given subscription as close handler and show.
 */
public void showYourself(Subscription parentBinding) {
    myPopupStage.setOnCloseRequest(e -> parentBinding.unsubscribe());
    exportResultArea.setSyntaxHighlighter(new XmlSyntaxHighlighter());
    myPopupStage.show();
}

18 View Complete Implementation : SmartTextFieldListCell.java
Copyright BSD 2-Clause "Simplified" License
Author : pmd
/**
 * A copycat of {@link TextFieldListCell}, because it deletes the graphic and
 * is anyway quite simple.
 */
public abstract clreplaced SmartTextFieldListCell<T> extends ListCell<T> {

    private TextField textField;

    private Subscription subscriber;

    public SmartTextFieldListCell() {
        setEditable(true);
    }

    @Override
    public final void updateItem(T item, boolean empty) {
        super.updateItem(item, empty);
        if (isEmpty() || item == null) {
            setGraphic(null);
            textField = null;
            if (subscriber != null) {
                subscriber.unsubscribe();
                subscriber = Subscription.EMPTY;
            }
        } else {
            if (isEditing()) {
                if (textField != null) {
                    textField.setText(extractEditable(item).getValue());
                } else {
                    textField = getEditingGraphic(item);
                }
                setGraphic(textField);
            } else {
                textField = null;
                Pair<Node, Subscription> nodeAndSub = getNonEditingGraphic(item);
                setGraphic(nodeAndSub.getKey());
                if (subscriber != null) {
                    subscriber.unsubscribe();
                }
                subscriber = nodeAndSub.getValue();
            }
        }
    }

    protected abstract Pair<Node, Subscription> getNonEditingGraphic(T testCase);

    protected abstract Var<String> extractEditable(T t);

    @Nullable
    protected String getPrompt() {
        return null;
    }

    private TextField getEditingGraphic(T t) {
        Var<String> stringVar = extractEditable(t);
        final TextField textField = new TextField(stringVar.getValue());
        textField.setPromptText(getPrompt());
        ControlUtil.makeTextFieldShowPromptEvenIfFocused(textField);
        // Use onAction here rather than onKeyReleased (with check for Enter),
        // as otherwise we encounter RT-34685
        textField.setOnAction(event -> {
            stringVar.setValue(textField.getText());
            commitEdit(t);
            event.consume();
        });
        textField.setOnKeyReleased(ke -> {
            if (ke.getCode() == KeyCode.ESCAPE) {
                cancelEdit();
                ke.consume();
            }
        });
        return textField;
    }

    @Override
    public final void startEdit() {
        super.cancelEdit();
    }

    /**
     * Call this to really start editing.
     *
     * {@link #startEdit()} does nothing, to prevent the edit behaviour
     * from triggering on double-click on any part of the cell. Instead,
     * trigger it on the label part.
     */
    public final void doStartEdit() {
        super.startEdit();
        textField = getEditingGraphic(gereplacedem());
        textField.setText(extractEditable(gereplacedem()).getValue());
        setGraphic(textField);
        textField.selectAll();
        // requesting focus so that key input can immediately go into the
        // TextField (see RT-28132)
        textField.requestFocus();
    }

    @Override
    public final void cancelEdit() {
        super.cancelEdit();
        T item = gereplacedem();
        if (item != null) {
            Pair<Node, Subscription> nonEditingGraphic = getNonEditingGraphic(item);
            if (subscriber != null) {
                subscriber.unsubscribe();
            }
            subscriber = nonEditingGraphic.getValue();
            setGraphic(nonEditingGraphic.getKey());
            textField = null;
        }
    }

    @Override
    public void commitEdit(T newValue) {
        super.commitEdit(newValue);
        textField = null;
    }
}

18 View Complete Implementation : VarFromVal.java
Copyright BSD 2-Clause "Simplified" License
Author : TomasMikula
clreplaced VarFromVal<T> extends ProxyVal<T, T> implements Var<T> {

    private final Consumer<T> setter;

    private Subscription binding = null;

    VarFromVal(Val<T> underlying, Consumer<T> setter) {
        super(underlying);
        this.setter = setter;
    }

    @Override
    public T getValue() {
        return getUnderlyingObservable().getValue();
    }

    @Override
    protected Consumer<? super T> adaptObserver(Consumer<? super T> observer) {
        // no adaptation needed
        return observer;
    }

    @Override
    public void bind(ObservableValue<? extends T> observable) {
        unbind();
        binding = Val.observeChanges(observable, (obs, oldVal, newVal) -> setValue(newVal));
        setValue(observable.getValue());
    }

    @Override
    public void unbind() {
        if (binding != null) {
            binding.unsubscribe();
            binding = null;
        }
    }

    @Override
    public boolean isBound() {
        return binding != null;
    }

    @Override
    public void setValue(T value) {
        setter.accept(value);
    }
}

18 View Complete Implementation : MapDynamicTest.java
Copyright BSD 2-Clause "Simplified" License
Author : TomasMikula
@Test
public void test() {
    Var<Integer> src = Var.newSimpleVar(1);
    Var<UnaryOperator<Integer>> fn = Var.newSimpleVar(UnaryOperator.idenreplacedy());
    Val<Integer> mapped = src.mapDynamic(fn);
    replacedertEquals(1, mapped.getValue().intValue());
    src.setValue(2);
    replacedertEquals(2, mapped.getValue().intValue());
    fn.setValue(i -> i + i);
    replacedertEquals(4, mapped.getValue().intValue());
    Subscription sub = mapped.observeChanges((obs, oldVal, newVal) -> {
        replacedertEquals(4, oldVal.intValue());
        replacedertEquals(8, newVal.intValue());
    });
    fn.setValue(i -> i * i * i);
    sub.unsubscribe();
    fn.setValue(null);
    replacedertTrue(mapped.isEmpty());
}

17 View Complete Implementation : NeuralImageCell.java
Copyright GNU General Public License v3.0
Author : cameronleger
public clreplaced NeuralImageCell {

    private static final Logger log = Logger.getLogger(NeuralImageCell.clreplaced.getName());

    private NeuralImage neuralImage;

    private Subscription weightChanges;

    private StringConverter<Number> doubleConverter = new StringConverter<Number>() {

        @Override
        public String toString(Number t) {
            return String.valueOf(t.doubleValue());
        }

        @Override
        public Number fromString(String string) {
            try {
                return Double.parseDouble(string);
            } catch (Exception e) {
                return 0;
            }
        }
    };

    @FXML
    private GridPane gridPane;

    @FXML
    private ImageView image;

    @FXML
    private CheckBox selected;

    @FXML
    private TextField weight;

    public NeuralImageCell(boolean editable) {
        FXMLLoader fxmlLoader = new FXMLLoader(getClreplaced().getResource("/neuralImageCell.fxml"));
        fxmlLoader.setController(this);
        try {
            fxmlLoader.load();
        } catch (IOException e) {
            log.log(Level.SEVERE, e.toString(), e);
            exit();
            return;
        }
        replacedert gridPane != null : "fx:id=\"gridPane\" was not injected.";
        replacedert image != null : "fx:id=\"image\" was not injected.";
        replacedert selected != null : "fx:id=\"selected\" was not injected.";
        replacedert weight != null : "fx:id=\"weight\" was not injected.";
        setEditable(editable);
    }

    public void setNeuralImage(NeuralImage newNeuralImage) {
        // Remove previous bindings if applicable
        image.imageProperty().unbind();
        if (neuralImage != null)
            selected.selectedProperty().unbindBidirectional(neuralImage.selectedProperty());
        if (weightChanges != null)
            weightChanges.unsubscribe();
        neuralImage = newNeuralImage;
        if (neuralImage != null) {
            // Bind Image and Selection
            image.imageProperty().bind(neuralImage.imageProperty());
            selected.selectedProperty().bindBidirectional(neuralImage.selectedProperty());
            // Event Streams for Weight to convert between double and string
            weight.setText(String.valueOf(neuralImage.getWeight()));
            weightChanges = EventStreams.changesOf(weight.focusedProperty()).subscribe(focusChange -> {
                if (!focusChange.getNewValue()) {
                    // focusing away from input
                    double newWeight = doubleConverter.fromString(weight.getText()).doubleValue();
                    neuralImage.setWeight(newWeight);
                    if (newWeight == 0)
                        weight.setText("1.0");
                }
            });
        }
    }

    public void setEditable(boolean editable) {
        selected.setVisible(editable);
        weight.setVisible(editable);
    }

    public GridPane getCellLayout() {
        return gridPane;
    }

    public ImageView getImage() {
        return image;
    }

    public CheckBox getSelected() {
        return selected;
    }

    public TextField getWeight() {
        return weight;
    }
}

17 View Complete Implementation : CaretNode.java
Copyright BSD 2-Clause "Simplified" License
Author : FXMisc
/**
 * Default implementation for a {@link Caret}. Since only one {@link Path} object is used per caret, the model
 * and view were combined into one item to grant easier access to and modification of CSS-related
 * properties. Caution must be exercised when depending on Path-related properties in any way (e.g.
 * {@link #boundsInLocalProperty()}, {@link #parentProperty()}, etc.). Also, {@link #caretBoundsProperty()}
 * is distinguishable from {@link #boundsInLocalProperty()}.
 *
 * <p>
 *     This clreplaced adds the css property "-rtfx-blink-rate" ({@link #blinkRateProperty()}}
 * </p>
 */
public clreplaced CaretNode extends Path implements Caret, Comparable<CaretNode> {

    private static final javafx.util.Duration HALF_A_SECOND = javafx.util.Duration.millis(500);

    private static final EventStream<Boolean> ALWAYS_FALSE = Val.constant(false).values();

    private static final EventStream<Boolean> ALWAYS_TRUE = Val.constant(true).values();

    /* ********************************************************************** *
     *                                                                        *
     * 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.                          *
     *                                                                        *
     * ********************************************************************** */
    /**
     * Controls the blink rate of the caret, when one is displayed. Setting
     * the duration to zero disables blinking.
     */
    private final StyleableObjectProperty<javafx.util.Duration> blinkRate = new CustomStyleableProperty<>(HALF_A_SECOND, "blinkRate", this, BLINK_RATE);

    /**
     * The blink rate of the caret.
     *
     * Can be styled from CSS using the "-rtfx-blink-rate" property.
     */
    @Override
    public ObjectProperty<javafx.util.Duration> blinkRateProperty() {
        return blinkRate;
    }

    @Override
    public javafx.util.Duration getBlinkRate() {
        return blinkRate.getValue();
    }

    @Override
    public void setBlinkRate(javafx.util.Duration rate) {
        blinkRate.set(rate);
    }

    private final SuspendableVal<Integer> position;

    @Override
    public final int getPosition() {
        return position.getValue();
    }

    @Override
    public final ObservableValue<Integer> positionProperty() {
        return position;
    }

    private final SuspendableVal<Integer> paragraphIndex;

    @Override
    public final int getParagraphIndex() {
        return paragraphIndex.getValue();
    }

    @Override
    public final ObservableValue<Integer> paragraphIndexProperty() {
        return paragraphIndex;
    }

    private final SuspendableVal<OptionalInt> lineIndex;

    @Override
    public final OptionalInt getLineIndex() {
        return lineIndex.getValue();
    }

    @Override
    public final ObservableValue<OptionalInt> lineIndexProperty() {
        return lineIndex;
    }

    private final SuspendableVal<Integer> columnPosition;

    @Override
    public final int getColumnPosition() {
        return columnPosition.getValue();
    }

    @Override
    public final ObservableValue<Integer> columnPositionProperty() {
        return columnPosition;
    }

    private final Var<CaretVisibility> showCaret = Var.newSimpleVar(CaretVisibility.AUTO);

    @Override
    public final CaretVisibility getShowCaret() {
        return showCaret.getValue();
    }

    @Override
    public final void setShowCaret(CaretVisibility value) {
        showCaret.setValue(value);
    }

    @Override
    public final Var<CaretVisibility> showCaretProperty() {
        return showCaret;
    }

    private final SuspendableVal<Optional<Bounds>> bounds;

    @Override
    public final Optional<Bounds> getCaretBounds() {
        return bounds.getValue();
    }

    @Override
    public final ObservableValue<Optional<Bounds>> caretBoundsProperty() {
        return bounds;
    }

    private Optional<ParagraphBox.CaretOffsetX> targetOffset = Optional.empty();

    @Override
    public final void clearTargetOffset() {
        targetOffset = Optional.empty();
    }

    @Override
    public final ParagraphBox.CaretOffsetX getTargetOffset() {
        if (!targetOffset.isPresent()) {
            targetOffset = Optional.of(area.getCaretOffsetX(this));
        }
        return targetOffset.get();
    }

    private final SuspendableNo beingUpdated = new SuspendableNo();

    @Override
    public final boolean isBeingUpdated() {
        return beingUpdated.get();
    }

    @Override
    public final SuspendableNo beingUpdatedProperty() {
        return beingUpdated;
    }

    private final GenericStyledArea<?, ?, ?> area;

    @Override
    public GenericStyledArea<?, ?, ?> getArea() {
        return area;
    }

    private final String name;

    @Override
    public final String getCaretName() {
        return name;
    }

    private final SuspendableNo dependentBeingUpdated;

    private final EventStream<?> dirty;

    private final Var<Integer> internalTextPosition;

    private Subscription subscriptions = () -> {
    };

    public CaretNode(String name, GenericStyledArea<?, ?, ?> area) {
        this(name, area, 0);
    }

    public CaretNode(String name, GenericStyledArea<?, ?, ?> area, int startingPosition) {
        this(name, area, area.beingUpdatedProperty(), startingPosition);
    }

    public CaretNode(String name, GenericStyledArea<?, ?, ?> area, SuspendableNo dependentBeingUpdated, int startingPosition) {
        this.name = name;
        this.area = area;
        this.dependentBeingUpdated = dependentBeingUpdated;
        this.getStyleClreplaced().add("caret");
        this.setManaged(false);
        internalTextPosition = Var.newSimpleVar(startingPosition);
        position = internalTextPosition.suspendable();
        Val<TwoDimensional.Position> caretPosition2D = Val.create(() -> area.offsetToPosition(internalTextPosition.getValue(), Forward), internalTextPosition, area.getParagraphs());
        paragraphIndex = caretPosition2D.map(TwoDimensional.Position::getMajor).suspendable();
        columnPosition = caretPosition2D.map(TwoDimensional.Position::getMinor).suspendable();
        // when content is updated by an area, update the caret of all the other
        // clones that also display the same doreplacedent
        manageSubscription(area.multiPlainChanges(), list -> {
            int finalPosition = getPosition();
            for (PlainTextChange plainTextChange : list) {
                int netLength = plainTextChange.getNetLength();
                if (netLength != 0) {
                    int indexOfChange = plainTextChange.getPosition();
                    // in case of a replacement: "hello there" -> "hi."
                    int endOfChange = indexOfChange + Math.abs(netLength);
                    /*
                        "->" means add (positive) netLength to position
                        "<-" means add (negative) netLength to position
                        "x" means don't update position

                        "+c" means caret was included in the deleted portion of content
                        "-c" means caret was not included in the deleted portion of content
                        Before/At/After means indexOfChange "<" / "==" / ">" position

                               |   Before +c   | Before -c | At | After
                        -------+---------------+-----------+----+------
                        Add    |      N/A      |    ->     | -> | x
                        Delete | indexOfChange |    <-     | x  | x
                     */
                    if (indexOfChange == finalPosition && netLength > 0) {
                        finalPosition = finalPosition + netLength;
                    } else if (indexOfChange < finalPosition) {
                        finalPosition = finalPosition < endOfChange ? indexOfChange : finalPosition + netLength;
                    }
                }
            }
            if (finalPosition != getPosition()) {
                moveTo(finalPosition);
            }
        });
        // whether or not to display the caret
        EventStream<Boolean> blinkCaret = showCaret.values().flatMap(mode -> {
            switch(mode) {
                case ON:
                    return ALWAYS_TRUE;
                case OFF:
                    return ALWAYS_FALSE;
                default:
                case AUTO:
                    return area.autoCaretBlink();
            }
        });
        dirty = merge(invalidationsOf(positionProperty()), invalidationsOf(area.getParagraphs()));
        // The caret is visible in periodic intervals,
        // but only when blinkCaret is true.
        EventStream<javafx.util.Duration> nonNullBlinkRates = EventStreams.valuesOf(blinkRate).filter(i -> i != null);
        manageSubscription(EventStreams.combine(blinkCaret, nonNullBlinkRates).flatMap(tuple -> {
            Boolean blink = tuple.get1();
            javafx.util.Duration rate = tuple.get2();
            if (blink) {
                return rate.lessThanOrEqualTo(ZERO) ? Val.constant(true).values() : booleanPulse(rate, dirty);
            } else {
                return Val.constant(false).values();
            }
        }).feedTo(visibleProperty()));
        bounds = Val.create(() -> area.getCaretBoundsOnScreen(this), EventStreams.merge(area.viewportDirtyEvents(), dirty)).suspendable();
        lineIndex = Val.create(() -> OptionalInt.of(area.lineIndex(getParagraphIndex(), getColumnPosition())), dirty).suspendable();
        Suspendable omniSuspendable = Suspendable.combine(beingUpdated, lineIndex, bounds, paragraphIndex, columnPosition, position);
        manageSubscription(omniSuspendable.suspendWhen(dependentBeingUpdated));
    }

    /* ********************************************************************** *
     *                                                                        *
     * Actions                                                                *
     *                                                                        *
     * Actions change the state of this control. They typically cause a       *
     * change of one or more observables and/or produce an event.             *
     *                                                                        *
     * ********************************************************************** */
    public void moveTo(int paragraphIndex, int columnPosition) {
        moveTo(textPosition(paragraphIndex, columnPosition));
    }

    public void moveTo(int position) {
        Runnable updatePos = () -> internalTextPosition.setValue(position);
        if (isBeingUpdated()) {
            updatePos.run();
        } else {
            dependentBeingUpdated.suspendWhile(updatePos);
        }
    }

    @Override
    public void moveToParStart() {
        moveTo(getPosition() - getColumnPosition());
    }

    @Override
    public void moveToParEnd() {
        moveTo(getPosition() - getColumnPosition() + area.getParagraphLength(getParagraphIndex()));
    }

    @Override
    public void moveToAreaEnd() {
        moveTo(area.getLength());
    }

    @Override
    public void moveToNextChar() {
        moveTo(getPosition() + 1);
    }

    @Override
    public void moveToPrevChar() {
        moveTo(getPosition() - 1);
    }

    @Override
    public void moveBreaksBackwards(int numOfBreaks, BreakIterator breakIterator) {
        moveContentBreaks(numOfBreaks, breakIterator, false);
    }

    @Override
    public void moveBreaksForwards(int numOfBreaks, BreakIterator breakIterator) {
        moveContentBreaks(numOfBreaks, breakIterator, true);
    }

    @Override
    public int compareTo(CaretNode o) {
        return Integer.compare(hashCode(), o.hashCode());
    }

    public void dispose() {
        subscriptions.unsubscribe();
    }

    @Override
    public boolean equals(Object obj) {
        return this == obj;
    }

    @Override
    public int hashCode() {
        return name.hashCode();
    }

    @Override
    public String toString() {
        return String.format("CaretNode(name=%s position=%s paragraphIndex=%s columnPosition=%s %s)", getCaretName(), getPosition(), getParagraphIndex(), getColumnPosition(), super.toString());
    }

    /* ********************************************************************** *
     *                                                                        *
     * Private methods                                                        *
     *                                                                        *
     * ********************************************************************** */
    private int textPosition(int row, int col) {
        return area.position(row, col).toOffset();
    }

    private <T> void manageSubscription(EventStream<T> stream, Consumer<T> subscriber) {
        manageSubscription(stream.subscribe(subscriber));
    }

    private void manageSubscription(Subscription s) {
        subscriptions = subscriptions.and(s);
    }

    private static EventStream<Boolean> booleanPulse(javafx.util.Duration javafxDuration, EventStream<?> restartImpulse) {
        Duration duration = Duration.ofMillis(Math.round(javafxDuration.toMillis()));
        EventStream<?> ticks = EventStreams.restartableTicks(duration, restartImpulse);
        return StateMachine.init(false).on(restartImpulse.withDefaultEvent(null)).transition((state, impulse) -> true).on(ticks).transition((state, tick) -> !state).toStateStream();
    }

    /**
     * Helper method for reducing duplicate code
     * @param numOfBreaks the number of breaks
     * @param breakIterator the type of iterator to use
     * @param followingNotPreceding if true, use {@link BreakIterator#following(int)}.
     *                              Otherwise, use {@link BreakIterator#preceding(int)}.
     */
    private void moveContentBreaks(int numOfBreaks, BreakIterator breakIterator, boolean followingNotPreceding) {
        if (area.getLength() == 0) {
            return;
        }
        breakIterator.setText(area.getText());
        if (followingNotPreceding) {
            breakIterator.following(getPosition());
        } else {
            breakIterator.preceding(getPosition());
        }
        for (int i = 1; i < numOfBreaks; i++) {
            breakIterator.next();
        }
        moveTo(breakIterator.current());
    }

    /* ********************************************************************** *
     *                                                                        *
     * CSS                                                                    *
     *                                                                        *
     * ********************************************************************** */
    private static final CssMetaData<CaretNode, javafx.util.Duration> BLINK_RATE = new CustomCssMetaData<>("-rtfx-blink-rate", StyleConverter.getDurationConverter(), javafx.util.Duration.millis(500), s -> s.blinkRate);

    private static final List<CssMetaData<? extends Styleable, ?>> CSS_META_DATA_LIST;

    static {
        List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(Path.getClreplacedCssMetaData());
        styleables.add(BLINK_RATE);
        CSS_META_DATA_LIST = Collections.unmodifiableList(styleables);
    }

    @Override
    public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
        return CSS_META_DATA_LIST;
    }

    public static List<CssMetaData<? extends Styleable, ?>> getClreplacedCssMetaData() {
        return CSS_META_DATA_LIST;
    }
}

17 View Complete Implementation : SelectionImpl.java
Copyright BSD 2-Clause "Simplified" License
Author : FXMisc
/**
 * Default implementation for {@link Selection}.
 */
public clreplaced SelectionImpl<PS, SEG, S> implements Selection<PS, SEG, S>, Comparable<SelectionImpl<PS, SEG, S>> {

    /* ********************************************************************** *
     *                                                                        *
     * 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.                          *
     *                                                                        *
     * ********************************************************************** */
    private final SuspendableVal<IndexRange> range;

    @Override
    public final IndexRange getRange() {
        return range.getValue();
    }

    @Override
    public final ObservableValue<IndexRange> rangeProperty() {
        return range;
    }

    private final SuspendableVal<Integer> length;

    @Override
    public final int getLength() {
        return length.getValue();
    }

    @Override
    public final ObservableValue<Integer> lengthProperty() {
        return length;
    }

    private final Val<Integer> paragraphSpan;

    @Override
    public final int getParagraphSpan() {
        return paragraphSpan.getValue();
    }

    @Override
    public final ObservableValue<Integer> paragraphSpanProperty() {
        return paragraphSpan;
    }

    private final SuspendableVal<StyledDoreplacedent<PS, SEG, S>> selectedDoreplacedent;

    @Override
    public final ObservableValue<StyledDoreplacedent<PS, SEG, S>> selectedDoreplacedentProperty() {
        return selectedDoreplacedent;
    }

    @Override
    public final StyledDoreplacedent<PS, SEG, S> getSelectedDoreplacedent() {
        return selectedDoreplacedent.getValue();
    }

    private final SuspendableVal<String> selectedText;

    @Override
    public final String getSelectedText() {
        return selectedText.getValue();
    }

    @Override
    public final ObservableValue<String> selectedTextProperty() {
        return selectedText;
    }

    private final SuspendableVal<Integer> startPosition;

    @Override
    public final int getStartPosition() {
        return startPosition.getValue();
    }

    @Override
    public final ObservableValue<Integer> startPositionProperty() {
        return startPosition;
    }

    private final Val<Integer> startParagraphIndex;

    @Override
    public final int getStartParagraphIndex() {
        return startParagraphIndex.getValue();
    }

    @Override
    public final ObservableValue<Integer> startParagraphIndexProperty() {
        return startParagraphIndex;
    }

    private final Val<Integer> startColumnPosition;

    @Override
    public final int getStartColumnPosition() {
        return startColumnPosition.getValue();
    }

    @Override
    public final ObservableValue<Integer> startColumnPositionProperty() {
        return startColumnPosition;
    }

    private final SuspendableVal<Integer> endPosition;

    @Override
    public final int getEndPosition() {
        return endPosition.getValue();
    }

    @Override
    public final ObservableValue<Integer> endPositionProperty() {
        return endPosition;
    }

    private final Val<Integer> endParagraphIndex;

    @Override
    public final int getEndParagraphIndex() {
        return endParagraphIndex.getValue();
    }

    @Override
    public final ObservableValue<Integer> endParagraphIndexProperty() {
        return endParagraphIndex;
    }

    private final Val<Integer> endColumnPosition;

    @Override
    public final int getEndColumnPosition() {
        return endColumnPosition.getValue();
    }

    @Override
    public final ObservableValue<Integer> endColumnPositionProperty() {
        return endColumnPosition;
    }

    private final SuspendableVal<Optional<Bounds>> bounds;

    @Override
    public final Optional<Bounds> getSelectionBounds() {
        return bounds.getValue();
    }

    @Override
    public final ObservableValue<Optional<Bounds>> selectionBoundsProperty() {
        return bounds;
    }

    private final SuspendableNo beingUpdated = new SuspendableNo();

    @Override
    public final boolean isBeingUpdated() {
        return beingUpdated.get();
    }

    @Override
    public final ObservableValue<Boolean> beingUpdatedProperty() {
        return beingUpdated;
    }

    private final GenericStyledArea<PS, SEG, S> area;

    @Override
    public GenericStyledArea<PS, SEG, S> getArea() {
        return area;
    }

    private final String name;

    @Override
    public String getSelectionName() {
        return name;
    }

    private final SuspendableNo dependentBeingUpdated;

    private final Var<IndexRange> internalRange;

    private final EventStream<?> dirty;

    private final Var<Position> start2DPosition;

    private final Val<Position> end2DPosition;

    private final Consumer<SelectionPath> configurePath;

    private Subscription subscription = () -> {
    };

    /**
     * Creates a selection with both the start and end position at 0.
     * @param name must be unique and is also used as a StyleClreplaced for
     * configuration via CSS using selectors from Path, Shape, and Node.
     */
    public SelectionImpl(String name, GenericStyledArea<PS, SEG, S> area) {
        this(name, area, 0, 0);
    }

    /**
     * Creates a selection with customized configuration via {@code configurePath}
     * with both the start and end position at 0.
     */
    public SelectionImpl(String name, GenericStyledArea<PS, SEG, S> area, Consumer<SelectionPath> configurePath) {
        this(name, area, 0, 0, area.beingUpdatedProperty(), configurePath);
    }

    /**
     * Creates a selection. Name must be unique and is also used as a StyleClreplaced
     * for configuration via CSS using selectors from Path, Shape, and Node.
     */
    public SelectionImpl(String name, GenericStyledArea<PS, SEG, S> area, int startPosition, int endPosition) {
        this(name, area, new IndexRange(startPosition, endPosition), area.beingUpdatedProperty());
    }

    /**
     * Creates a selection that is to be used in a {@link CaretSelectionBind}.
     */
    SelectionImpl(String name, GenericStyledArea<PS, SEG, S> area, int startPosition, int endPosition, SuspendableNo dependentBeingUpdated) {
        this(name, area, new IndexRange(startPosition, endPosition), dependentBeingUpdated);
    }

    /**
     * Creates a selection that is to be used in a {@link CaretSelectionBind} with customized configuration.
     */
    SelectionImpl(String name, GenericStyledArea<PS, SEG, S> area, int startPosition, int endPosition, SuspendableNo dependentBeingUpdated, Consumer<SelectionPath> configurePath) {
        this(name, area, new IndexRange(startPosition, endPosition), dependentBeingUpdated, configurePath);
    }

    /**
     * Creates a selection that is to be used in a {@link CaretSelectionBind}. It adds the style clreplaced
     * {@code selection} to any {@link SelectionPath} used to render this selection.
     */
    SelectionImpl(String name, GenericStyledArea<PS, SEG, S> area, IndexRange range, SuspendableNo dependentBeingUpdated) {
        this(name, area, range, dependentBeingUpdated, path -> path.getStyleClreplaced().add("selection"));
    }

    /**
     * Creates a selection that is to be used in a {@link CaretSelectionBind}
     * with customized configuration and starting at the given range.
     */
    SelectionImpl(String name, GenericStyledArea<PS, SEG, S> area, IndexRange range, SuspendableNo dependentBeingUpdated, Consumer<SelectionPath> configurePath) {
        this.name = name;
        this.area = area;
        this.dependentBeingUpdated = dependentBeingUpdated;
        this.configurePath = configurePath;
        internalRange = Var.newSimpleVar(range);
        this.range = internalRange.suspendable();
        length = internalRange.map(IndexRange::getLength).suspendable();
        Val<StyledDoreplacedent<PS, SEG, S>> doreplacedentVal = Val.create(() -> area.subDoreplacedent(internalRange.getValue()), internalRange, area.getParagraphs());
        selectedDoreplacedent = doreplacedentVal.suspendable();
        selectedText = doreplacedentVal.map(StyledDoreplacedent::getText).suspendable();
        start2DPosition = Var.newSimpleVar(position(0, 0));
        end2DPosition = start2DPosition.map(startPos2D -> getLength() == 0 ? startPos2D : startPos2D.offsetBy(getLength(), Backward));
        internalRange.addListener(obs -> {
            IndexRange sel = internalRange.getValue();
            start2DPosition.setValue(area.offsetToPosition(sel.getStart(), Forward));
        });
        startPosition = internalRange.map(IndexRange::getStart).suspendable();
        startParagraphIndex = start2DPosition.map(Position::getMajor);
        startColumnPosition = start2DPosition.map(Position::getMinor);
        endPosition = internalRange.map(IndexRange::getEnd).suspendable();
        endParagraphIndex = end2DPosition.map(Position::getMajor);
        endColumnPosition = end2DPosition.map(Position::getMinor);
        paragraphSpan = Val.combine(startParagraphIndex, endParagraphIndex, (startP, endP) -> endP - startP + 1);
        dirty = merge(invalidationsOf(rangeProperty()), invalidationsOf(area.getParagraphs()));
        bounds = Val.create(() -> area.getSelectionBoundsOnScreen(this), EventStreams.merge(area.viewportDirtyEvents(), dirty)).suspendable();
        manageSubscription(area.multiPlainChanges(), list -> {
            int finalStart = getStartPosition();
            int finalEnd = getEndPosition();
            for (PlainTextChange plainTextChange : list) {
                int netLength = plainTextChange.getNetLength();
                // if (netLength != 0)  Causes IndexOutOfBoundsException in ParagraphText.getRangeShapeSafely issue #689
                // but can be safely reimplemented if this causes other issues.
                {
                    int indexOfChange = plainTextChange.getPosition();
                    // in case of a replacement: "hello there" -> "hi."
                    int endOfChange = indexOfChange + Math.abs(netLength);
                    if (getLength() != 0) {
                        /*
                            "->" means add (positive) netLength to position
                            "<-" means add (negative) netLength to position
                            "x" means don't update position

                            "start / end" means what should be done in each case for each anchor if they differ

                            "+a" means one of the anchors was included in the deleted portion of content
                            "-a" means one of the anchors was not included in the deleted portion of content
                            Before/At/After means indexOfChange "<" / "==" / ">" position

                                   |   Before +a   | Before -a |   At   | After
                            -------+---------------+-----------+--------+------
                            Add    |      N/A      |    ->     | -> / x | x
                            Delete | indexOfChange |    <-     |    x   | x
                         */
                        if (indexOfChange == finalStart && netLength > 0) {
                            finalStart = finalStart + netLength;
                        } else if (indexOfChange < finalStart) {
                            finalStart = finalStart < endOfChange ? indexOfChange : finalStart + netLength;
                        }
                        if (indexOfChange < finalEnd) {
                            finalEnd = finalEnd < endOfChange ? indexOfChange : finalEnd + netLength;
                        }
                        if (finalStart > finalEnd) {
                            finalStart = finalEnd;
                        }
                    } else {
                        // force-update internalSelection in case empty selection is
                        // at the end of area and a character was deleted
                        // (prevents a StringIndexOutOfBoundsException because
                        // end is one char farther than area's length).
                        if (getEndPosition() > 0) {
                            finalStart = area.getLength();
                            finalEnd = finalStart;
                        }
                    }
                }
            }
            selectRange(finalStart, finalEnd);
        });
        Suspendable omniSuspendable = Suspendable.combine(// first, so it's released last
        beingUpdated, bounds, endPosition, startPosition, selectedText, selectedDoreplacedent, length, this.range);
        manageSubscription(omniSuspendable.suspendWhen(dependentBeingUpdated));
    }

    /* ********************************************************************** *
     *                                                                        *
     * Actions                                                                *
     *                                                                        *
     * Actions change the state of this control. They typically cause a       *
     * change of one or more observables and/or produce an event.             *
     *                                                                        *
     * ********************************************************************** */
    @Override
    public void selectRange(int startParagraphIndex, int startColPosition, int endParagraphIndex, int endColPosition) {
        selectRange(textPosition(startParagraphIndex, startColPosition), textPosition(endParagraphIndex, endColPosition));
    }

    @Override
    public void selectRange(int startPosition, int endPosition) {
        selectRange(new IndexRange(startPosition, endPosition));
    }

    private void selectRange(IndexRange range) {
        Runnable updateRange = () -> internalRange.setValue(range);
        if (dependentBeingUpdated.get()) {
            updateRange.run();
        } else {
            dependentBeingUpdated.suspendWhile(updateRange);
        }
    }

    @Override
    public void updateStartBy(int amount, Direction direction) {
        moveBoundary(direction, amount, getStartPosition(), newStartTextPos -> IndexRange.normalize(newStartTextPos, getEndPosition()));
    }

    @Override
    public void updateEndBy(int amount, Direction direction) {
        moveBoundary(direction, amount, getEndPosition(), newEndTextPos -> IndexRange.normalize(getStartPosition(), newEndTextPos));
    }

    @Override
    public void updateStartTo(int position) {
        selectRange(position, getEndPosition());
    }

    @Override
    public void updateStartTo(int paragraphIndex, int columnPosition) {
        selectRange(textPosition(paragraphIndex, columnPosition), getEndPosition());
    }

    @Override
    public void updateStartByBreaksForward(int numOfBreaks, BreakIterator breakIterator) {
        updateStartByBreaks(numOfBreaks, breakIterator, true);
    }

    @Override
    public void updateStartByBreaksBackward(int numOfBreaks, BreakIterator breakIterator) {
        updateStartByBreaks(numOfBreaks, breakIterator, false);
    }

    @Override
    public void updateEndTo(int position) {
        selectRange(getStartPosition(), position);
    }

    @Override
    public void updateEndTo(int paragraphIndex, int columnPosition) {
        selectRange(getStartPosition(), textPosition(paragraphIndex, columnPosition));
    }

    @Override
    public void updateEndByBreaksForward(int numOfBreaks, BreakIterator breakIterator) {
        updateEndByBreaks(numOfBreaks, breakIterator, true);
    }

    @Override
    public void updateEndByBreaksBackward(int numOfBreaks, BreakIterator breakIterator) {
        updateEndByBreaks(numOfBreaks, breakIterator, false);
    }

    @Override
    public void selectAll() {
        selectRange(0, area.getLength());
    }

    @Override
    public void selectParagraph(int paragraphIndex) {
        int start = textPosition(paragraphIndex, 0);
        int end = start + area.getParagraphLength(paragraphIndex);
        selectRange(start, end);
    }

    @Override
    public void selectWord(int wordPositionInArea) {
        if (area.getLength() == 0) {
            return;
        }
        BreakIterator breakIterator = BreakIterator.getWordInstance();
        breakIterator.setText(area.getText());
        breakIterator.preceding(wordPositionInArea);
        breakIterator.next();
        int wordStart = breakIterator.current();
        breakIterator.following(wordPositionInArea);
        breakIterator.next();
        int wordEnd = breakIterator.current();
        selectRange(wordStart, wordEnd);
    }

    @Override
    public void configureSelectionPath(SelectionPath path) {
        configurePath.accept(path);
    }

    @Override
    public boolean equals(Object obj) {
        return this == obj;
    }

    @Override
    public int hashCode() {
        return name.hashCode();
    }

    @Override
    public int compareTo(SelectionImpl<PS, SEG, S> o) {
        return Integer.compare(hashCode(), o.hashCode());
    }

    @Override
    public String toString() {
        return String.format("SelectionImpl(name=%s startPar=%s startCol=%s " + "endPar=%s endCol=%s paragraphSpan=%s " + "selectedDoreplacedent=%s", name, getStartParagraphIndex(), getStartColumnPosition(), getEndParagraphIndex(), getEndColumnPosition(), getParagraphSpan(), getSelectedDoreplacedent());
    }

    @Override
    public void dispose() {
        subscription.unsubscribe();
    }

    /* ********************************************************************** *
     *                                                                        *
     * Private methods                                                        *
     *                                                                        *
     * ********************************************************************** */
    private <T> void manageSubscription(EventStream<T> stream, Consumer<T> consumer) {
        manageSubscription(stream.subscribe(consumer));
    }

    private void manageSubscription(Subscription s) {
        subscription = subscription.and(s);
    }

    private IndexRange getParagraphSelection(int paragraph) {
        int startPar = getStartParagraphIndex();
        int endPar = getEndParagraphIndex();
        if (paragraph < startPar || paragraph > endPar) {
            return EMPTY_RANGE;
        }
        int start = paragraph == startPar ? getStartColumnPosition() : 0;
        int end = paragraph == endPar ? getEndColumnPosition() : area.getParagraphLength(paragraph) + 1;
        // force rangeProperty() to be valid
        // selection.getRange(); // not sure why this line is even here...
        return new IndexRange(start, end);
    }

    private Position position(int row, int col) {
        return area.position(row, col);
    }

    private int textPosition(int row, int col) {
        return position(row, col).toOffset();
    }

    private void moveBoundary(Direction direction, int amount, int oldBoundaryPosition, Function<Integer, IndexRange> updatedRange) {
        switch(direction) {
            case LEFT:
                moveBoundary(() -> oldBoundaryPosition - amount, (pos) -> 0 <= pos, updatedRange);
                break;
            default:
            case RIGHT:
                moveBoundary(() -> oldBoundaryPosition + amount, (pos) -> pos <= area.getLength(), updatedRange);
        }
    }

    private void moveBoundary(IntSupplier textPosition, Function<Integer, Boolean> boundsCheckPreplacedes, Function<Integer, IndexRange> updatedRange) {
        int newTextPosition = textPosition.getAsInt();
        if (boundsCheckPreplacedes.apply(newTextPosition)) {
            selectRange(updatedRange.apply(newTextPosition));
        }
    }

    private void updateStartByBreaks(int numOfBreaks, BreakIterator breakIterator, boolean forwardsNotBackwards) {
        updateSelectionByBreaks(numOfBreaks, breakIterator, forwardsNotBackwards, true);
    }

    private void updateEndByBreaks(int numOfBreaks, BreakIterator breakIterator, boolean forwardsNotBackwards) {
        updateSelectionByBreaks(numOfBreaks, breakIterator, forwardsNotBackwards, false);
    }

    private void updateSelectionByBreaks(int numOfBreaks, BreakIterator breakIterator, boolean followingNotPreceding, boolean updateStartNotEnd) {
        if (area.getLength() == 0) {
            return;
        }
        breakIterator.setText(area.getText());
        int pos;
        Runnable updateSelection;
        if (updateStartNotEnd) {
            pos = getStartPosition();
            updateSelection = () -> selectRange(breakIterator.current(), getEndPosition());
        } else {
            pos = getEndPosition();
            updateSelection = () -> selectRange(getStartPosition(), breakIterator.current());
        }
        if (followingNotPreceding) {
            breakIterator.following(pos);
        } else {
            breakIterator.preceding(pos);
        }
        breakIterator.next(numOfBreaks);
        updateSelection.run();
    }
}

17 View Complete Implementation : SubscribeableContentsObsSetTest.java
Copyright BSD 2-Clause "Simplified" License
Author : FXMisc
@Test
public void adding_subscriber_and_later_removing_it_will_unsubscribe_from_all_elements() {
    SubscribeableContentsObsSet<BoxedProperty> contentSet = new SubscribeableContentsObsSet<>();
    List<Integer> storageList = new LinkedList<>();
    // when property is set to a new value, store the new value in storageList
    Subscription removeSubscriber = contentSet.addSubscriber(b -> b.intValues.subscribe(storageList::add));
    BoxedProperty box1 = new BoxedProperty(1);
    BoxedProperty box2 = new BoxedProperty(2);
    contentSet.add(box1);
    contentSet.add(box2);
    box1.addOne();
    box2.addOne();
    replacedertEquals(2, storageList.size());
    storageList.clear();
    removeSubscriber.unsubscribe();
    box1.addOne();
    box2.addOne();
    replacedertEquals(0, storageList.size());
}

17 View Complete Implementation : SubscribeableContentsObsSetTest.java
Copyright BSD 2-Clause "Simplified" License
Author : FXMisc
@Test
public void adding_new_subscriber_when_list_has_contents_does_not_fire_invalidation_event() {
    SubscribeableContentsObsSet<Integer> contentSet = new SubscribeableContentsObsSet<>();
    contentSet.add(1);
    contentSet.add(2);
    contentSet.add(3);
    // when a change occurs add the additions/removals in another list
    SimpleBooleanProperty changeWasFired = new SimpleBooleanProperty(false);
    Subscription removeInvalidationListener = contentSet.addInvalidationListener(change -> changeWasFired.set(true));
    // when property is set to a new value, store the new value in storageList
    contentSet.addSubscriber(ignore -> Subscription.EMPTY);
    replacedertFalse(changeWasFired.get());
    // cleanup
    removeInvalidationListener.unsubscribe();
}

17 View Complete Implementation : SubscribeableContentsObsSetTest.java
Copyright BSD 2-Clause "Simplified" License
Author : FXMisc
@Test
public void adding_new_subscriber_when_list_has_contents_does_not_fire_change_event() {
    SubscribeableContentsObsSet<Integer> contentSet = new SubscribeableContentsObsSet<>();
    contentSet.add(1);
    contentSet.add(2);
    contentSet.add(3);
    SimpleBooleanProperty changeWasFired = new SimpleBooleanProperty(false);
    Subscription removeChangeListener = contentSet.addChangeListener(change -> changeWasFired.set(true));
    contentSet.addSubscriber(b -> Subscription.EMPTY);
    replacedertFalse(changeWasFired.get());
    // cleanup
    removeChangeListener.unsubscribe();
}

17 View Complete Implementation : UndoManagerImpl.java
Copyright BSD 2-Clause "Simplified" License
Author : FXMisc
/**
 * Implementation for {@link UndoManager} for single changes. For multiple changes, see
 * {@link MultiChangeUndoManagerImpl}.
 *
 * @param <C> the type of change to undo/redo
 */
public clreplaced UndoManagerImpl<C> implements UndoManager<C> {

    private clreplaced UndoPositionImpl implements UndoPosition {

        private final QueuePosition queuePos;

        UndoPositionImpl(QueuePosition queuePos) {
            this.queuePos = queuePos;
        }

        @Override
        public void mark() {
            mark = queuePos;
            canMerge = false;
            atMarkedPosition.invalidate();
        }

        @Override
        public boolean isValid() {
            return queuePos.isValid();
        }
    }

    private final ChangeQueue<C> queue;

    private final Function<? super C, ? extends C> invert;

    private final Consumer<C> apply;

    private final BiFunction<C, C, Optional<C>> merge;

    private final Predicate<C> isIdenreplacedy;

    private final Subscription subscription;

    private final SuspendableNo performingAction = new SuspendableNo();

    private final EventSource<Void> invalidationRequests = new EventSource<Void>();

    private final Val<C> nextUndo = new ValBase<C>() {

        @Override
        protected Subscription connect() {
            return invalidationRequests.subscribe(x -> invalidate());
        }

        @Override
        protected C computeValue() {
            return queue.hasPrev() ? queue.peekPrev() : null;
        }
    };

    private final Val<C> nextRedo = new ValBase<C>() {

        @Override
        protected Subscription connect() {
            return invalidationRequests.subscribe(x -> invalidate());
        }

        @Override
        protected C computeValue() {
            return queue.hasNext() ? queue.peekNext() : null;
        }
    };

    private final BooleanBinding atMarkedPosition = new BooleanBinding() {

        {
            invalidationRequests.addObserver(x -> this.invalidate());
        }

        @Override
        protected boolean computeValue() {
            return mark.equals(queue.getCurrentPosition());
        }
    };

    private boolean canMerge;

    private QueuePosition mark;

    private C expectedChange = null;

    public UndoManagerImpl(ChangeQueue<C> queue, Function<? super C, ? extends C> invert, Consumer<C> apply, BiFunction<C, C, Optional<C>> merge, Predicate<C> isIdenreplacedy, EventStream<C> changeSource) {
        this(queue, invert, apply, merge, isIdenreplacedy, changeSource, Duration.ZERO);
    }

    public UndoManagerImpl(ChangeQueue<C> queue, Function<? super C, ? extends C> invert, Consumer<C> apply, BiFunction<C, C, Optional<C>> merge, Predicate<C> isIdenreplacedy, EventStream<C> changeSource, Duration preventMergeDelay) {
        this.queue = queue;
        this.invert = invert;
        this.apply = apply;
        this.merge = merge;
        this.isIdenreplacedy = isIdenreplacedy;
        this.mark = queue.getCurrentPosition();
        Subscription mainSub = changeSource.subscribe(this::changeObserved);
        if (preventMergeDelay.isZero() || preventMergeDelay.isNegative()) {
            subscription = mainSub;
        } else {
            Subscription sub2 = changeSource.successionEnds(preventMergeDelay).subscribe(ignore -> preventMerge());
            subscription = mainSub.and(sub2);
        }
    }

    @Override
    public void close() {
        subscription.unsubscribe();
    }

    @Override
    public boolean undo() {
        return applyChange(isUndoAvailable(), () -> invert.apply(queue.prev()));
    }

    @Override
    public boolean redo() {
        return applyChange(isRedoAvailable(), queue::next);
    }

    @Override
    public Val<C> nextUndoProperty() {
        return nextUndo;
    }

    @Override
    public Val<C> nextRedoProperty() {
        return nextRedo;
    }

    @Override
    public boolean isUndoAvailable() {
        return nextUndo.isPresent();
    }

    @Override
    public Val<Boolean> undoAvailableProperty() {
        return nextUndo.map(c -> true).orElseConst(false);
    }

    @Override
    public boolean isRedoAvailable() {
        return nextRedo.isPresent();
    }

    @Override
    public Val<Boolean> redoAvailableProperty() {
        return nextRedo.map(c -> true).orElseConst(false);
    }

    @Override
    public boolean isPerformingAction() {
        return performingAction.get();
    }

    @Override
    public ObservableBooleanValue performingActionProperty() {
        return performingAction;
    }

    @Override
    public boolean isAtMarkedPosition() {
        return atMarkedPosition.get();
    }

    @Override
    public ObservableBooleanValue atMarkedPositionProperty() {
        return atMarkedPosition;
    }

    @Override
    public UndoPosition getCurrentPosition() {
        return new UndoPositionImpl(queue.getCurrentPosition());
    }

    @Override
    public void preventMerge() {
        canMerge = false;
    }

    @Override
    public void forgetHistory() {
        queue.forgetHistory();
        invalidateProperties();
    }

    /**
     * Helper method for reducing code duplication
     *
     * @param isChangeAvailable same as `isUndoAvailable()` [Undo] or `isRedoAvailable()` [Redo]
     * @param changeToApply same as `invert.apply(queue.prev())` [Undo] or `queue.next()` [Redo]
     */
    private boolean applyChange(boolean isChangeAvailable, Supplier<C> changeToApply) {
        if (isChangeAvailable) {
            canMerge = false;
            // perform change
            C change = changeToApply.get();
            this.expectedChange = change;
            performingAction.suspendWhile(() -> apply.accept(change));
            if (this.expectedChange != null) {
                throw new IllegalStateException("Expected change not received:\n" + this.expectedChange);
            }
            invalidateProperties();
            return true;
        } else {
            return false;
        }
    }

    private void changeObserved(C change) {
        if (expectedChange == null) {
            if (!isIdenreplacedy.test(change)) {
                addChange(change);
            }
        } else if (expectedChange.equals(change)) {
            expectedChange = null;
        } else {
            throw new IllegalArgumentException("Unexpected change received." + "\nExpected:\n" + expectedChange + "\nReceived:\n" + change);
        }
    }

    @SuppressWarnings("unchecked")
    private void addChange(C change) {
        if (canMerge && queue.hasPrev()) {
            C prev = queue.prev();
            // attempt to merge the changes
            Optional<C> merged = merge.apply(prev, change);
            if (merged.isPresent()) {
                if (isIdenreplacedy.test(merged.get())) {
                    canMerge = false;
                    // clears the future
                    queue.push();
                } else {
                    canMerge = true;
                    queue.push(merged.get());
                }
            } else {
                canMerge = true;
                queue.next();
                queue.push(change);
            }
        } else {
            queue.push(change);
            canMerge = true;
        }
        invalidateProperties();
    }

    private void invalidateProperties() {
        invalidationRequests.push(null);
    }
}

17 View Complete Implementation : EventLogController.java
Copyright BSD 2-Clause "Simplified" License
Author : pmd
public void showPopup(Subscription extSub) {
    myPopupStage.show();
    Subscription popupBinding = bindPopupToThisController().and(extSub);
    eventLogTableView.refresh();
    myPopupStage.setOnCloseRequest(e -> popupBinding.unsubscribe());
}

16 View Complete Implementation : SubscribeableContentsObsSet.java
Copyright BSD 2-Clause "Simplified" License
Author : FXMisc
/**
 * Subscribes to all current and future elements' internal changes in this set until either they are removed
 * or this subscriber is removed by calling {@link Subscription#unsubscribe() unsubscribe} on the function's
 * returned {@link Subscription}.
 */
public Subscription addSubscriber(Function<? super E, Subscription> subscriber) {
    Objects.requireNonNull(subscriber);
    subscribers.add(subscriber);
    List<E> keys = new ArrayList<>(map.keySet());
    keys.forEach(key -> {
        List<Subscription> otherSubs = map.get(key);
        Subscription sub = subscriber.apply(key);
        otherSubs.add(sub);
        map.put(key, otherSubs);
    });
    return () -> removeSubscriber(subscriber);
}

16 View Complete Implementation : EventLogController.java
Copyright BSD 2-Clause "Simplified" License
Author : pmd
/**
 * Binds the popup to the rest of the app. Necessarily performed after the initialization
 * of the controller (ie @FXML fields are non-null). All bindings must be revocable
 * with the returned subscription, that way no processing is done when the popup is not
 * shown.
 */
private Subscription bindPopupToThisController() {
    Subscription binding = valuesOf(eventLogTableView.getSelectionModel().selectedItemProperty()).distinct().subscribe(this::onExceptionSelectionChanges);
    // reset error nodes on closing
    binding = binding.and(() -> selectedErrorNodes.setValue(Collections.emptyList()));
    SortedList<LogEntry> logEntries = new SortedList<>(getLogger().getLog(), Comparator.reverseOrder());
    eventLogTableView.itemsProperty().setValue(logEntries);
    binding = binding.and(() -> eventLogTableView.itemsProperty().setValue(FXCollections.emptyObservableList()));
    myPopupStage.replacedleProperty().bind(this.replacedleProperty());
    binding = binding.and(() -> myPopupStage.replacedleProperty().unbind());
    return binding;
}

15 View Complete Implementation : SearchableTreeView.java
Copyright BSD 2-Clause "Simplified" License
Author : pmd
/**
 * Update the cells to search for anything.
 */
private Subscription bindSearchQuery(ObservableValue<String> query, Var<Integer> numResults, javafx.scene.Node eventSource) {
    Val<List<SearchableTreeItem<T>>> allItems = Val.wrap(rootProperty()).map(it1 -> getRealRoot()).map(it1 -> {
        List<SearchableTreeItem<T>> tmp = new ArrayList<>();
        it1.foreach(tmp::add);
        return tmp;
    }).orElseConst(Collections.emptyList());
    return ReactfxUtil.subscribeDisposable(query, q -> {
        Val<List<MatchResult<SearchableTreeItem<T>>>> selectedResults = allItems.map(it -> selectMatches(q, it));
        return ReactfxUtil.subscribeDisposable(selectedResults, newRes -> {
            numResults.setValue(newRes.size());
            // the values are never null, at most empty, because of orElseConst above
            newRes.forEach(res -> res.getData().currentSearchResult.setValue(res));
            Subscription sub = Subscription.EMPTY;
            if (!newRes.isEmpty()) {
                Var<Integer> curIdx = Var.newSimpleVar(0);
                curIdx.values().subscribe(idx -> {
                    SearchableTreeItem<T> item = newRes.get(idx).getData();
                    int row = getRow(item);
                    getSelectionModel().select(row);
                    if (!myWrapper.isIndexVisible(row)) {
                        int safe = row < 3 ? 0 : row - 3;
                        scrollTo(safe);
                    }
                });
                sub = sub.and(subscribeKeyNav(newRes.size(), curIdx, eventSource));
            }
            refresh();
            return sub;
        }).and(() -> {
            selectedResults.ifPresent(lst -> lst.forEach(it -> it.getData().currentSearchResult.setValue(null)));
            refresh();
        });
    });
}

15 View Complete Implementation : ListSizeTest.java
Copyright BSD 2-Clause "Simplified" License
Author : TomasMikula
@Test
public void test() {
    ObservableList<Integer> list = FXCollections.observableArrayList();
    Val<Integer> size = LiveList.sizeOf(list);
    List<Integer> sizes = new ArrayList<>();
    Subscription sub = EventStreams.valuesOf(size).subscribe(sizes::add);
    list.add(1);
    list.addAll(2, 3, 4);
    replacedertEquals(Arrays.asList(0, 1, 4), sizes);
    sub.unsubscribe();
    sizes.clear();
    list.addAll(5, 6);
    replacedertEquals(Arrays.asList(), sizes);
    EventStreams.valuesOf(size).subscribe(sizes::add);
    list.addAll(7, 8);
    replacedertEquals(Arrays.asList(6, 8), sizes);
}

13 View Complete Implementation : SourceEditorController.java
Copyright BSD 2-Clause "Simplified" License
Author : pmd
private void handleTestOpenRequest(@NonNull LiveTestCase oldValue, @NonNull LiveTestCase newValue) {
    oldValue.commitChanges();
    if (!newValue.getSource().equals(nodeEditionCodeArea.getText())) {
        nodeEditionCodeArea.replaceText(newValue.getSource());
    }
    if (newValue.getLanguageVersion() == null) {
        newValue.setLanguageVersion(globalLanguageProperty().getValue().getDefaultVersion());
    }
    Subscription sub = Subscription.multi(ReactfxUtil.rewireInit(newValue.sourceProperty(), astManager.sourceCodeProperty()), ReactfxUtil.rewireInit(newValue.languageVersionProperty(), languageVersionUIProperty), () -> propertiesPopover.rebind(null));
    newValue.addCommitHandler(t -> sub.unsubscribe());
}

13 View Complete Implementation : Highlight.java
Copyright GNU Lesser General Public License v3.0
Author : tronprotocol
/**
 * Do highlighting.
 */
public final Subscription highlight() {
    final String code = codeArea.getText();
    final Subscription subscription = codeArea.richChanges().filter(ch -> !ch.getInserted().equals(ch.getRemoved())).successionEnds(Duration.ofMillis(500)).supplyTask(this::computeHighlightingAsync).awaitLatest(codeArea.richChanges()).filterMap(t -> {
        if (t.isSuccess()) {
            return Optional.of(t.get());
        }
        t.getFailure().printStackTrace();
        return Optional.empty();
    }).subscribe(this::applyHighlighting);
    codeArea.replaceText(code);
    return subscription;
}

12 View Complete Implementation : ContentEditor.java
Copyright MIT License
Author : warmuuh
private void setupCodeArea() {
    codeArea = new CodeArea();
    // codeArea.setWrapText(true);
    setupParagraphGraphics();
    EventStream<Object> highLightTrigger = EventStreams.merge(codeArea.multiPlainChanges(), EventStreams.changesOf(highlighters.getSelectionModel().selectedItemProperty()), EventStreams.eventsOf(format, MouseEvent.MOUSE_CLICKED));
    // sync highlighting:
    // Subscription cleanupWhenNoLongerNeedIt = highLightTrigger
    // .successionEnds(Duration.ofMillis(500))
    // .subscribe(ignore -> {
    // System.out.println("Triggered highlight via end-of-succession");
    // highlightCode();
    // });
    // async highlighting:
    Subscription cleanupWhenNoLongerNeedIt = highLightTrigger.successionEnds(Duration.ofMillis(500)).supplyTask(this::highlightCodeAsync).awaitLatest(codeArea.multiPlainChanges()).filterMap(t -> {
        if (t.isSuccess()) {
            return Optional.of(t.get());
        } else {
            t.getFailure().printStackTrace();
            return Optional.empty();
        }
    }).subscribe(this::applyHighlighting);
    val keyCombination = new KeyCodeCombination(KeyCode.F, KeyCombination.CONTROL_DOWN);
    codeArea.setOnKeyPressed(e -> {
        if (keyCombination.match(e)) {
            focusSearch();
        }
    });
}

11 View Complete Implementation : MainDesignerController.java
Copyright BSD 2-Clause "Simplified" License
Author : pmd
@Override
protected void beforeParentInit() {
    getDesignerRoot().getService(DesignerRoot.PERSISTENCE_MANAGER).restoreSettings(this);
    licenseMenuItem.setOnAction(e -> showLicensePopup());
    openFileMenuItem.setOnAction(e -> onOpenFileClicked());
    openRecentMenu.setOnAction(e -> updateRecentFilesMenu());
    openRecentMenu.setOnShowing(e -> updateRecentFilesMenu());
    saveMenuItem.setOnAction(e -> getService(DesignerRoot.PERSISTENCE_MANAGER).persistSettings(this));
    fileMenu.setOnShowing(e -> onFileMenuShowing());
    aboutMenuItem.setOnAction(e -> SimplePopups.showAboutPopup(getDesignerRoot()));
    docMenuItem.setOnAction(e -> getService(DesignerRoot.HOST_SERVICES).showDoreplacedent(DesignerUtil.DESIGNER_DOC_URL));
    reportIssueMenuItem.setOnAction(e -> getService(DesignerRoot.HOST_SERVICES).showDoreplacedent(DesignerUtil.DESIGNER_NEW_ISSUE_URL));
    setupAuxclreplacedpathMenuItem.setOnAction(e -> sourceEditorController.showAuxclreplacedpathSetupPopup());
    openEventLogMenuItem.setOnAction(e -> {
        EventLogController wizard = eventLogController.get();
        Subscription parentToWizSubscription = wizard.errorNodesProperty().values().subscribe(sourceEditorController.currentErrorNodesProperty()::setValue);
        wizard.showPopup(parentToWizSubscription);
    });
    openEventLogMenuItem.textProperty().bind(getLogger().numNewLogEntriesProperty().map(i -> "Event _Log (" + (i > 0 ? i : "no") + " new)"));
    initLanguageChoicebox();
}

11 View Complete Implementation : PropertyCollectionView.java
Copyright BSD 2-Clause "Simplified" License
Author : pmd
private PopOver rebindPopover(PropertyDescriptorSpec newSpec, PopOver pop) {
    if (pop == null) {
        // create it
        return detailsPopOver(newSpec);
    }
    Optional.ofNullable(pop.getOnHiding()).ifPresent(it -> it.handle(null));
    pop.replacedleProperty().bind(newSpec.nameProperty().filter(StringUtils::isNotBlank).orElseConst("(no name)").map(it -> "Property '" + it + "'"));
    EditPropertyDialogController wizard = (EditPropertyDialogController) pop.getUserData();
    Subscription sub = wizard.bindToDescriptor(newSpec, view.gereplacedems()).and(pop.replacedleProperty()::unbind);
    pop.setOnHiding(we -> sub.unsubscribe());
    return pop;
}

9 View Complete Implementation : JavaKeywordsDemo.java
Copyright GNU Lesser General Public License v3.0
Author : javafxchina
@Override
public void start(Stage primaryStage) {
    CodeArea codeArea = new CodeArea();
    // add line numbers to the left of area
    codeArea.setParagraphGraphicFactory(LineNumberFactory.get(codeArea));
    // recompute the syntax highlighting 500 ms after user stops editing area
    Subscription cleanupWhenNoLongerNeedIt = codeArea.multiPlainChanges().successionEnds(Duration.ofMillis(500)).subscribe(ignore -> codeArea.setStyleSpans(0, computeHighlighting(codeArea.getText())));
    // when no longer need syntax highlighting and wish to clean up memory leaks
    // run: `cleanupWhenNoLongerNeedIt.unsubscribe();`
    codeArea.replaceText(0, 0, sampleCode);
    Scene scene = new Scene(new StackPane(new VirtualizedScrollPane<>(codeArea)), 600, 400);
    scene.getStylesheets().add(JavaKeywordsAsyncDemo.clreplaced.getResource("java-keywords.css").toExternalForm());
    primaryStage.setScene(scene);
    primaryStage.setreplacedle("Java Keywords Demo");
    primaryStage.show();
}

7 View Complete Implementation : JavaKeywordsAsyncDemo.java
Copyright BSD 2-Clause "Simplified" License
Author : FXMisc
@Override
public void start(Stage primaryStage) {
    executor = Executors.newSingleThreadExecutor();
    codeArea = new CodeArea();
    codeArea.setParagraphGraphicFactory(LineNumberFactory.get(codeArea));
    Subscription cleanupWhenDone = codeArea.multiPlainChanges().successionEnds(Duration.ofMillis(500)).supplyTask(this::computeHighlightingAsync).awaitLatest(codeArea.multiPlainChanges()).filterMap(t -> {
        if (t.isSuccess()) {
            return Optional.of(t.get());
        } else {
            t.getFailure().printStackTrace();
            return Optional.empty();
        }
    }).subscribe(this::applyHighlighting);
    // call when no longer need it: `cleanupWhenFinished.unsubscribe();`
    codeArea.replaceText(0, 0, sampleCode);
    Scene scene = new Scene(new StackPane(new VirtualizedScrollPane<>(codeArea)), 600, 400);
    scene.getStylesheets().add(JavaKeywordsAsyncDemo.clreplaced.getResource("java-keywords.css").toExternalForm());
    primaryStage.setScene(scene);
    primaryStage.setreplacedle("Java Keywords Async Demo");
    primaryStage.show();
}

7 View Complete Implementation : SpellCheckingDemo.java
Copyright BSD 2-Clause "Simplified" License
Author : FXMisc
@Override
public void start(Stage primaryStage) {
    StyleClreplacededTextArea textArea = new StyleClreplacededTextArea();
    textArea.setWrapText(true);
    Subscription cleanupWhenFinished = textArea.multiPlainChanges().successionEnds(Duration.ofMillis(500)).subscribe(change -> {
        textArea.setStyleSpans(0, computeHighlighting(textArea.getText()));
    });
    // call when no longer need it: `cleanupWhenFinished.unsubscribe();`
    // load the dictionary
    try (InputStream input = getClreplaced().getResourcereplacedtream("spellchecking.dict");
        BufferedReader br = new BufferedReader(new InputStreamReader(input))) {
        String line;
        while ((line = br.readLine()) != null) {
            dictionary.add(line);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    // load the sample doreplacedent
    InputStream input2 = getClreplaced().getResourcereplacedtream("spellchecking.txt");
    try (java.util.Scanner s = new java.util.Scanner(input2)) {
        String doreplacedent = s.useDelimiter("\\A").hasNext() ? s.next() : "";
        textArea.replaceText(0, 0, doreplacedent);
    }
    Scene scene = new Scene(new StackPane(new VirtualizedScrollPane<>(textArea)), 600, 400);
    scene.getStylesheets().add(getClreplaced().getResource("spellchecking.css").toExternalForm());
    primaryStage.setScene(scene);
    primaryStage.setreplacedle("Spell Checking Demo");
    primaryStage.show();
}

7 View Complete Implementation : SearchableTreeView.java
Copyright BSD 2-Clause "Simplified" License
Author : pmd
private void popSearchField() {
    TextField textField = new TextField();
    textField.setPrefWidth(150);
    textField.setPromptText("Search tree");
    ControlUtil.makeTextFieldShowPromptEvenIfFocused(textField);
    Label label = new Label();
    label.getStyleClreplaced().addAll("hint-label");
    label.setTooltip(new Tooltip("Go to next result with F3"));
    StackPane pane = new StackPane();
    pane.getStyleClreplaced().addAll("search-popup");
    pane.getStylesheets().addAll(DesignerUtil.getCss("designer").toString());
    StackPane.setAlignment(textField, Pos.TOP_RIGHT);
    StackPane.setAlignment(label, Pos.BOTTOM_RIGHT);
    pane.getChildren().addAll(textField, label);
    Val<String> query = Val.wrap(textField.textProperty()).filter(StringUtils::isNotBlank).map(String::trim).filter(it -> it.length() >= MIN_QUERY_LENGTH);
    Var<Integer> numResults = Var.newSimpleVar(0);
    Subscription subscription = bindSearchQuery(query.conditionOnShowing(pane), numResults, textField);
    label.textProperty().bind(numResults.map(n -> n == 0 ? "no match" : n == 1 ? "1 match" : n + " matches"));
    label.visibleProperty().bind(query.map(Objects::nonNull));
    Popup popup = new Popup();
    popup.getContent().addAll(pane);
    popup.setAutoHide(true);
    popup.setHideOnEscape(true);
    Bounds bounds = localToScreen(getBoundsInLocal());
    popup.show(this, bounds.getMaxX() - textField.getPrefWidth() - 1, bounds.getMinY());
    popup.setOnHidden(e -> {
        openSearchField = null;
        subscription.unsubscribe();
    });
    // release resources
    // Hide popup when ENTER or ESCAPE is pressed
    EventStreams.eventsOf(popup, KeyEvent.KEY_RELEASED).filter(it -> it.getCode() == KeyCode.ENTER || it.getCode() == KeyCode.ESCAPE).subscribeForOne(e -> {
        popup.hide();
        e.consume();
    });
    textField.requestFocus();
    openSearchField = textField;
}

6 View Complete Implementation : NodeEditionCodeArea.java
Copyright BSD 2-Clause "Simplified" License
Author : pmd
public IntFunction<javafx.scene.Node> testCaseLineNumberFactory(LiveTestCase liveTestCase) {
    IntFunction<javafx.scene.Node> base = defaultLineNumberFactory();
    Val<Map<Integer, LiveList<LiveViolationRecord>>> mapVal = ReactfxUtil.groupBy(liveTestCase.getExpectedViolations(), (LiveViolationRecord v) -> v.getRange().startPos.line);
    Subscription pin = mapVal.pin();
    liveTestCase.addCommitHandler(t -> pin.unsubscribe());
    Val<IntFunction<Val<Integer>>> map1 = mapVal.map(it -> (int j) -> Optional.ofNullable(it.get(j)).orElse(new LiveArrayList<>()).sizeProperty());
    IntFunction<Val<Integer>> numViolationsPerLine = i -> map1.flatMap(it -> it.apply(i));
    return idx -> {
        javafx.scene.Node label = base.apply(idx);
        HBox hBox = new HBox();
        hBox.setSpacing(3);
        Label foo = buildExpectedLabel(numViolationsPerLine, idx);
        hBox.getChildren().addAll(foo, label);
        return hBox;
    };
}

3 View Complete Implementation : PopupDemo.java
Copyright BSD 2-Clause "Simplified" License
Author : FXMisc
@Override
public void start(Stage primaryStage) {
    stage = primaryStage;
    StringBuilder sb = new StringBuilder();
    for (int i = 2; i < 100; i++) {
        sb.append(String.valueOf(i)).append("        END\n");
    }
    InlineCssTextArea area = new InlineCssTextArea("Hello popup!\n" + sb.toString());
    area.setWrapText(true);
    VirtualizedScrollPane<InlineCssTextArea> vsPane = new VirtualizedScrollPane<>(area);
    BoundsPopup caretPopup = new BoundsPopup("I am the caret popup button!");
    BoundsPopup selectionPopup = new BoundsPopup("I am the selection popup button!");
    VBox caretOptions = createPopupOptions(caretPopup, "Show/Hide caret-based popup", "Show/Hide popup even when caret is out of viewport");
    VBox selectionOptions = createPopupOptions(selectionPopup, "Show/Hide selection-based popup", "Show/Hide popup even when selection is out of viewport");
    BorderPane borderPane = new BorderPane();
    borderPane.setTop(caretOptions);
    borderPane.setCenter(vsPane);
    borderPane.setBottom(selectionOptions);
    primaryStage.setScene(new Scene(borderPane, 400, 500));
    primaryStage.setreplacedle("Popup Demo");
    primaryStage.show();
    // ### Set up EventStreams
    // update labels depending on whether item is within viewport
    EventStream<Optional<Bounds>> caretBounds = nonNullValuesOf(area.caretBoundsProperty());
    Subscription cBoundsSub = feedVisibilityToLabelText(caretBounds, caretPopup, "Caret");
    EventStream<Optional<Bounds>> selectionBounds = nonNullValuesOf(area.selectionBoundsProperty());
    Subscription sBoundsSub = feedVisibilityToLabelText(selectionBounds, selectionPopup, "Selection");
    // set up event streams to update popups every time bounds change
    double caretXOffset = 0;
    double caretYOffset = 0;
    double selectionXOffset = 30;
    double selectionYOffset = 30;
    Subscription caretPopupSub = EventStreams.combine(caretBounds, caretPopup.outsideViewportValues()).subscribe(tuple3 -> {
        Optional<Bounds> opt = tuple3._1;
        boolean showPopupWhenCaretOutside = tuple3._2;
        if (opt.isPresent()) {
            Bounds b = opt.get();
            caretPopup.setX(b.getMaxX() + caretXOffset);
            caretPopup.setY(b.getMaxY() + caretYOffset);
            if (caretPopup.isHiddenTemporarily()) {
                caretPopup.show(stage);
                caretPopup.setHideTemporarily(false);
            }
        } else {
            if (!showPopupWhenCaretOutside) {
                caretPopup.hide();
                caretPopup.setHideTemporarily(true);
            }
        }
    });
    Subscription selectionPopupSub = EventStreams.combine(selectionBounds, selectionPopup.outsideViewportValues()).subscribe(tuple3 -> {
        Optional<Bounds> opt = tuple3._1;
        boolean showPopupWhenSelectionOutside = tuple3._2;
        if (opt.isPresent()) {
            Bounds b = opt.get();
            selectionPopup.setX(b.getMinX() + selectionXOffset + caretPopup.getWidth());
            selectionPopup.setY(b.getMinY() + selectionYOffset);
            if (selectionPopup.isHiddenTemporarily()) {
                selectionPopup.show(stage);
                selectionPopup.setHideTemporarily(false);
            }
        } else {
            if (!showPopupWhenSelectionOutside) {
                selectionPopup.hide();
                selectionPopup.setHideTemporarily(true);
            }
        }
    });
    Subscription caretSubs = caretPopupSub.and(cBoundsSub);
    Subscription selectionSubs = selectionPopupSub.and(sBoundsSub);
    caretPopup.show(primaryStage);
    selectionPopup.show(primaryStage);
    area.moveTo(0);
    area.requestFollowCaret();
}

0 View Complete Implementation : JavaKeywordsDemo.java
Copyright BSD 2-Clause "Simplified" License
Author : FXMisc
@Override
public void start(Stage primaryStage) {
    CodeArea codeArea = new CodeArea();
    // add line numbers to the left of area
    codeArea.setParagraphGraphicFactory(LineNumberFactory.get(codeArea));
    // recompute the syntax highlighting 500 ms after user stops editing area
    Subscription cleanupWhenNoLongerNeedIt = codeArea.multiPlainChanges().successionEnds(Duration.ofMillis(500)).subscribe(ignore -> codeArea.setStyleSpans(0, computeHighlighting(codeArea.getText())));
    // when no longer need syntax highlighting and wish to clean up memory leaks
    // run: `cleanupWhenNoLongerNeedIt.unsubscribe();`
    // auto-indent: insert previous line's indents on enter
    final Pattern whiteSpace = Pattern.compile("^\\s+");
    codeArea.addEventHandler(KeyEvent.KEY_PRESSED, KE -> {
        if (KE.getCode() == KeyCode.ENTER) {
            int caretPosition = codeArea.getCaretPosition();
            int currentParagraph = codeArea.getCurrentParagraph();
            Matcher m0 = whiteSpace.matcher(codeArea.getParagraph(currentParagraph - 1).getSegments().get(0));
            if (m0.find())
                Platform.runLater(() -> codeArea.insertText(caretPosition, m0.group()));
        }
    });
    codeArea.replaceText(0, 0, sampleCode);
    Scene scene = new Scene(new StackPane(new VirtualizedScrollPane<>(codeArea)), 600, 400);
    scene.getStylesheets().add(JavaKeywordsAsyncDemo.clreplaced.getResource("java-keywords.css").toExternalForm());
    primaryStage.setScene(scene);
    primaryStage.setreplacedle("Java Keywords Demo");
    primaryStage.show();
}

0 View Complete Implementation : TestCaseListCell.java
Copyright BSD 2-Clause "Simplified" License
Author : pmd
@Override
protected Pair<Node, Subscription> getNonEditingGraphic(LiveTestCase testCase) {
    HBox hBox = new HBox();
    hBox.setSpacing(10);
    FontIcon statusIcon = new FontIcon();
    Label statusLabel = new Label();
    statusLabel.setGraphic(statusIcon);
    statusLabel.getStyleClreplaced().addAll("status-label");
    // todo subscription
    Subscription sub = testCase.statusProperty().changes().subscribe(ch -> {
        TestResult st = ch.getNewValue();
        statusIcon.getStyleClreplaced().setAll(st.getStatus().getStyleClreplaced());
        statusIcon.setIconLiteral(st.getStatus().getIcon());
        this.getStyleClreplaced().removeAll(TestStatus.allStyleClreplacedes());
        this.getStyleClreplaced().addAll(st.getStatus().getStyleClreplaced());
        if (ch.getOldValue() != null && st.getStatus() != ch.getOldValue().getStatus() && st.getStatus() == TestStatus.FAIL) {
            getStatusTransition(st.getStatus()).play();
        }
        String message = st.getMessage();
        if (message != null) {
            statusLabel.setTooltip(new Tooltip(message));
        } else {
            statusLabel.setTooltip(null);
        }
    });
    Label descriptionLabel = new Label();
    ControlUtil.bindLabelPropertyWithDefault(descriptionLabel, "(no description)", testCase.descriptionProperty());
    ControlUtil.registerDoubleClickListener(descriptionLabel, this::doStartEdit);
    Button editDescription = new Button();
    editDescription.setGraphic(new FontIcon("far-edit"));
    editDescription.getStyleClreplaced().addAll("edit-test-description", "icon-button");
    Tooltip.install(editDescription, new Tooltip("Edit test description"));
    editDescription.setOnAction(e1 -> doStartEdit());
    sub = sub.and(() -> editDescription.setOnAction(null));
    Pane spacer = new Pane();
    HBox.setHgrow(spacer, Priority.ALWAYS);
    Button duplicate = new Button();
    duplicate.setGraphic(new FontIcon("far-copy"));
    duplicate.getStyleClreplaced().addAll("duplicate-test", "icon-button");
    Tooltip.install(duplicate, new Tooltip("Copy test case"));
    duplicate.setOnAction(e1 -> collection.duplicate(testCase));
    sub = sub.and(() -> duplicate.setOnAction(null));
    ToggleButton load = new ToggleButton();
    load.setToggleGroup(collection.getLoadedToggleGroup());
    load.setGraphic(new FontIcon("fas-external-link-alt"));
    load.getStyleClreplaced().addAll("load-button", "icon-button");
    Tooltip.install(load, new Tooltip("Load test case in editor"));
    load.setUserData(testCase);
    load.setOnAction(e -> {
        if (load.isSelected()) {
            collection.loadTestCase(getIndex());
        } else {
            collection.unloadTestCase();
        }
    });
    Val.wrap(load.selectedProperty()).values().distinct().subscribe(it -> pseudoClreplacedStateChanged(PseudoClreplaced.getPseudoClreplaced("loaded-test"), it));
    sub = sub.and(() -> {
        collection.getLoadedToggleGroup().getToggles().removeAll(load);
        load.setOnAction(null);
    });
    Button delete = new Button();
    delete.setGraphic(new FontIcon("fas-trash-alt"));
    delete.getStyleClreplaced().addAll("delete-button", "icon-button");
    Tooltip.install(delete, new Tooltip("Remove test case"));
    delete.setOnAction(e -> collection.deleteTestCase(testCase));
    ControlUtil.registerDoubleClickListener(spacer, load::fire);
    hBox.getChildren().setAll(statusLabel, descriptionLabel, editDescription, spacer, delete, duplicate, load);
    hBox.setAlignment(Pos.CENTER_LEFT);
    MyXPathSubscriber subscriber = new MyXPathSubscriber(testCase, collection.getDesignerRoot());
    sub = sub.and(subscriber.init(getManagerOf(testCase)));
    if (!testCase.isFrozen() && !load.isSelected()) {
        load.setSelected(true);
        load.getOnAction().handle(new ActionEvent());
    }
    ControlUtil.makeListCellFitListViewWidth(this);
    return new Pair<>(hBox, sub);
}