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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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> > >
