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>

Reply via email to