diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/InlineCssTextField.java b/richtextfx/src/main/java/org/fxmisc/richtext/InlineCssTextField.java
new file mode 100644
index 000000000..c27369148
--- /dev/null
+++ b/richtextfx/src/main/java/org/fxmisc/richtext/InlineCssTextField.java
@@ -0,0 +1,23 @@
+package org.fxmisc.richtext;
+
+import org.fxmisc.richtext.model.SimpleEditableStyledDocument;
+
+import javafx.scene.text.TextFlow;
+
+/**
+ * A TextField that uses inline CSS, i.e. setStyle(String)
, to define the styles of text segments.
+ *
Use CSS Style Class ".styled-text-field" for styling the control.
+ * @author Jurgen
+ */
+public class InlineCssTextField extends StyledTextField
+{
+ public InlineCssTextField() {
+ super( "", TextFlow::setStyle, "", TextExt::setStyle, new SimpleEditableStyledDocument<>("", "") );
+ }
+
+ public InlineCssTextField( String text ) {
+ this(); replaceText( text );
+ getUndoManager().forgetHistory();
+ getUndoManager().mark();
+ }
+}
diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/StyleClassedTextField.java b/richtextfx/src/main/java/org/fxmisc/richtext/StyleClassedTextField.java
new file mode 100644
index 000000000..6876beff4
--- /dev/null
+++ b/richtextfx/src/main/java/org/fxmisc/richtext/StyleClassedTextField.java
@@ -0,0 +1,58 @@
+package org.fxmisc.richtext;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.fxmisc.richtext.model.SimpleEditableStyledDocument;
+
+/**
+ * A TextField that uses style classes, i.e. getStyleClass().add(String)
, to define the styles of text segments.
+ * Use CSS Style Class ".styled-text-field" for styling the control.
+ * @author Jurgen
+ */
+public class StyleClassedTextField extends StyledTextField, Collection>
+{
+ public StyleClassedTextField() {
+ super(
+ Collections.emptyList(),
+ (paragraph, styleClasses) -> paragraph.getStyleClass().addAll(styleClasses),
+ Collections.emptyList(),
+ (text, styleClasses) -> text.getStyleClass().addAll(styleClasses),
+ new SimpleEditableStyledDocument<>( Collections.emptyList(), Collections.emptyList() )
+ );
+ }
+
+ public StyleClassedTextField( String text ) {
+ this(); replaceText( text );
+ getUndoManager().forgetHistory();
+ getUndoManager().mark();
+ }
+
+ /**
+ * Convenient method to append text together with a single style class.
+ */
+ public void append( String text, String styleClass ) {
+ insert( getLength(), text, styleClass );
+ }
+
+ /**
+ * Convenient method to insert text together with a single style class.
+ */
+ public void insert( int position, String text, String styleClass ) {
+ replace( position, position, text, Collections.singleton( styleClass ) );
+ }
+
+ /**
+ * Convenient method to replace text together with a single style class.
+ */
+ public void replace( int start, int end, String text, String styleClass ) {
+ replace( start, end, text, Collections.singleton( styleClass ) );
+ }
+
+ /**
+ * Convenient method to assign a single style class.
+ */
+ public void setStyleClass( int from, int to, String styleClass ) {
+ setStyle( from, to, Collections.singletonList( styleClass ) );
+ }
+}
diff --git a/richtextfx/src/main/java/org/fxmisc/richtext/StyledTextField.java b/richtextfx/src/main/java/org/fxmisc/richtext/StyledTextField.java
new file mode 100644
index 000000000..df05da18b
--- /dev/null
+++ b/richtextfx/src/main/java/org/fxmisc/richtext/StyledTextField.java
@@ -0,0 +1,158 @@
+package org.fxmisc.richtext;
+
+import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.regex.Pattern;
+
+import org.fxmisc.richtext.model.EditableStyledDocument;
+
+import javafx.application.Application;
+import javafx.beans.NamedArg;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.ObjectPropertyBase;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.scene.AccessibleRole;
+import javafx.scene.Group;
+import javafx.scene.Node;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.scene.control.TextField;
+import javafx.scene.input.KeyCode;
+import javafx.scene.input.KeyEvent;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.Pane;
+import javafx.scene.text.TextFlow;
+
+public class StyledTextField extends StyledTextArea
+{
+ private final Pattern VERTICAL_WHITESPACE = Pattern.compile( "\\v" );
+ private final static String STYLE_SHEET;
+ private final static double HEIGHT;
+ static {
+ String globalCSS = System.getProperty( "javafx.userAgentStylesheetUrl" ); // JavaFX preference!
+ if ( globalCSS == null ) globalCSS = Application.getUserAgentStylesheet();
+ if ( globalCSS == null ) globalCSS = Application.STYLESHEET_MODENA;
+ globalCSS = "styled-text-field-"+ globalCSS.toLowerCase() +".css";
+ STYLE_SHEET = StyledTextField.class.getResource( globalCSS ).toExternalForm();
+
+ // Ugly hack to get a TextFields default height :(
+ // as it differs between Caspian, Modena, etc.
+ TextField tf = new TextField( "GetHeight" );
+ new Scene(tf); tf.applyCss(); tf.layout();
+ HEIGHT = tf.getHeight();
+ }
+
+ private boolean selectAll = true;
+
+
+ public StyledTextField(@NamedArg("initialParagraphStyle") PS initialParagraphStyle,
+ @NamedArg("applyParagraphStyle") BiConsumer applyParagraphStyle,
+ @NamedArg("initialTextStyle") S initialTextStyle,
+ @NamedArg("applyStyle") BiConsumer super TextExt, S> applyStyle,
+ @NamedArg("document") EditableStyledDocument document)
+ {
+ super( initialParagraphStyle, applyParagraphStyle, initialTextStyle, applyStyle, document, true );
+
+ getStylesheets().add( STYLE_SHEET );
+ getStyleClass().setAll( "styled-text-field" );
+
+ setAccessibleRole( AccessibleRole.TEXT_FIELD );
+ setPrefSize( 135, HEIGHT );
+
+ addEventFilter( KeyEvent.KEY_PRESSED, KE -> {
+ if ( KE.getCode() == KeyCode.ENTER ) {
+ fireEvent( new ActionEvent( this, null ) );
+ KE.consume();
+ }
+ else if ( KE.getCode() == KeyCode.TAB ) {
+ traverse( this.getParent(), this, KE.isShiftDown() ? -1 : +1 );
+ KE.consume();
+ }
+ });
+
+ addEventFilter( MouseEvent.MOUSE_PRESSED, ME -> selectAll = isFocused() );
+
+ focusedProperty().addListener( (ob,was,focused) -> {
+ if ( ! was && focused && selectAll ) {
+ selectRange( getLength(), 0 );
+ }
+ else if ( ! focused && was ) {
+ moveTo( 0 ); requestFollowCaret();
+ }
+ selectAll = true;
+ });
+ }
+
+ /*
+ * There's no public API to move the focus forward or backward
+ * without explicitly knowing the node. So here's a basic local
+ * implementation to accomplish that.
+ */
+ private Node traverse( Parent p, Node from, int dir )
+ {
+ if ( p == null ) return null;
+
+ List nodeList = p.getChildrenUnmodifiable();
+ int len = nodeList.size();
+ int neighbor = -1;
+
+ if ( from != null ) while ( ++neighbor < len && nodeList.get(neighbor) != from );
+ else if ( dir == 1 ) neighbor = -1;
+ else neighbor = len;
+
+ for ( neighbor += dir; neighbor > -1 && neighbor < len; neighbor += dir ) {
+
+ Node target = nodeList.get( neighbor );
+
+ if ( target instanceof Pane || target instanceof Group ) {
+ target = traverse( (Parent) target, null, dir ); // down
+ if ( target != null ) return target;
+ }
+ else if ( target.isVisible() && ! target.isDisabled() && target.isFocusTraversable() ) {
+ target.requestFocus();
+ return target;
+ }
+ }
+
+ return traverse( p.getParent(), p, dir ); // up
+ }
+
+
+ public void setText( String text )
+ {
+ replaceText( text );
+ }
+
+ /**
+ * The action handler associated with this text field, or
+ * {@code null} if no action handler is assigned.
+ *
+ * The action handler is normally called when the user types the ENTER key.
+ */
+ private ObjectProperty> onAction = new ObjectPropertyBase>() {
+ @Override
+ protected void invalidated() {
+ setEventHandler(ActionEvent.ACTION, get());
+ }
+
+ @Override
+ public Object getBean() {
+ return StyledTextField.this;
+ }
+
+ @Override
+ public String getName() {
+ return "onAction";
+ }
+ };
+ public final ObjectProperty> onActionProperty() { return onAction; }
+ public final EventHandler getOnAction() { return onActionProperty().get(); }
+ public final void setOnAction(EventHandler value) { onActionProperty().set(value); }
+
+ @Override
+ public void replaceText( int start, int end, String text )
+ {
+ super.replaceText( start, end, VERTICAL_WHITESPACE.matcher( text ).replaceAll( " " ) );
+ }
+}
diff --git a/richtextfx/src/main/resources/org/fxmisc/richtext/styled-text-field-caspian.css b/richtextfx/src/main/resources/org/fxmisc/richtext/styled-text-field-caspian.css
new file mode 100644
index 000000000..9ddd8ce69
--- /dev/null
+++ b/richtextfx/src/main/resources/org/fxmisc/richtext/styled-text-field-caspian.css
@@ -0,0 +1,23 @@
+.styled-text-field
+{
+ -fx-cursor: text;
+ -fx-text-fill: -fx-text-inner-color;
+ -fx-background-color: -fx-shadow-highlight-color, -fx-text-box-border, -fx-control-inner-background;
+ -fx-background-insets: 0, 1, 2;
+ -fx-background-radius: 3, 2, 2;
+ -fx-padding: 3 5 4 5;
+}
+.styled-text-field:focused
+{
+ -fx-background-color: -fx-focus-color, -fx-text-box-border, -fx-control-inner-background;
+ -fx-background-insets: -0.4, 1, 2;
+ -fx-background-radius: 3.4, 2, 2;
+}
+.styled-text-field:disabled
+{
+ -fx-opacity: -fx-disabled-opacity;
+}
+.styled-text-field .main-selection
+{
+ -fx-fill: #0093ff;
+}
diff --git a/richtextfx/src/main/resources/org/fxmisc/richtext/styled-text-field-modena.css b/richtextfx/src/main/resources/org/fxmisc/richtext/styled-text-field-modena.css
new file mode 100644
index 000000000..62b070a88
--- /dev/null
+++ b/richtextfx/src/main/resources/org/fxmisc/richtext/styled-text-field-modena.css
@@ -0,0 +1,25 @@
+.styled-text-field
+{
+ -fx-cursor: text;
+ -fx-text-fill: -fx-text-inner-color;
+ -fx-background-color: linear-gradient(to bottom, derive(-fx-text-box-border, -10%), -fx-text-box-border),
+ linear-gradient(from 0px 0px to 0px 5px, derive(-fx-control-inner-background, -9%), -fx-control-inner-background);
+ -fx-background-insets: 0, 1;
+ -fx-background-radius: 3, 2;
+ -fx-padding: 4 7 4 7;
+}
+.styled-text-field:focused
+{
+ -fx-background-color: -fx-focus-color, -fx-control-inner-background, -fx-faint-focus-color,
+ linear-gradient(from 0px 0px to 0px 5px, derive(-fx-control-inner-background, -9%), -fx-control-inner-background);
+ -fx-background-insets: -0.2, 1, -1.4, 3;
+ -fx-background-radius: 3, 2, 4, 0;
+}
+.styled-text-field:disabled
+{
+ -fx-opacity: 0.4;
+}
+.styled-text-field .main-selection
+{
+ -fx-fill: #0096C9;
+}