Skip to content

Commit 2d34cef

Browse files
committed
Merge pull request #318 from JordanMartinez/showCaret
Allow customization of caret visibility and CSS for caret blink rate.
2 parents 71f8bca + 5626f3a commit 2d34cef

2 files changed

Lines changed: 87 additions & 11 deletions

File tree

richtextfx/src/main/java/org/fxmisc/richtext/CssProperties.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,33 @@ public CssMetaData<? extends Styleable, Paint> getCssMetaData() {
109109
return cssMetaData;
110110
}
111111
}
112+
113+
static class CaretBlinkRateProperty extends StyleableObjectProperty<javafx.util.Duration> {
114+
private final Object bean;
115+
116+
private final CssMetaData<? extends Styleable, javafx.util.Duration> cssMetaData;
117+
118+
public CaretBlinkRateProperty(Object bean, javafx.util.Duration initialValue) {
119+
super(initialValue);
120+
this.bean = bean;
121+
cssMetaData = new PropertyCssMetaData<>(
122+
this, "-fx-caret-blink-rate",
123+
StyleConverter.getDurationConverter(), initialValue);
124+
}
125+
126+
@Override
127+
public Object getBean() {
128+
return bean;
129+
}
130+
131+
@Override
132+
public String getName() {
133+
return "caretBlinkRate";
134+
}
135+
136+
@Override
137+
public CssMetaData<? extends Styleable, javafx.util.Duration> getCssMetaData() {
138+
return cssMetaData;
139+
}
140+
}
112141
}

richtextfx/src/main/java/org/fxmisc/richtext/StyledTextArea.java

Lines changed: 58 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.fxmisc.richtext;
22

3+
import static javafx.util.Duration.ZERO;
34
import static org.fxmisc.richtext.PopupAlignment.*;
45
import static org.reactfx.EventStreams.*;
56
import static org.reactfx.util.Tuples.*;
@@ -17,7 +18,6 @@
1718

1819
import javafx.beans.binding.Binding;
1920
import javafx.beans.binding.Bindings;
20-
import javafx.beans.binding.BooleanBinding;
2121
import javafx.beans.binding.ObjectBinding;
2222
import javafx.beans.property.BooleanProperty;
2323
import 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

Comments
 (0)