Formatted text field API (RT-14000, RT-30881)
Martin Sladecek
martin.sladecek at oracle.com
Wed Jun 11 16:38:04 UTC 2014
I already posted a comment in JIRA (RT-30881) that we should make the
value writable.
Anyhow, we should move the discussion to JIRA to keep everything at one
place.
Thanks,
-Martin
On 11.6.2014 18:14, Scott Palmer wrote:
> I already have a use for a bi-directional binding. I have a UI that
> shows properties of a component. The component's properties can be
> updated via the UI, and also by other events from background processes
> that are running, or even just as a reaction to some other property
> changing. So I need the binding from the model to the control to be
> bi-directional. The UI view is both read/write. The properties can be
> numbers, rational numbers expressed as fractions ##/###, times
> HH:MM:SS, enum values, etc.
>
> (My app has some complex requirements so in actual fact a simple
> binding probably isn't enough. But in principle it could be.)
>
> Or just think of a spreadsheet in Google docs being edited by two
> people on a network... I want to be able to update a cell, and I want
> to see when someone else updates it. Two different read/write fields
> connected to the same model.
>
> I just thought of something else. Can this be easily used as the field
> for a editable ComboBox?
>
> Scott
>
>
> On Wed, Jun 11, 2014 at 10:08 AM, Martin Sladecek
> <martin.sladecek at oracle.com <mailto:martin.sladecek at oracle.com>> wrote:
>
> Although I'm not sure the second one can be considered a use-case,
> I agree that somebody might want uneditable field with some format
> (and it's up to the developer to make it uneditable), so I'll make
> the value rw.
>
> -Martin
>
> On 11.6.2014 16:03, Scott Palmer wrote:
>
> There are two cases for this:
> - a regular binding could be used with a read-only
> FormattedTextField
> for cases where you want the text representation shown in the
> UI and
> have it selectable and readable for copy/paste.
> - a bi-directional binding should "just work"
>
> Scott
>
> On Wed, Jun 11, 2014 at 9:59 AM, Martin Sladecek
> <martin.sladecek at oracle.com
> <mailto:martin.sladecek at oracle.com>> wrote:
>
> Binding the property would mean the text field can be
> edited (the field is
> still editable), but it's content cannot be committed (by
> either pressing
> ENTER or focusing out of the field), so it's content is
> practically
> irrelevant.
> If you think there's a reasonable use case for this, there
> should be no
> problem with having writable value.
>
> -Martin
>
>
> On 11.6.2014 15:53, Scott Palmer wrote:
>
> In FormattedTextField why is the value property
> returned as a
> read-only property?
> This control should allow bi-directional bindings to
> the value property.
>
> Scott
>
> On Wed, Jun 11, 2014 at 4:50 AM, Martin Sladecek
> <martin.sladecek at oracle.com
> <mailto:martin.sladecek at oracle.com>> wrote:
>
> Hello,
> I would like to start some discussion about
> formatted text field API. The
> related JIRA issues are RT-14000 (formatted text
> field) and RT-30881
> (content filter).
>
> The RT-30881 defines a content filter for all text
> input controls (in
> TextInputControl class), like TextField and
> TextArea. This was originally
> implemented by Richard Bair and briefly discussed
> here:
>
> http://mail.openjdk.java.net/pipermail/openjfx-dev/2012-December/004687.html.
> I've tried to build formatted text field on top of
> that (and made some
> changes to the API) since content filtering is
> something most formatted
> text
> fields will use.
>
> First, the TextInputControl additions:
>
> * contentFilter property of type
> ObjectProperty<ContentFilter>
>
> So let's look at the content filter and content
> change (both nested
> classes
> of TextInputControl):
>
> /**
> * Content Filter specifies the filter to be
> used with {@link
> TextInputControl#contentFilterProperty()}.
> * It allow user to intercept and modify any
> change done to the text
> content.
> * To avoid content that's not valid for the
> filter, it's possible
> to
> assign a default value supplier for the filter.
> * <p>
> * The filter itself is an {@code
> UnaryOperator} that accepts {@link
> javafx.scene.control.TextInputControl.ContentChange}
> object.
> * It should return a {@link
> javafx.scene.control.TextInputControl.ContentChange}
> object that contains
> the actual (filtered)
> * change. Returning null rejects the change.
> * <p>
> * If default value supplier is provided, it
> is used when the {@code
> ContentFilter} is assigned to a {@code
> TextInputControl}
> * and it's current text is invalid. It is
> expected that the
> provided
> default value is accepted by the filtering operator.
> */
> public static class ContentFilter {
>
> /**
> * Creates a new filter with the
> providing filtering operator.
> * @param filter the filtering operator
> *
> * @throws
> java.lang.NullPointerException if filter is null
> */
> public
> ContentFilter(UnaryOperator<ContentChange> filter) {}
>
> /**
> * Creates a new filter with the
> providing filtering operator
> and
> default value supplier.
> * @param filter the filtering operator
> * @param defaultValue the default value
> or null
> *
> * @throws
> java.lang.NullPointerException if filter is null
> */
> public
> ContentFilter(UnaryOperator<ContentChange> filter,
> Supplier<String> defaultValue) {}
>
> /**
> * The filtering operator of this filter.
> * @return the operator
> */
> public UnaryOperator<ContentChange>
> getFilter() {}
>
> /**
> * The default value provider of this filter
> * @return the default value provider or
> null
> */
> public Supplier<String> getDefaultValue() {}
>
> /**
> * Chains this filter with a filtering
> operator. The other
> filtering
> operator is used only if this
> * filter's operator rejects the
> operation. The default value of
> the
> new {@code ContentFilter} is the same
> * as of this filter.
> * @param other the filtering operator
> to chain
> * @return a new ContentFilter as
> described above
> */
> public ContentFilter
> orApply(UnaryOperator<ContentChange> other)
> {}
>
> /**
> * Chains this filter with a filtering
> operator of another
> {@code
> ContentFilter}. The other filtering operator is
> used only if this
> * filter's operator rejects the
> operation. The default value of
> the
> new {@code ContentFilter} is the same
> * as of this filter.
> * @param other the filter to chain
> * @return a new ContentFilter as
> described above
> */
> public ContentFilter
> orApply(ContentFilter other) {}
>
> }
>
> /**
> * Contains the state representing a change
> in the content for a
> * TextInputControl. This object is passed
> to any registered
> * {@code contentFilter} on the
> TextInputControl whenever the text
> * for the TextInputControl is modified.
> * <p>
> * This class contains state and
> convenience methods for
> determining
> what
> * change occurred on the control. It
> also has a reference to
> the
> * TextInputControl itself so that the
> developer may query any
> other
> * state on the control. Note that you
> should never modify the
> state
> * of the control directly from within
> the contentFilter
> handler.
> * </p>
> * <p>
> * The ContentChange is mutable, but not
> observable. It should
> be
> used
> * only for the life of a single change.
> It is intended that the
> * ContentChange will be modified from
> within the contentFilter.
> * </p>
> */
> public static final class ContentChange
> implements Cloneable{
>
> /**
> * Gets the control associated with this
> change.
> * @return The control associated with
> this change. This will
> never
> be null.
> */
> public final TextInputControl
> getControl() {}
>
> /**
> * Gets the start index into the {@link
> javafx.scene.control.TextInputControl#getText()}
> * for the modification. This will
> always be a value > 0 and
> * <= {@link
> javafx.scene.control.TextInputControl#getLength()}.
> *
> * @return The start index
> */
> public final int getStart() {}
>
> /**
> * Sets the start index for the range to
> be modified. This value
> must
> * always be a value > 0 and <= {@link
> javafx.scene.control.TextInputControl#getLength()}
> * <p>
> * <b>Note</b> that setting start before
> the current end will
> also
> change end to the same value.
> *
> * @param value the new start index
> */
> public final void setStart(int value) {}
>
> /**
> * Gets the end index into the {@link
> javafx.scene.control.TextInputControl#getText()}
> * for the modification. This will
> always be a value > {@link
> #getStart()} and
> * <= {@link
> javafx.scene.control.TextInputControl#getLength()}.
> *
> * @return The end index
> */
> public final int getEnd() {}
>
> /**
> * Sets the end index for the range to
> be modified. This value
> must
> * always be a value > {@link
> #getStart()} and <= {@link
> javafx.scene.control.TextInputControl#getLength()}.
> * Note that there is an order
> dependency between modifying the
> start
> * and end values. They must always be
> modified in order such
> that
> they
> * do not violate their constraints.
> *
> * @param value The new end index
> */
> public final void setEnd(int value) {}
>
> /**
> * A convenience method returning an
> IndexRange representing the
> * start and end values.
> *
> * @return a non-null IndexRange
> representing the range from
> start
> to end.
> */
> public final IndexRange getRange() {}
>
> /**
> * A convenience method assigning both
> the start and end values
> * together, in such a way as to ensure
> they are valid with
> respect
> to
> * each other. One way to use this
> method is to set the range
> like
> this:
> * {@code
> *
> change.setRange(IndexRange.normalize(newStart,
> newEnd));
> * }
> *
> * @param value The new range. Cannot be
> null.
> */
> public final void setRange(IndexRange
> value) {}
>
> /**
> * A convenience method assigning both
> the start and end values
> * together, in such a way as to ensure
> they are valid with
> respect
> to
> * each other. The start must be less
> than or equal to the end.
> *
> * @param start The new start value.
> Must be a valid start value
> * @param end The new end value. Must be
> a valid end value
> */
> public final void setRange(int start,
> int end) {}
>
> /**
> * Gets the new anchor. This value will
> always be > 0 and
> * <= {@link
> #getProposedControlText()}{@code}.getLength()}
> *
> * @return The new anchor position
> */
> public final int getNewAnchor() {}
>
> /**
> * Sets the new anchor. This value must
> be > 0 and
> * <= {@link
> #getProposedControlText()}{@code}.getLength()}.
> Note
> that there
> * is an order dependence here, in that
> the anchor should be
> * specified after the new text has been
> specified.
> *
> * @param value The new anchor position
> */
> public final void setNewAnchor(int value) {}
>
> /**
> * Gets the new caret position. This
> value will always be > 0
> and
> * <= {@link
> #getProposedControlText()}{@code}.getLength()}
> *
> * @return The new caret position
> */
> public final int getNewCaretPosition() {}
>
> /**
> * Sets the new caret position. This
> value must be > 0 and
> * <= {@link
> #getProposedControlText()}{@code}.getLength()}.
> Note
> that there
> * is an order dependence here, in that
> the caret position
> should be
> * specified after the new text has been
> specified.
> *
> * @param value The new caret position
> */
> public final void
> setNewCaretPosition(int value) {}
>
> /**
> * Gets the text used in this change.
> For example, this may be
> new
> * text being added, or text which is
> replacing all the
> control's
> text
> * within the range of start and end.
> Typically it is an empty
> string
> * only for cases where the range is
> being deleted.
> *
> * @return The text involved in this
> change. This will never be
> null.
> */
> public final String getText() {}
>
> /**
> * Sets the text to use in this change.
> This is used to replace
> the
> * range from start to end, if such a
> range exists, or to insert
> text
> * at the position represented by start
> == end.
> *
> * @param value The text. This cannot be
> null.
> */
> public final void setText(String value) {}
>
> /**
> * Gets the complete new text which will
> be used on the control
> after
> * this change. Note that some controls
> (such as TextField) may
> do
> further
> * filtering after the change is made
> (such as stripping out
> newlines)
> * such that you cannot assume that the
> newText will be exactly
> the
> same
> * as what is finally set as the content
> on the control, however
> it
> is
> * correct to assume that this is the
> case for the purpose of
> computing
> * the new caret position and new anchor
> position (as those
> values
> supplied
> * will be modified as necessary after
> the control has stripped
> any
> * additional characters that the
> control might strip).
> *
> * @return The controls proposed new
> text at the time of this
> call,
> according
> * to the state set for start,
> end, and text properties
> on
> this ContentChange object.
> */
> public final String
> getProposedControlText() {}
>
> /**
> * Gets whether this change was in
> response to text being added.
> Note that
> * after the ContentChange object is
> modified by the
> contentFilter
> (by one
> * of the setters) the return value of
> this method is not
> altered.
> It answers
> * as to whether this change was fired
> as a result of text being
> added,
> * not whether text will end up being
> added in the end.
> *
> * <p>
> * Text may have been added either cause
> it was {@link
> #isInserted()},
> * {@link #isAppended()}, or {@link
> #isPrepended()}.
> * </p>
> *
> * @return true if text was being added
> */
> public final boolean isAdded() {}
>
> /**
> * Gets whether this change was in
> response to text being
> deleted.
> Note that
> * after the ContentChange object is
> modified by the
> contentFilter
> (by one
> * of the setters) the return value of
> this method is not
> altered.
> It answers
> * as to whether this change was fired
> as a result of text being
> deleted,
> * not whether text will end up being
> deleted in the end.
> *
> * @return true if text was being deleted
> */
> public final boolean isDeleted() {}
>
> /**
> * Gets whether this change was in
> response to text being added,
> and
> in
> * particular, inserted into the midst
> of the control text. If
> this
> is
> * true, then {@link #isAdded()} will
> return true.
> *
> * @return true if text was being inserted
> */
> public final boolean isInserted() {}
>
> /**
> * Gets whether this change was in
> response to text being added,
> and
> in
> * particular, appended to the end of
> the control text. If this
> is
> * true, then {@link #isAdded()} will
> return true.
> *
> * @return true if text was being appended
> */
> public final boolean isAppended() {}
>
> /**
> * Gets whether this change was in
> response to text being added,
> and
> in
> * particular, prepended at the start of
> the control text. If
> this
> is
> * true, then {@link #isAdded()} will
> return true.
> *
> * @return true if text was being prepended
> */
> public final boolean isPrepended() {}
>
> /**
> * Gets whether this change was in
> response to text being
> replaced.
> Note that
> * after the ContentChange object is
> modified by the
> contentFilter
> (by one
> * of the setters) the return value of
> this method is not
> altered.
> It answers
> * as to whether this change was fired
> as a result of text being
> replaced,
> * not whether text will end up being
> replaced in the end.
> *
> * @return true if text was being replaced
> */
> public final boolean isReplaced() {}
>
> @Override
> public ContentChange clone() {}
> }
>
>
>
> The new FormattedTextField class relies on the
> content filtering and adds
> a
> concept of values convertible to/from a text:
>
> **
> * FormattedTextField is a special kind of
> TextField that handles
> conversion
> between a value of type {@code T} and
> * a text of this TextField.
> * <p>
> * There are two types of converters:
> * <ul>
> * <li>{@link #valueConverterProperty()}
> represents the default
> converter between the text and values</li>
> * <li>{@link #editConverterProperty()} is a
> special converter that
> takes precedence when the textfield is being edited.
> * This allows to have a edit-friendly
> format and a nice display
> format
> for the value</li>
> * </ul>
> *
> * <p>
> * When editing the text field, the value is not
> updated until either
> {@code
> ENTER} key is pressed or focus is lost.
> * If the conversion fail, the last known valid
> value is used instead.
> *
> * @param <T> the value type
> */
> public final class FormattedTextField<T> extends
> TextField{
>
> /**
> * Creates a formatted text field with the
> specified converter and a
> null value.
> * @param valueConverter the value converter
> */
> public FormattedTextField(StringConverter<T>
> valueConverter) {}
>
> /**
> * Creates a formatted text field with the
> specified converter and a
> value
> * @param valueConverter the value converter
> * @param value the initial value
> */
> public FormattedTextField(StringConverter<T>
> valueConverter, T
> value) {}
>
> /**
> * This represents the current value of the
> formatted text field. If
> a
> {@link #valueConverterProperty()} is provided,
> * and the text field is not being edited,
> the value is a
> representation
> of the text in the text field.
> */
> public final ReadOnlyObjectProperty<T>
> valueProperty() {}
> public final void setValue(T value) {}
> public final T getValue() {}
>
> /**
> * The default converter between the values
> and text.
> * Note that changing the converter might
> lead to a change of value,
> but
> only if the text can be converted by the new
> * converter. Otherwise, the current value
> is converted to a text,
> which
> is set to the field.
> * @see #editConverterProperty()
> */
> public final ObjectProperty<StringConverter<T>>
> valueConverterProperty()
> {}
> public final void
> setValueConverter(StringConverter<T> converter) {}
> public final StringConverter<T>
> getValueConverter() {}
>
> /**
> * Converter between values and text when
> the field is being edited.
> * @see #valueConverterProperty()
> */
> public final ObjectProperty<StringConverter<T>>
> editConverterProperty()
> {}
> public final void
> setEditConverter(StringConverter<T> converter) {}
> public final StringConverter<T>
> getEditConverter() {}
>
> }
>
> You can find the whole patch here:
>
> https://javafx-jira.kenai.com/secure/attachment/44678/rt-30881_14000_proposal.patch
> There are some examples for content filtering in
> RT-30881. I'll attach
> some
> formatted text field samples soon.
>
> Thanks,
> -Martin
>
>
>
More information about the openjfx-dev
mailing list