TextField Document model

Mark Claassen markclaassenx at gmail.com
Thu Oct 18 13:00:48 PDT 2012


> BTW, if you find that changing it dynamically works perfect, I'd be happy
to do the code review (including Jonathan).

Sure.  How do I proceed with this?  Create a Jira issue and then submit the
changes that way?

> Content should be pluggable on a given TextInputControl, or whether it
should require subclassing.

Subclassing seems a bit inconvenient.  To control the input on every text
field on a form, all the fields would need to be custom components.  This
would mean that there would be very few TextFields in my app, I would have
just MyTextField everywhere.  Also, how would I code it?  If I was just
using setContent() I would do something like:

firstField.setContent(new
RestrictedLengthContent(Person.FIRST_NAME_LENGTH));
lastField.setContent(new RestrictedLengthContent(Person.LAST_NAME_LENGTH));

How would I do that in subclasses and still use SceneBuilder?  Maybe there
would be a way in SceneBuilder to allow the use of a parameterized
constructor with a custom parameter, but that seems pretty complex.

> In your case I'm guessing the content is set before...

I just provided you with one easy case.  In reality, we try to be far more
clever. ;-)
However, that is mostly right, and with all these new tools in FX, I think
we will make different choices when that time comes.

> might fail in the control & in the skin

I am pretty new to this, and I have not really addressed the Skin and
Behavior aspects of everything.  Is there a good place to learn how this
all is intended to work together?  It sounds like this is to break apart
the PLAF Look and the PLAF Feel.  (Also, a FX skin is more about how things
are painted, and is not the definition that some products use where a
different skin completely rearranges the UI.)

Mark

On Thu, Oct 18, 2012 at 3:28 PM, Richard Bair <richard.bair at oracle.com>wrote:

> I think Greg did make it all mutable initially, and I probably made them
> final. This was likely due to the fact that the text controls were being
> rewritten only weeks before the 2.0 release and it was easier to prove
> correctness when the Content wasn't changing. Note that "Content" is
> protected already, so from our perspective it is public API, so making it
> public isn't a big change for us. The question is whether Content should be
> pluggable on a given TextInputControl, or whether it should require
> subclassing.
>
> I think I remember at one point the theory being, just subclass and
> provide your own Content in the call to super's constructor. However there
> was a problem with this, because TextInputControlSkin wasn't public, and
> TextFieldSkin etc require a specific subtype of TextInputControl, and as
> such, you couldn't realistically create any subclass of TextInputControl
> (crap!). I discovered that problem after we shipped 2.0.
>
> I think if we want to make content mutable, then we need to do a
> comprehensive review to see where it might fail in the control & in the
> skin. That is, does the skin get a reference to the content and hold on to
> it? Or does it always call getContent() when it needs a reference? If the
> content changes, do we need to clean any state on the skin or on the
> control? In your case I'm guessing the content is set before the skin is,
> and never thereafter changed, and so it acts as though it is an immutable
> property still, so things work great. I'm guessing though that dynamically
> changing the content at runtime might cause some issues?
>
> I don't have any philosophical reason for not having content be mutable.
> My only concern here is that having it mutable introduces some additional
> complexity in the class / skin -- or at least it might.
>
> BTW, if you find that changing it dynamically works perfect, I'd be happy
> to do the code review (including Jonathan).
>
> Thanks
> Richard
>
> On Oct 18, 2012, at 12:03 PM, Mark Claassen wrote:
>
> > My initial proof of concept worked perfectly... limited to 10 characters,
> > forced upper case
> >
> > Altering the source I did the following:
> > Made TextInputControl.Content public
> > Made a setContent(Content content) method in TextInputControl
> > Made the member variable "content" in TextInputControl to be non-final
> > Made TextFieldContent in TextField public
> > Made TextFieldContent in TextField non-static
> >
> > In my source code, I then did:
> >        TextInputControl.Content content = new
> TextField.TextFieldContent()
> > {
> >            @Override
> >            public void insert(int i, String string, boolean bln) {
> >                if (10 - length() => string.length())
> >                    super.insert(i, string.toUpperCase(), bln);
> >            }
> >        };
> >
> > I don't know why the designers made these things final and
> > private/protected.  I would imagine they had a reason, so my JFX source
> > changes would probably need to be more carefully considered.  I just
> wanted
> > tom show it was possible.
> >
> > Mark
> >
> >
> > On Thu, Oct 18, 2012 at 2:37 PM, Mark Claassen <markclaassenx at gmail.com
> >wrote:
> >
> >> I got the source and figured it out...or at least found a problem.
> >>
> >> The TextInputControl.Content seems to be exactly what I need.  However,
> it
> >> is not public.  Further, the instance variable is a final member in
> >> TextInputControl.
> >>
> >> First, Content, and the default implementations, need to be public so
> they
> >> can be easily extended.  I have feeling these were intended to be public
> >> when the time was right.
> >>
> >> Second, there needs to be some way to plug this behavior.  If it is
> going
> >> to remain final, then there needs to be a way to specify the Content
> class
> >> in the FXML.  The SceneBuilder is clearly the way most UIs are supposed
> to
> >> be designed, so, therefore, it needs to be pretty all-encompassing.
> >>
> >> Mark
> >>
> >>
> >> On Wed, Oct 17, 2012 at 5:05 PM, Scott Palmer <swpalmer at gmail.com>
> wrote:
> >>
> >>> Using the override mechanism that Will suggested is probably easier for
> >>> converting to uppercase.
> >>>
> >>> final TextField allCapsTextField = new TextField() {
> >>>        @Override
> >>>        public void replaceText(int start, int end, String text) {
> >>>                super.replaceText(start, end, text.toUppercase());
> >>>        }
> >>>        @Override
> >>>        public void replaceSelection(String text) {
> >>>                super.replaceSelection(text.toUppercase());
> >>>        }
> >>> };
> >>>
> >>> or you could still use the Event Filter and handle the insertion of the
> >>> characters manually if they are lowercase
> >>> and then consume the event.  I think that will be more work and be more
> >>> error-prone though.  As you mention you would have to handle pasting
> and
> >>> drag and drop and all ugly details.  Overriding seems cleaner.
> >>>
> >>> Perhaps you should take a look at the source code to TextInputControl.
> >>> Instead of the Document they have a Content interface.  Maybe you can
> do
> >>> some of what you want by overriding getContent().
> >>>
> >>> Scott
> >>>
> >>> On 2012-10-17, at 4:41 PM, Mark Claassen <markclaassenx at gmail.com>
> wrote:
> >>>
> >>>> Thanks for the tips.  The overriding method does not seem very
> >>> pluggable,
> >>>> so I started with the event filter.
> >>>>
> >>>> I like the idea of an event filter, and I really like the how JavaFX
> >>>> defined the process and the order in which items will receive events.
> >>>>
> >>>> So, I quickly implemented my event filter like this:
> >>>>       input.addEventFilter(KeyEvent.
> >>>> KEY_TYPED, new EventHandler<KeyEvent>() {
> >>>>           @Override
> >>>>           public void handle(KeyEvent t) {
> >>>>               if (input.getText().length() >=10)
> >>>>                   t.consume();
> >>>>           }
> >>>>       });
> >>>>
> >>>> This works for typing, but, of course, I can paste whatever I wanted.
> >>>> (Perhaps I need to find a second filter for that?  How about DnD?)
> >>>>
> >>>> All input events go through the Swing Document, so with that, there
> was
> >>>> just one method to mess with.
> >>>>
> >>>> Further, I currently have a Document implementation that takes user
> >>> input
> >>>> and converts it to upper case.  (It doesn't force the user to type in
> an
> >>>> upper case character, it just converts it if it is not.)  Since, in
> the
> >>>> case of a Document, I can control exactly what the data is, this is
> >>> pretty
> >>>> straightforward.  How is that accomplished here?  Consume the event,
> and
> >>>> then first a new modified copy of the original.  Or do I need to start
> >>>> overriding various methods?
> >>>>
> >>>>
> >>>> On Wed, Oct 17, 2012 at 4:40 PM, Mark Claassen <
> markclaassenx at gmail.com
> >>>> wrote:
> >>>>
> >>>>> Thanks for the tips.  The overriding method does not seem very
> >>> pluggable,
> >>>>> so I started with the event filter.
> >>>>>
> >>>>> I like the idea of an event filter, and I really like the how JavaFX
> >>>>> defined the process and the order in which items will receive events.
> >>>>>
> >>>>> So, I quickly implemented my event filter like this:
> >>>>>       input.addEventFilter(KeyEvent.KEY_TYPED, new
> >>>>> EventHandler<KeyEvent>() {
> >>>>>           @Override
> >>>>>           public void handle(KeyEvent t) {
> >>>>>               if (input.getText().length() >=10)
> >>>>>                   t.consume();
> >>>>>           }
> >>>>>       });
> >>>>>
> >>>>> This works for typing, but, of course, I can paste whatever I wanted.
> >>>>> (Perhaps I need to find a second filter for that?  How about DnD?)
> >>>>>
> >>>>> All input events go through the Swing Document, so with that, there
> was
> >>>>> just one method to mess with.
> >>>>>
> >>>>> Further, I currently have a Document implementation that takes user
> >>> input
> >>>>> and converts it to upper case.  (It doesn't force the user to type in
> >>> an
> >>>>> upper case character, it just converts it if it is not.)  Since, in
> the
> >>>>> case of a Document, I can control exactly what the data is, this is
> >>> pretty
> >>>>> straightforward.  How is that accomplished here?  Consume the event,
> >>> and
> >>>>> then first a new modified copy of the original.  Or do I need to
> start
> >>>>> overriding various methods?
> >>>>>
> >>>>> Mark
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>> On Wed, Oct 17, 2012 at 2:13 PM, Will Hoover <java.whoover at gmail.com
> >>>> wrote:
> >>>>>
> >>>>>> Have you tried:
> >>>>>>
> >>>>>> final TextField tf = new TextField() {
> >>>>>>       final String restictTo = "[A-Z\\s]*";
> >>>>>>       @Override
> >>>>>>       public void replaceText(int start, int end, String text) {
> >>>>>>               if (matchTest(text)) {
> >>>>>>                       super.replaceText(start, end, text);
> >>>>>>               }
> >>>>>>       }
> >>>>>>       @Override
> >>>>>>       public void replaceSelection(String text) {
> >>>>>>               if (matchTest(text)) {
> >>>>>>                       super.replaceSelection(text);
> >>>>>>               }
> >>>>>>       }
> >>>>>>       private boolean matchTest(String text) {
> >>>>>>               return text.isEmpty() || text.matches(restictTo);
> >>>>>>       }
> >>>>>> };
> >>>>>>
> >>>>>> -----Original Message-----
> >>>>>> From: openjfx-dev-bounces at openjdk.java.net
> >>>>>> [mailto:openjfx-dev-bounces at openjdk.java.net] On Behalf Of Mark
> >>> Claassen
> >>>>>> Sent: Wednesday, October 17, 2012 1:08 PM
> >>>>>> To: openjfx-dev at openjdk.java.net
> >>>>>> Subject: TextField Document model
> >>>>>>
> >>>>>> JTextComponents (like JTextField) has a javax.swing.text.Document
> >>> model
> >>>>>> that
> >>>>>> made it pretty easy to create a text field that only allowed a
> certain
> >>>>>> number of characters in it.  Similarly, it was also easy to make a
> >>>>>> Document
> >>>>>> model that took all input, but forced characters to upper case.
> >>>>>>
> >>>>>> @Override
> >>>>>> public void insertString(int offs, String str, AttributeSet a)
> throws
> >>>>>> BadLocationException {
> >>>>>>    <Do stuff>
> >>>>>> }
> >>>>>>
> >>>>>> What is there going to be in JavaFX to accomplish the same goals?
> >>>>>>
> >>>>>> Mark
> >>>>>>
> >>>>>>
> >>>>>
> >>>
> >>>
> >>
>
>


More information about the openjfx-dev mailing list