TextField Document model

Richard Bair richard.bair at oracle.com
Thu Oct 18 12:28:33 PDT 2012


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