Search in sources :

Example 1 with EventStream

use of org.reactfx.EventStream in project RichTextFX by FXMisc.

the class GenericStyledArea method createCell.

/* ********************************************************************** *
     *                                                                        *
     * Private methods                                                        *
     *                                                                        *
     * ********************************************************************** */
private Cell<Paragraph<PS, SEG, S>, ParagraphBox<PS, SEG, S>> createCell(Paragraph<PS, SEG, S> paragraph, BiConsumer<TextFlow, PS> applyParagraphStyle, Function<StyledSegment<SEG, S>, Node> nodeFactory) {
    ParagraphBox<PS, SEG, S> box = new ParagraphBox<>(paragraph, applyParagraphStyle, nodeFactory);
    box.highlightTextFillProperty().bind(highlightTextFill);
    box.wrapTextProperty().bind(wrapTextProperty());
    box.graphicFactoryProperty().bind(paragraphGraphicFactoryProperty());
    box.graphicOffset.bind(virtualFlow.breadthOffsetProperty());
    EventStream<Integer> boxIndexValues = box.indexProperty().values().filter(i -> i != -1);
    Subscription firstParPseudoClass = boxIndexValues.subscribe(idx -> box.pseudoClassStateChanged(FIRST_PAR, idx == 0));
    Subscription lastParPseudoClass = EventStreams.combine(boxIndexValues, getParagraphs().sizeProperty().values()).subscribe(in -> in.exec((i, n) -> box.pseudoClassStateChanged(LAST_PAR, i == n - 1)));
    // set up caret
    Function<CaretNode, Subscription> subscribeToCaret = caret -> {
        EventStream<Integer> caretIndexStream = EventStreams.nonNullValuesOf(caret.paragraphIndexProperty());
        // a new event stream needs to be created for each caret added, so that it will immediately
        // fire the box's current index value as an event, thereby running the code in the subscribe block
        // Reusing boxIndexValues will not fire its most recent event, leading to a caret not being added
        // Thus, we'll call the new event stream "fresh" box index values
        EventStream<Integer> freshBoxIndexValues = box.indexProperty().values().filter(i -> i != -1);
        return EventStreams.combine(caretIndexStream, freshBoxIndexValues).subscribe(t -> {
            int caretParagraphIndex = t.get1();
            int boxIndex = t.get2();
            if (caretParagraphIndex == boxIndex) {
                box.caretsProperty().add(caret);
            } else {
                box.caretsProperty().remove(caret);
            }
        });
    };
    Subscription caretSubscription = caretSet.addSubscriber(subscribeToCaret);
    // TODO: how should 'hasCaret' be handled now?
    Subscription hasCaretPseudoClass = EventStreams.combine(boxIndexValues, Val.wrap(currentParagraphProperty()).values()).map(t -> t.get1().equals(t.get2())).subscribe(value -> box.pseudoClassStateChanged(HAS_CARET, value));
    Function<Selection<PS, SEG, S>, Subscription> subscribeToSelection = selection -> {
        EventStream<Integer> startParagraphValues = EventStreams.nonNullValuesOf(selection.startParagraphIndexProperty());
        EventStream<Integer> endParagraphValues = EventStreams.nonNullValuesOf(selection.endParagraphIndexProperty());
        // see comment in caret section about why a new box index EventStream is needed
        EventStream<Integer> freshBoxIndexValues = box.indexProperty().values().filter(i -> i != -1);
        return EventStreams.combine(startParagraphValues, endParagraphValues, freshBoxIndexValues).subscribe(t -> {
            int startPar = t.get1();
            int endPar = t.get2();
            int boxIndex = t.get3();
            if (startPar <= boxIndex && boxIndex <= endPar) {
                // So that we don't add multiple paths for the same selection,
                // which leads to not removing the additional paths when selection is removed,
                // this is a `Map#putIfAbsent(Key, Value)` implementation that creates the path lazily
                SelectionPath p = box.selectionsProperty().get(selection);
                if (p == null) {
                    // create & configure path
                    Val<IndexRange> range = Val.create(() -> boxIndex != -1 ? getParagraphSelection(selection, boxIndex) : EMPTY_RANGE, selection.rangeProperty());
                    SelectionPath path = new SelectionPath(range);
                    selection.configureSelectionPath(path);
                    box.selectionsProperty().put(selection, path);
                }
            } else {
                box.selectionsProperty().remove(selection);
            }
        });
    };
    Subscription selectionSubscription = selectionSet.addSubscriber(subscribeToSelection);
    return new Cell<Paragraph<PS, SEG, S>, ParagraphBox<PS, SEG, S>>() {

        @Override
        public ParagraphBox<PS, SEG, S> getNode() {
            return box;
        }

        @Override
        public void updateIndex(int index) {
            box.setIndex(index);
        }

        @Override
        public void dispose() {
            box.highlightTextFillProperty().unbind();
            box.wrapTextProperty().unbind();
            box.graphicFactoryProperty().unbind();
            box.graphicOffset.unbind();
            firstParPseudoClass.unsubscribe();
            lastParPseudoClass.unsubscribe();
            caretSubscription.unsubscribe();
            hasCaretPseudoClass.unsubscribe();
            selectionSubscription.unsubscribe();
        }
    };
}
Also used : EventHandler(javafx.event.EventHandler) NamedArg(javafx.beans.NamedArg) SuspendableNo(org.reactfx.SuspendableNo) IntUnaryOperator(java.util.function.IntUnaryOperator) PseudoClass(javafx.css.PseudoClass) Cell(org.fxmisc.flowless.Cell) BiFunction(java.util.function.BiFunction) TextOps(org.fxmisc.richtext.model.TextOps) SuspendableEventStream(org.reactfx.SuspendableEventStream) PlainTextChange(org.fxmisc.richtext.model.PlainTextChange) StyleConverter(javafx.css.StyleConverter) ContextMenu(javafx.scene.control.ContextMenu) GenericEditableStyledDocument(org.fxmisc.richtext.model.GenericEditableStyledDocument) Duration(java.time.Duration) TwoDimensional(org.fxmisc.richtext.model.TwoDimensional) Point2D(javafx.geometry.Point2D) UndoManager(org.fxmisc.undo.UndoManager) VirtualFlowHit(org.fxmisc.flowless.VirtualFlowHit) SubscribeableContentsObsSet(org.fxmisc.richtext.util.SubscribeableContentsObsSet) Guard(org.reactfx.Guard) ObservableSet(javafx.collections.ObservableSet) UndoUtils(org.fxmisc.richtext.util.UndoUtils) LiveList(org.reactfx.collection.LiveList) CssMetaData(javafx.css.CssMetaData) Event(javafx.event.Event) Virtualized(org.fxmisc.flowless.Virtualized) List(java.util.List) BooleanProperty(javafx.beans.property.BooleanProperty) Region(javafx.scene.layout.Region) TwoLevelNavigator(org.fxmisc.richtext.model.TwoLevelNavigator) Subscription(org.reactfx.Subscription) Tuple2(org.reactfx.util.Tuple2) Paint(javafx.scene.paint.Paint) EventStreams(org.reactfx.EventStreams) Optional(java.util.Optional) SimpleDoubleProperty(javafx.beans.property.SimpleDoubleProperty) Suspendable(org.reactfx.Suspendable) Styleable(javafx.css.Styleable) Bounds(javafx.geometry.Bounds) CornerRadii(javafx.scene.layout.CornerRadii) Val(org.reactfx.value.Val) BoundingBox(javafx.geometry.BoundingBox) MouseEvent(javafx.scene.input.MouseEvent) StyleSpans(org.fxmisc.richtext.model.StyleSpans) FXCollections(javafx.collections.FXCollections) Codec(org.fxmisc.richtext.model.Codec) DoubleProperty(javafx.beans.property.DoubleProperty) TextFlow(javafx.scene.text.TextFlow) Function(java.util.function.Function) MouseOverTextEvent(org.fxmisc.richtext.event.MouseOverTextEvent) ArrayList(java.util.ArrayList) IndexRange(javafx.scene.control.IndexRange) Paragraph(org.fxmisc.richtext.model.Paragraph) Insets(javafx.geometry.Insets) BackgroundFill(javafx.scene.layout.BackgroundFill) BiConsumer(java.util.function.BiConsumer) IntSupplier(java.util.function.IntSupplier) IntFunction(java.util.function.IntFunction) Color(javafx.scene.paint.Color) ObjectProperty(javafx.beans.property.ObjectProperty) ReadOnlyStyledDocument(org.fxmisc.richtext.model.ReadOnlyStyledDocument) Node(javafx.scene.Node) RichTextChange(org.fxmisc.richtext.model.RichTextChange) Replacement(org.fxmisc.richtext.model.Replacement) Tuples(org.reactfx.util.Tuples) Background(javafx.scene.layout.Background) Consumer(java.util.function.Consumer) StyledDocument(org.fxmisc.richtext.model.StyledDocument) SimpleBooleanProperty(javafx.beans.property.SimpleBooleanProperty) StyleableObjectProperty(javafx.css.StyleableObjectProperty) VirtualizedScrollPane(org.fxmisc.flowless.VirtualizedScrollPane) StyledSegment(org.fxmisc.richtext.model.StyledSegment) SimpleObjectProperty(javafx.beans.property.SimpleObjectProperty) SuspendableList(org.reactfx.collection.SuspendableList) ObservableValue(javafx.beans.value.ObservableValue) EditableStyledDocument(org.fxmisc.richtext.model.EditableStyledDocument) VirtualFlow(org.fxmisc.flowless.VirtualFlow) Var(org.reactfx.value.Var) EventStream(org.reactfx.EventStream) Collections(java.util.Collections) Val(org.reactfx.value.Val) SuspendableEventStream(org.reactfx.SuspendableEventStream) EventStream(org.reactfx.EventStream) Subscription(org.reactfx.Subscription) Cell(org.fxmisc.flowless.Cell)

Example 2 with EventStream

use of org.reactfx.EventStream in project RichTextFX by FXMisc.

the class CaretNode method booleanPulse.

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();
}
Also used : Path(javafx.scene.shape.Path) ZERO(javafx.util.Duration.ZERO) SuspendableNo(org.reactfx.SuspendableNo) Val(org.reactfx.value.Val) EventStreams.merge(org.reactfx.EventStreams.merge) OptionalInt(java.util.OptionalInt) ArrayList(java.util.ArrayList) PlainTextChange(org.fxmisc.richtext.model.PlainTextChange) StyleConverter(javafx.css.StyleConverter) Duration(java.time.Duration) TwoDimensional(org.fxmisc.richtext.model.TwoDimensional) ObjectProperty(javafx.beans.property.ObjectProperty) SuspendableVal(org.reactfx.value.SuspendableVal) CssMetaData(javafx.css.CssMetaData) Forward(org.fxmisc.richtext.model.TwoDimensional.Bias.Forward) Consumer(java.util.function.Consumer) List(java.util.List) Subscription(org.reactfx.Subscription) EventStreams.invalidationsOf(org.reactfx.EventStreams.invalidationsOf) StyleableObjectProperty(javafx.css.StyleableObjectProperty) BreakIterator(java.text.BreakIterator) EventStreams(org.reactfx.EventStreams) StateMachine(org.reactfx.StateMachine) ObservableValue(javafx.beans.value.ObservableValue) Optional(java.util.Optional) Var(org.reactfx.value.Var) Suspendable(org.reactfx.Suspendable) EventStream(org.reactfx.EventStream) Collections(java.util.Collections) Styleable(javafx.css.Styleable) Bounds(javafx.geometry.Bounds) Duration(java.time.Duration)

Example 3 with EventStream

use of org.reactfx.EventStream in project RichTextFX by FXMisc.

the class MouseStationaryHelper method events.

/**
 * 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();
}
Also used : Either(org.reactfx.util.Either) Subscription(org.reactfx.Subscription) MouseEvent(javafx.scene.input.MouseEvent) Duration(java.time.Duration) Node(javafx.scene.Node) EventStreams(org.reactfx.EventStreams) Event(javafx.event.Event) Point2D(javafx.geometry.Point2D) EventStream(org.reactfx.EventStream) MouseEvent(javafx.scene.input.MouseEvent) Point2D(javafx.geometry.Point2D)

Aggregations

Duration (java.time.Duration)3 EventStream (org.reactfx.EventStream)3 EventStreams (org.reactfx.EventStreams)3 Subscription (org.reactfx.Subscription)3 ArrayList (java.util.ArrayList)2 Collections (java.util.Collections)2 List (java.util.List)2 Optional (java.util.Optional)2 Consumer (java.util.function.Consumer)2 ObjectProperty (javafx.beans.property.ObjectProperty)2 ObservableValue (javafx.beans.value.ObservableValue)2 CssMetaData (javafx.css.CssMetaData)2 StyleConverter (javafx.css.StyleConverter)2 Styleable (javafx.css.Styleable)2 StyleableObjectProperty (javafx.css.StyleableObjectProperty)2 Event (javafx.event.Event)2 Bounds (javafx.geometry.Bounds)2 Point2D (javafx.geometry.Point2D)2 Node (javafx.scene.Node)2 MouseEvent (javafx.scene.input.MouseEvent)2