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 <[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> >> >>
