11package org .fxmisc .richtext ;
22
3+ import static javafx .util .Duration .ZERO ;
34import static org .fxmisc .richtext .PopupAlignment .*;
45import static org .reactfx .EventStreams .*;
56import static org .reactfx .util .Tuples .*;
1718
1819import javafx .beans .binding .Binding ;
1920import javafx .beans .binding .Bindings ;
20- import javafx .beans .binding .BooleanBinding ;
2121import javafx .beans .binding .ObjectBinding ;
2222import javafx .beans .property .BooleanProperty ;
2323import javafx .beans .property .ObjectProperty ;
@@ -160,6 +160,13 @@ public class StyledTextArea<PS, S> extends Region
160160 private final StyleableObjectProperty <Paint > highlightTextFill
161161 = new CssProperties .HighlightTextFillProperty (this , Color .WHITE );
162162
163+ /**
164+ * Controls the blink rate of the caret, when one is displayed. Setting
165+ * the duration to zero disables blinking.
166+ */
167+ private final StyleableObjectProperty <javafx .util .Duration > caretBlinkRate
168+ = new CssProperties .CaretBlinkRateProperty (this , javafx .util .Duration .millis (500 ));
169+
163170 // editable property
164171 /**
165172 * Indicates whether this text area can be edited by the user.
@@ -181,6 +188,24 @@ public class StyledTextArea<PS, S> extends Region
181188 public final void setWrapText (boolean value ) { wrapText .set (value ); }
182189 public final BooleanProperty wrapTextProperty () { return wrapText ; }
183190
191+ // showCaret property
192+ /**
193+ * Indicates when this text area should display a caret.
194+ */
195+ private final Var <CaretVisibility > showCaret = Var .newSimpleVar (CaretVisibility .AUTO );
196+ public final CaretVisibility getShowCaret () { return showCaret .getValue (); }
197+ public final void setShowCaret (CaretVisibility value ) { showCaret .setValue (value ); }
198+ public final Var <CaretVisibility > showCaretProperty () { return showCaret ; }
199+
200+ public static enum CaretVisibility {
201+ /** Caret is displayed. */
202+ ON ,
203+ /** Caret is displayed when area is focused, enabled, and editable. */
204+ AUTO ,
205+ /** Caret is not displayed. */
206+ OFF
207+ }
208+
184209 // undo manager
185210 @ Override public UndoManager getUndoManager () { return model .getUndoManager (); }
186211 @ Override public void setUndoManager (UndoManagerFactory undoManagerFactory ) {
@@ -569,18 +594,39 @@ public StyledTextArea(PS initialParagraphStyle, BiConsumer<TextFlow, PS> applyPa
569594 EventStream <?> caretDirty = merge (caretPosDirty , paragraphsDirty , selectionDirty );
570595 subscribeTo (caretDirty , x -> requestFollowCaret ());
571596
572- // whether or not to animate the caret
573- BooleanBinding blinkCaret = focusedProperty ()
574- .and (editableProperty ())
575- .and (disabledProperty ().not ());
576- manageBinding (blinkCaret );
597+ // whether or not to display the caret
598+ EventStream <Boolean > blinkCaret = EventStreams .valuesOf (showCaretProperty ())
599+ .flatMap (mode -> {
600+ switch (mode ) {
601+ case ON :
602+ return EventStreams .valuesOf (Val .constant (true ));
603+ case OFF :
604+ return EventStreams .valuesOf (Val .constant (false ));
605+ default :
606+ case AUTO :
607+ return EventStreams .valuesOf (focusedProperty ()
608+ .and (editableProperty ())
609+ .and (disabledProperty ().not ()));
610+ }
611+ });
577612
613+ // the rate at which to display the caret
614+ EventStream <javafx .util .Duration > blinkRate = EventStreams .valuesOf (caretBlinkRate );
615+
578616 // The caret is visible in periodic intervals,
579617 // but only when blinkCaret is true.
580- caretVisible = EventStreams .valuesOf (blinkCaret )
581- .flatMap (blink -> blink
582- ? booleanPulse (Duration .ofMillis (500 ), caretDirty )
583- : EventStreams .valuesOf (Val .constant (false )))
618+ caretVisible = EventStreams .combine (blinkCaret , blinkRate )
619+ .flatMap (tuple -> {
620+ Boolean blink = tuple .get1 ();
621+ javafx .util .Duration rate = tuple .get2 ();
622+ if (blink ) {
623+ return rate .lessThanOrEqualTo (ZERO )
624+ ? EventStreams .valuesOf (Val .constant (true ))
625+ : booleanPulse (rate , caretDirty );
626+ } else {
627+ return EventStreams .valuesOf (Val .constant (false ));
628+ }
629+ })
584630 .toBinding (false );
585631 manageBinding (caretVisible );
586632
@@ -1216,7 +1262,8 @@ private static Bounds extendLeft(Bounds b, double w) {
12161262 }
12171263 }
12181264
1219- private static EventStream <Boolean > booleanPulse (Duration duration , EventStream <?> restartImpulse ) {
1265+ private static EventStream <Boolean > booleanPulse (javafx .util .Duration javafxDuration , EventStream <?> restartImpulse ) {
1266+ Duration duration = Duration .ofMillis (Math .round (javafxDuration .toMillis ()));
12201267 EventStream <?> ticks = EventStreams .restartableTicks (duration , restartImpulse );
12211268 return StateMachine .init (false )
12221269 .on (restartImpulse .withDefaultEvent (null )).transition ((state , impulse ) -> true )
0 commit comments