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.slade...@oracle.com <mailto:martin.slade...@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.slade...@oracle.com
        <mailto:martin.slade...@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.slade...@oracle.com
                <mailto:martin.slade...@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 &gt; 0 and
                               * &lt;= {@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 &gt; 0 and &lt;= {@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 &gt; {@link
                    #getStart()} and
                               * &lt;= {@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 &gt; {@link
                    #getStart()} and &lt;= {@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 &gt; 0 and
                               * &lt;= {@link
                    #getProposedControlText()}{@code}.getLength()}
                               *
                               * @return The new anchor position
                               */
                              public final int getNewAnchor() {}

                              /**
                               * Sets the new anchor. This value must
                    be &gt; 0 and
                               * &lt;= {@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 &gt; 0
                    and
                               * &lt;= {@link
                    #getProposedControlText()}{@code}.getLength()}
                               *
                               * @return The new caret position
                               */
                              public final int getNewCaretPosition() {}

                              /**
                               * Sets the new caret position. This
                    value must be &gt; 0 and
                               * &lt;= {@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




Reply via email to