Greg, Is there time before the 2.0 vote to put in the constructor work-around I suggested? I see there have been a few commits recently and see it as very low risk.
Either way I will add it to JIRA for 2.0.1. As a pure fix, or the fix and removal of the workaround. Chris On 12 December 2010 21:08, Greg Brown <gk_br...@verizon.net> wrote: > Yes, that would definitely explain it. > > On Dec 12, 2010, at 9:01 AM, Chris Bartlett wrote: > > > I didn't think to look that closely at installSkin(), but it sounds like > the > > behaviour you describe would give the same problem due to there not being > a > > skin installed when the setText() call occurs, so no there is no listener > to > > catch the TextAreaContentListener#paragraphInserted(TextArea, int) event. > > > > On 12 December 2010 20:03, Greg Brown <gk_br...@verizon.net> wrote: > > > >> I don't think the problem is that the skin is getting installed twice - > >> installSkin() was written specifically to handle that case. When a more > >> specific component/skin mapping exists, installSkin() is supposed to > ignore > >> the first call because it knows a more specific skin will be installed > >> later. So something else is probably going on, most likely related to > the > >> call to setText(). > >> > >> On Dec 11, 2010, at 1:48 PM, Chris Bartlett wrote: > >> > >>> I created a custom skin extended from TerraTextAreaSkin in order to > >> handle these keypresses. > >>> However I had problems after associating my subclass of TextArea with a > >> custom skin. > >>> > >>> > >>> TextArea only has one constructor, a public no args one which will > always > >> be called when instantiating a subclass. > >>> public TextArea() { > >>> installSkin(TextArea.class); > >>> setText(""); > >>> } > >>> > >>> The problem is that org.apache.pivot.wtk.skin.TextAreaSkin is installed > >> immediately, and this skin holds some state. > >>> The setText() call ultimately adds a empty paragraph via > >> TerraTextAreaSkin(TextAreaSkin).paragraphInserted(TextArea, int), and > this > >> is tracked within TextAreaSkin. > >>> > >>> > >>> Normally I would extends a skin & component as follows > >>> public class MyTextAreaSkin extends TerraTextAreaSkin { > >>> public MyTextAreaSkin() { > >>> super(); > >>> } > >>> // My styles, overrides & supporting code > >>> } > >>> > >>> public class MyTextArea extends TextArea { > >>> public MyTextArea() { > >>> super(); > >>> installSkin(MyTextArea.class); > >>> } > >>> // My properties, overrides & supporting code > >>> } > >>> > >>> And make them known to the Theme > >>> Theme.getTheme().set(MyTextArea.class, MyTextAreaSkin.class); > >>> > >>> > >>> This will fail because by the time I get to install my custom skin in > the > >> MyTextArea constructor, it superclass has already installed a skin, > *and* > >> altered its state. > >>> The installSkin() in MyTextArea runs again, and essentially resets the > >> state by installing a new skin over the old one. > >>> The next call to TextArea.setText(Reader) will fail with the following > >> error > >>> > >>> Exception in thread "main" java.lang.IndexOutOfBoundsException: index 0 > >> out of bounds. > >>> at > >> > org.apache.pivot.collections.ArrayList.verifyIndexBounds(ArrayList.java:577) > >>> at org.apache.pivot.collections.ArrayList.get(ArrayList.java:346) > >>> at > >> > org.apache.pivot.wtk.skin.TextAreaSkin.paragraphsRemoved(TextAreaSkin.java:1216) > >>> at > >> > org.apache.pivot.wtk.TextArea$TextAreaContentListenerList.paragraphsRemoved(TextArea.java:511) > >>> at > >> > org.apache.pivot.wtk.TextArea$ParagraphSequence.remove(TextArea.java:420) > >>> at org.apache.pivot.wtk.TextArea.setText(TextArea.java:722) > >>> at org.apache.pivot.wtk.TextArea.setText(TextArea.java:667) > >>> at mytextarea.SimpleTest.test(SimpleTest.java:23) > >>> at mytextarea.SimpleTest.main(SimpleTest.java:27) > >>> > >>> > >>> This occurs because the state held in the TextArea component is now out > >> of sync with the state held in the TextAreaSkin. (The component > remembers > >> the first paragraph that was added, but the newly installed skin knows > >> nothing about it, so fails when it tries to remove the paragraph) > >>> > >>> Hopefully that all makes sense! > >>> > >>> > >>> A simple fix would be something like changing the TextArea constructors > >> as follows > >>> // Single constructor to be replaced with 2 new constructors > >>> public TextArea() { > >>> installSkin(TextArea.class); > >>> setText(""); > >>> } > >>> > >>> ...becomes... > >>> > >>> // Default > >>> public TextArea() { > >>> this(TextArea.class); > >>> } > >>> > >>> // To be called by any class extending this which wishes to use a > custom > >> skin > >>> public TextArea(Class<? extends TextArea> componentClass) { > >>> installSkin(componentClass); > >>> setText(""); > >>> } > >>> > >>> > >>> Then in the constructor for MyTextArea() I can call the 2nd constructor > >> and only install a single skin instance > >>> public MyTextArea() { > >>> super(MyTextArea.class); > >>> } > >>> > >>> Zip file (hopefully) attached with some simple test code to demonstrate > >> the issue. > >>> > >>> Chris > >>> > >>> On 11 December 2010 17:25, Chris Bartlett <cbartlet...@gmail.com> > wrote: > >>> I forgot to add that it would be nice to be able to place the caret at > >> the start and end of the file with keystrokes such as CTRL+HOME and > >> CTRL+END. > >>> Holding SHIFT in addition to the other keys would bound the selection > >> from the caret to the start/end point respectively. > >>> > >>> As I will be following up on the whole component keystroke handling > area > >> in PIVOT-638, I will just make a note to include it then. > >>> > >>> Chris > >>> > >>> On 11 December 2010 17:12, Chris Bartlett <cbartlet...@gmail.com> > wrote: > >>> Greg, > >>> > >>> I finally found a little time to play around with TextArea today, and > it > >> looks good. The only points of note I could find are the following. > >>> > >>> > >>> TextAreaSkin fails as follows if when the END key is pressed on the > final > >> line of a TextArea (ie, a line without a linebreak) > >>> java.lang.IndexOutOfBoundsException > >>> at org.apache.pivot.wtk.TextArea.getCharacterAt(TextArea.java:870) > >>> at > >> org.apache.pivot.wtk.skin.TextAreaSkin.keyPressed(TextAreaSkin.java:934) > >>> at > >> > org.apache.pivot.wtk.Component$ComponentKeyListenerList.keyPressed(Component.java:524) > >>> at org.apache.pivot.wtk.Component.keyPressed(Component.java:2813) > >>> at > >> > org.apache.pivot.wtk.ApplicationContext$DisplayHost.processKeyEvent(ApplicationContext.java:1257) > >>> at java.awt.Component.processEvent(Unknown Source) > >>> at > >> > org.apache.pivot.wtk.ApplicationContext$DisplayHost.processEvent(ApplicationContext.java:709) > >>> ... > >>> You can see this by simply pressing END with a freshly created TextArea > >> with no content. > >>> If you add a second line, END will work on the first line, but throw on > >> the last one. > >>> > >>> > >>> TextAreaSkin.getInsertionPoint(int, int) did not behave as I > anticipated > >> in one scenario. > >>> I was expecting it to place the caret at the end of the text (the same > >> placement that would occur if I press the END key) if I left click > anywhere > >> within the 'space' following the final character in TextArea. > >>> Clicking in that space currently seems to have no effect. > >>> > >>> Chris > >>> > >>> > >>> <mytextarea.zip> > >> > >> > >