You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by gb...@apache.org on 2009/05/20 15:45:43 UTC

svn commit: r776701 - in /incubator/pivot/trunk: tutorials/src/pivot/tutorials/ tutorials/src/pivot/tutorials/navigation/ wtk/src/pivot/wtk/ wtk/src/pivot/wtk/skin/ wtk/src/pivot/wtk/skin/terra/ wtk/test/pivot/wtk/test/ wtk/test/pivot/wtk/text/test/

Author: gbrown
Date: Wed May 20 13:45:43 2009
New Revision: 776701

URL: http://svn.apache.org/viewvc?rev=776701&view=rev
Log:
Fix bug in Component validate()/setVisible() logic; move valid flag from Container to component since some components now use the layout() method; register for FlowPane events in FlowPaneSkin; improvements to TerraTabPaneSkin.

Added:
    incubator/pivot/trunk/tutorials/src/pivot/tutorials/navigation/TabPanes.java
    incubator/pivot/trunk/tutorials/src/pivot/tutorials/navigation/tab_panes.wtkx
Modified:
    incubator/pivot/trunk/tutorials/src/pivot/tutorials/navigation.wtkx
    incubator/pivot/trunk/wtk/src/pivot/wtk/Component.java
    incubator/pivot/trunk/wtk/src/pivot/wtk/Container.java
    incubator/pivot/trunk/wtk/src/pivot/wtk/FlowPane.java
    incubator/pivot/trunk/wtk/src/pivot/wtk/skin/CardPaneSkin.java
    incubator/pivot/trunk/wtk/src/pivot/wtk/skin/FlowPaneSkin.java
    incubator/pivot/trunk/wtk/src/pivot/wtk/skin/terra/TerraTabPaneSkin.java
    incubator/pivot/trunk/wtk/test/pivot/wtk/test/AccordionTest.java
    incubator/pivot/trunk/wtk/test/pivot/wtk/text/test/TextAreaTest.java

Modified: incubator/pivot/trunk/tutorials/src/pivot/tutorials/navigation.wtkx
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/tutorials/src/pivot/tutorials/navigation.wtkx?rev=776701&r1=776700&r2=776701&view=diff
==============================================================================
--- incubator/pivot/trunk/tutorials/src/pivot/tutorials/navigation.wtkx (original)
+++ incubator/pivot/trunk/tutorials/src/pivot/tutorials/navigation.wtkx Wed May 20 13:45:43 2009
@@ -55,12 +55,20 @@
                                     <TabPane selectedIndex="-1" styles="{tabOrientation:'horizontal', collapsible:true}"
                                         tooltipText="Click a tab to expand/collapse">
                                         <tabs>
-                                            <Label TabPane.icon="@anchor.png" TabPane.name="Anchor" text="Collapsible"
-                                                styles="{horizontalAlignment:'center', verticalAlignment:'center'}"/>
-                                            <Label TabPane.icon="@cup.png" TabPane.name="Cup" text="Collapsible"
-                                                styles="{horizontalAlignment:'center', verticalAlignment:'center'}"/>
-                                            <Label TabPane.icon="@star.png" TabPane.name="Star" text="Collapsible"
-                                                styles="{horizontalAlignment:'center', verticalAlignment:'center'}"/>
+                                            <FlowPane TabPane.icon="@anchor.png" TabPane.name="Anchor"
+                                                styles="{horizontalAlignment:'center', verticalAlignment:'top'}">
+                                                <Label text="Collapsible"/>
+                                            </FlowPane>
+
+                                            <FlowPane TabPane.icon="@cup.png" TabPane.name="Cup"
+                                                styles="{horizontalAlignment:'center', verticalAlignment:'center'}">
+                                                <Label text="Collapsible"/>
+                                            </FlowPane>
+
+                                            <FlowPane TabPane.icon="@star.png" TabPane.name="Star"
+                                                styles="{horizontalAlignment:'center', verticalAlignment:'bottom'}">
+                                                <Label text="Collapsible"/>
+                                            </FlowPane>
                                         </tabs>
                                     </TabPane>
                                 </TablePane.Row>
@@ -88,12 +96,20 @@
                                     <TabPane selectedIndex="-1" styles="{tabOrientation:'vertical', collapsible:true}"
                                         tooltipText="Click a tab to expand/collapse">
                                         <tabs>
-                                            <Label TabPane.icon="@anchor.png" TabPane.name="Anchor" text="Collapsible"
-                                                styles="{horizontalAlignment:'center', verticalAlignment:'center'}"/>
-                                            <Label TabPane.icon="@cup.png" TabPane.name="Cup" text="Collapsible"
-                                                styles="{horizontalAlignment:'center', verticalAlignment:'center'}"/>
-                                            <Label TabPane.icon="@star.png" TabPane.name="Star" text="Collapsible"
-                                                styles="{horizontalAlignment:'center', verticalAlignment:'center'}"/>
+                                            <FlowPane TabPane.icon="@anchor.png" TabPane.name="Anchor"
+                                                styles="{horizontalAlignment:'left', verticalAlignment:'center'}">
+                                                <Label text="Collapsible"/>
+                                            </FlowPane>
+
+                                            <FlowPane TabPane.icon="@cup.png" TabPane.name="Cup"
+                                                styles="{horizontalAlignment:'center', verticalAlignment:'center'}">
+                                                <Label text="Collapsible"/>
+                                            </FlowPane>
+
+                                            <FlowPane TabPane.icon="@star.png" TabPane.name="Star"
+                                                styles="{horizontalAlignment:'right', verticalAlignment:'center'}">
+                                                <Label text="Collapsible"/>
+                                            </FlowPane>
                                         </tabs>
                                     </TabPane>
                                 </TablePane.Row>

Added: incubator/pivot/trunk/tutorials/src/pivot/tutorials/navigation/TabPanes.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/tutorials/src/pivot/tutorials/navigation/TabPanes.java?rev=776701&view=auto
==============================================================================
--- incubator/pivot/trunk/tutorials/src/pivot/tutorials/navigation/TabPanes.java (added)
+++ incubator/pivot/trunk/tutorials/src/pivot/tutorials/navigation/TabPanes.java Wed May 20 13:45:43 2009
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.tutorials.navigation;
+
+import pivot.collections.Dictionary;
+import pivot.wtk.Application;
+import pivot.wtk.Button;
+import pivot.wtk.ButtonStateListener;
+import pivot.wtk.Checkbox;
+import pivot.wtk.DesktopApplicationContext;
+import pivot.wtk.Display;
+import pivot.wtk.FlowPane;
+import pivot.wtk.Orientation;
+import pivot.wtk.RadioButton;
+import pivot.wtk.TabPane;
+import pivot.wtk.Window;
+import pivot.wtk.Button.Group;
+import pivot.wtkx.Bindable;
+
+public class TabPanes extends Bindable implements Application {
+    @Load(resourceName="tab_panes.wtkx") private Window window;
+    @Bind(fieldName="window") private TabPane tabPane;
+    @Bind(fieldName="window") private Checkbox collapsibleCheckbox;
+    @Bind(fieldName="window") private RadioButton horizontalRadioButton;
+    @Bind(fieldName="window") private FlowPane cornerFlowPane;
+
+    private Button.Group tabOrientationGroup = null;
+
+    public void startup(Display display, Dictionary<String, String> properties)
+        throws Exception {
+        bind();
+
+        collapsibleCheckbox.getButtonStateListeners().add(new ButtonStateListener() {
+            public void stateChanged(Button button, Button.State previousState) {
+                updateTabPane();
+            }
+        });
+
+        tabOrientationGroup = Button.getGroup("tabOrientation");
+        tabOrientationGroup.getGroupListeners().add(new Button.GroupListener() {
+            public void selectionChanged(Group group, Button previousSelection) {
+                updateTabPane();
+            }
+        });
+
+        updateTabPane();
+
+        window.open(display);
+    }
+
+    public boolean shutdown(boolean optional) {
+        if (window != null) {
+            window.close();
+        }
+
+        return true;
+    }
+
+    public void suspend() {
+    }
+
+    public void resume() {
+    }
+
+    private void updateTabPane() {
+        tabPane.getStyles().put("collapsible", collapsibleCheckbox.isSelected());
+
+        if (tabOrientationGroup.getSelection() == horizontalRadioButton) {
+            tabPane.getStyles().put("tabOrientation", Orientation.HORIZONTAL);
+            if (tabPane.getCorner() == null) {
+                tabPane.setCorner(cornerFlowPane);
+            }
+        } else {
+            tabPane.getStyles().put("tabOrientation", Orientation.VERTICAL);
+            if (tabPane.getCorner() == cornerFlowPane) {
+                tabPane.setCorner(null);
+            }
+        }
+    }
+
+    public static void main(String[] args) {
+        DesktopApplicationContext.main(TabPanes.class, args);
+    }
+}

Added: incubator/pivot/trunk/tutorials/src/pivot/tutorials/navigation/tab_panes.wtkx
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/tutorials/src/pivot/tutorials/navigation/tab_panes.wtkx?rev=776701&view=auto
==============================================================================
--- incubator/pivot/trunk/tutorials/src/pivot/tutorials/navigation/tab_panes.wtkx (added)
+++ incubator/pivot/trunk/tutorials/src/pivot/tutorials/navigation/tab_panes.wtkx Wed May 20 13:45:43 2009
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to you under the Apache License,
+Version 2.0 (the "License"); you may not use this file except in
+compliance with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<Window title="Tab Panes" maximized="true"
+    xmlns:wtkx="http://incubator.apache.org/pivot/wtkx/1.1" xmlns="pivot.wtk">
+    <content>
+        <TablePane styles="{padding:8, horizontalSpacing:6}">
+            <columns>
+                <TablePane.Column width="1*"/>
+                <TablePane.Column/>
+            </columns>
+            <rows>
+                <TablePane.Row height="1*">
+                    <Border styles="{padding:12}">
+                        <content>
+                            <FlowPane>
+                                <TabPane wtkx:id="tabPane" selectedIndex="0">
+                                    <tabs>
+                                        <FlowPane TabPane.icon="pivot/tutorials/bell.png" TabPane.name="Bell"
+                                            styles="{horizontalAlignment:'center', verticalAlignment:'center'}">
+                                            <Border>
+                                                <content>
+                                                    <Label text="240x180" preferredWidth="240" preferredHeight="180"/>
+                                                </content>
+                                            </Border>
+                                        </FlowPane>
+
+                                        <FlowPane TabPane.icon="pivot/tutorials/clock.png" TabPane.name="Clock"
+                                            styles="{horizontalAlignment:'center', verticalAlignment:'center'}">
+                                            <Border>
+                                                <content>
+                                                    <Label text="320x240" preferredWidth="320" preferredHeight="240"/>
+                                                </content>
+                                            </Border>
+                                        </FlowPane>
+
+                                        <FlowPane TabPane.icon="pivot/tutorials/house.png" TabPane.name="House"
+                                            styles="{horizontalAlignment:'center', verticalAlignment:'center'}">
+                                            <Border>
+                                                <content>
+                                                    <Label text="480x360" preferredWidth="480" preferredHeight="360"/>
+                                                </content>
+                                            </Border>
+                                        </FlowPane>
+                                    </tabs>
+                                    <corner>
+                                        <FlowPane wtkx:id="cornerFlowPane" styles="{horizontalAlignment:'right'}">
+                                            <TextInput/>
+                                        </FlowPane>
+                                    </corner>
+                                </TabPane>
+                            </FlowPane>
+                        </content>
+                    </Border>
+
+                    <Border>
+                        <content>
+                            <FlowPane orientation="vertical" styles="{padding:4, spacing:6}">
+                                <Checkbox wtkx:id="collapsibleCheckbox" buttonData="Collapsible"/>
+                                <Label text="Tab orientation:"/>
+                                <RadioButton wtkx:id="horizontalRadioButton" buttonData="Horizontal" selected="true"
+                                    group="tabOrientation"/>
+                                <RadioButton wtkx:id="verticalRadioButton" buttonData="Vertical"
+                                    group="tabOrientation"/>
+                            </FlowPane>
+                        </content>
+                    </Border>
+                </TablePane.Row>
+            </rows>
+        </TablePane>
+    </content>
+</Window>
+

Modified: incubator/pivot/trunk/wtk/src/pivot/wtk/Component.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtk/Component.java?rev=776701&r1=776700&r2=776701&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtk/Component.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtk/Component.java Wed May 20 13:45:43 2009
@@ -531,6 +531,9 @@
     // a parent
     private Container parent = null;
 
+    // The component's valid state
+    private boolean valid = false;
+
     // The component's location, relative to the parent's origin
     private int x = 0;
     private int y = 0;
@@ -801,11 +804,7 @@
             // of being laid out; it must be flagged as invalid to ensure
             // that layout is propagated downward when validate() is
             // called on it
-            if (visible) {
-                // We don't invalidate if the component is not visible; if
-                // the component later becomes visible, we validate
-                invalidate();
-            }
+            invalidate();
 
             // Redraw the region formerly occupied by this component
             if (parent != null) {
@@ -1599,18 +1598,19 @@
 
     /**
      * Returns the component's valid state.
-     *
-     * @return
-     * <tt>true</tt>; non-container components are always valid.
      */
     public boolean isValid() {
-        return true;
+        return valid;
     }
 
     /**
-     * Notifies the component's parent that it needs to re-layout.
+     * Flags the component's hierarchy as invalid, and clears any cached
+     * preferred size.
      */
     public void invalidate() {
+        valid = false;
+
+        // Clear the preferred size
         preferredSize = null;
 
         if (parent != null) {
@@ -1620,12 +1620,13 @@
 
     /**
      * Lays out the component by calling {@link Skin#layout()}.
-     * <p>
-     * This is an effective no-op for non-containers since the skin's
-     * implementation of layout() will be a no-op.
      */
     public void validate() {
-        skin.layout();
+        if (!valid
+            && visible) {
+            skin.layout();
+            valid = true;
+        }
     }
 
     /**

Modified: incubator/pivot/trunk/wtk/src/pivot/wtk/Container.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtk/Container.java?rev=776701&r1=776700&r2=776701&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtk/Container.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtk/Container.java Wed May 20 13:45:43 2009
@@ -125,8 +125,6 @@
     // are removed from the list and appended to the end
     private ArrayList<Component> components = new ArrayList<Component>();
 
-    private boolean valid = true;
-
     private FocusTraversalPolicy focusTraversalPolicy = null;
     private String contextKey = null;
 
@@ -315,28 +313,14 @@
     }
 
     @Override
-    public boolean isValid() {
-        return valid;
-    }
-
-    @Override
-    public void invalidate() {
-        valid = false;
-        super.invalidate();
-    }
-
-    @Override
     public void validate() {
-        super.validate();
-
-        if (!valid) {
-            try {
-                for (int i = 0, n = components.getLength(); i < n; i++) {
-                    Component component = components.get(i);
-                    component.validate();
-                }
-            } finally {
-                valid = true;
+        if (!isValid()
+            && isVisible()) {
+            super.validate();
+
+            for (int i = 0, n = components.getLength(); i < n; i++) {
+                Component component = components.get(i);
+                component.validate();
             }
         }
     }

Modified: incubator/pivot/trunk/wtk/src/pivot/wtk/FlowPane.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtk/FlowPane.java?rev=776701&r1=776700&r2=776701&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtk/FlowPane.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtk/FlowPane.java Wed May 20 13:45:43 2009
@@ -65,4 +65,8 @@
 
         setOrientation(Orientation.decode(orientation));
     }
+
+    public ListenerList<FlowPaneListener> getFlowPaneListeners() {
+        return flowPaneListeners;
+    }
 }

Modified: incubator/pivot/trunk/wtk/src/pivot/wtk/skin/CardPaneSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtk/skin/CardPaneSkin.java?rev=776701&r1=776700&r2=776701&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtk/skin/CardPaneSkin.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtk/skin/CardPaneSkin.java Wed May 20 13:45:43 2009
@@ -225,6 +225,7 @@
     public static final int SELECTION_CHANGE_DURATION = 250;
     public static final int SELECTION_CHANGE_RATE = 30;
 
+    @Override
     public void install(Component component) {
         super.install(component);
 
@@ -232,6 +233,7 @@
         cardPane.getCardPaneListeners().add(this);
     }
 
+    @Override
     public void uninstall() {
         CardPane cardPane = (CardPane)getComponent();
         cardPane.getCardPaneListeners().remove(this);
@@ -344,9 +346,6 @@
         int height = getHeight();
 
         for (Component card : cardPane) {
-            // Setting the size of non-visible cards does not impose a
-            // performance penalty since setSize() does not trigger an
-            // invalidate unless the component is visible
             card.setLocation(0, 0);
             card.setSize(width, height);
         }
@@ -357,6 +356,10 @@
     }
 
     public void setSizeToSelection(boolean sizeToSelection) {
+        if (selectionChangeTransition != null) {
+            selectionChangeTransition.end();
+        }
+
         this.sizeToSelection = sizeToSelection;
         invalidateComponent();
     }
@@ -366,7 +369,10 @@
     }
 
     public void setSelectionChangeEffect(SelectionChangeEffect selectionChangeEffect) {
-        // TODO Check against sizeToSelection?
+        if (selectionChangeTransition != null) {
+            selectionChangeTransition.end();
+        }
+
         this.selectionChangeEffect = selectionChangeEffect;
     }
 
@@ -381,8 +387,15 @@
     @Override
     public void componentInserted(Container container, int index) {
         if (selectionChangeTransition != null) {
-            selectionChangeTransition.stop();
-            selectionChangeTransition = null;
+            if (selectionChangeTransition.from >= index) {
+                selectionChangeTransition.from++;
+            }
+
+            if (selectionChangeTransition.to >= index) {
+                selectionChangeTransition.to++;
+            }
+
+            selectionChangeTransition.end();
         }
 
         super.componentInserted(container, index);
@@ -463,10 +476,6 @@
             && selectionChangeTransition != null) {
             selectionChangeTransition.stop();
             selectionChangeTransition = null;
-
-            if (sizeToSelection) {
-                invalidateComponent();
-            }
         }
     }
 

Modified: incubator/pivot/trunk/wtk/src/pivot/wtk/skin/FlowPaneSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtk/skin/FlowPaneSkin.java?rev=776701&r1=776700&r2=776701&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtk/skin/FlowPaneSkin.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtk/skin/FlowPaneSkin.java Wed May 20 13:45:43 2009
@@ -41,6 +41,22 @@
     private Insets padding = new Insets(0);
     private int spacing = 4;
 
+    @Override
+    public void install(Component component) {
+        super.install(component);
+
+        FlowPane flowPane = (FlowPane)component;
+        flowPane.getFlowPaneListeners().add(this);
+    }
+
+    @Override
+    public void uninstall() {
+        FlowPane flowPane = (FlowPane)getComponent();
+        flowPane.getFlowPaneListeners().remove(this);
+
+        super.uninstall();
+    }
+
     public int getPreferredWidth(int height) {
         int preferredWidth = 0;
 

Modified: incubator/pivot/trunk/wtk/src/pivot/wtk/skin/terra/TerraTabPaneSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtk/skin/terra/TerraTabPaneSkin.java?rev=776701&r1=776700&r2=776701&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtk/skin/terra/TerraTabPaneSkin.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtk/skin/terra/TerraTabPaneSkin.java Wed May 20 13:45:43 2009
@@ -44,7 +44,6 @@
 import pivot.wtk.Button.Group;
 import pivot.wtk.content.ButtonData;
 import pivot.wtk.content.ButtonDataRenderer;
-import pivot.wtk.effects.ClipDecorator;
 import pivot.wtk.effects.Transition;
 import pivot.wtk.effects.TransitionListener;
 import pivot.wtk.effects.easing.Easing;
@@ -62,22 +61,24 @@
  * style properties to present a disabled tab button state. We'd also need
  * to manage button enabled state independently of tab pane enabled state.
  * <p>
- * TODO Add showCloseButton style.
+ * TODO Add support for closeable attribute.
  *
  * @author gbrown
  */
 public class TerraTabPaneSkin extends ContainerSkin
-    implements TabPaneListener, TabPaneSelectionListener, TabPaneAttributeListener,
-        Button.GroupListener {
-    protected class TabButton extends Button {
-    	private boolean active = false;
-
+    implements TabPaneListener, TabPaneSelectionListener, TabPaneAttributeListener {
+    /**
+     * Tab button component.
+     *
+     * @author gbrown
+     */
+    public class TabButton extends Button {
     	public TabButton() {
             this(null);
         }
 
-        public TabButton(Object buttonData) {
-            super(buttonData);
+        public TabButton(Object tab) {
+            super(tab);
 
             super.setToggleButton(true);
             setDataRenderer(DEFAULT_DATA_RENDERER);
@@ -109,63 +110,23 @@
         }
     }
 
-    protected class TabButtonSkin extends ButtonSkin {
+    /**
+     * Tab button skin.
+     * <p>
+     * Note that this class does not respect preferred size constraints,
+     * because it will never be called to use them.
+     *
+     * @author gbrown
+     */
+    public class TabButtonSkin extends ButtonSkin {
         public int getPreferredWidth(int height) {
-            TabButton tabButton = (TabButton)getComponent();
-
-            Button.DataRenderer dataRenderer = tabButton.getDataRenderer();
-            dataRenderer.render(tabButton.getButtonData(), tabButton, false);
-
-            // Include padding in constraint
-            if (height != -1) {
-                height = Math.max(height - (buttonPadding.top + buttonPadding.bottom + 2), 0);
-            }
-
-            int preferredWidth = 0;
-            switch (tabOrientation) {
-                case HORIZONTAL: {
-                    preferredWidth = dataRenderer.getPreferredWidth(height)
-                        + buttonPadding.left + buttonPadding.right + 2;
-                    break;
-                }
-
-                case VERTICAL: {
-                    preferredWidth = dataRenderer.getPreferredHeight(height)
-                        + buttonPadding.top + buttonPadding.bottom + 2;
-                    break;
-                }
-            }
-
-            return preferredWidth;
+            Dimensions preferredSize = getPreferredSize();
+            return preferredSize.width;
         }
 
         public int getPreferredHeight(int width) {
-            TabButton tabButton = (TabButton)getComponent();
-
-            Button.DataRenderer dataRenderer = tabButton.getDataRenderer();
-            dataRenderer.render(tabButton.getButtonData(), tabButton, false);
-
-            // Include padding in constraint
-            if (width != -1) {
-                width = Math.max(width - (buttonPadding.left + buttonPadding.right + 2), 0);
-            }
-
-            int preferredHeight = 0;
-            switch (tabOrientation) {
-                case HORIZONTAL: {
-                    preferredHeight = dataRenderer.getPreferredHeight(width)
-                        + buttonPadding.top + buttonPadding.bottom + 2;
-                    break;
-                }
-
-                case VERTICAL: {
-                    preferredHeight = dataRenderer.getPreferredWidth(width)
-                        + buttonPadding.left + buttonPadding.right + 2;
-                    break;
-                }
-            }
-
-            return preferredHeight;
+            Dimensions preferredSize = getPreferredSize();
+            return preferredSize.height;
         }
 
         public Dimensions getPreferredSize() {
@@ -200,14 +161,19 @@
                 }
             }
 
-            return new Dimensions(preferredWidth, preferredHeight);
+            Dimensions preferredSize = new Dimensions(preferredWidth, preferredHeight);
+            return preferredSize;
         }
 
         public void paint(Graphics2D graphics) {
             TabButton tabButton = (TabButton)getComponent();
 
+            Component tab = (Component)tabButton.getButtonData();
+            boolean active = (selectionChangeTransition != null
+                && selectionChangeTransition.getTab() == tab);
+
             Color backgroundColor = (tabButton.isSelected()
-                || tabButton.active) ?
+                || active) ?
                 activeTabColor : inactiveTabColor;
 
             int width = getWidth();
@@ -237,7 +203,7 @@
             graphics.setPaint(borderColor);
 
             if (tabButton.isSelected()
-                || tabButton.active) {
+                || active) {
                 switch(tabOrientation) {
                     case HORIZONTAL: {
                         GraphicsUtilities.drawLine(graphics, 0, 0, height, Orientation.VERTICAL);
@@ -317,14 +283,30 @@
         }
     }
 
-    public class ExpandTransition extends Transition {
-    	boolean collapse;
+    /**
+     * Selection change transition.
+     *
+     * @author gbrown
+     */
+    public class SelectionChangeTransition extends Transition {
+        private Component tab;
+        private boolean expand;
+
         private Easing easing = new Quadratic();
-        private ClipDecorator clipDecorator = new ClipDecorator();
 
-        public ExpandTransition(boolean collapse, int duration, int rate) {
-            super(duration, rate, false);
-            this.collapse = collapse;
+        public SelectionChangeTransition(Component tab, boolean expand) {
+            super(SELECTION_CHANGE_DURATION, SELECTION_CHANGE_RATE, false);
+
+            this.tab = tab;
+            this.expand = expand;
+        }
+
+        public Component getTab() {
+            return tab;
+        }
+
+        public boolean isExpand() {
+            return expand;
         }
 
         public float getScale() {
@@ -332,10 +314,10 @@
             int duration = getDuration();
 
             float scale;
-            if (collapse) {
-                scale = easing.easeIn(elapsedTime, 1, -1, duration);
-            } else {
+            if (expand) {
                 scale = easing.easeOut(elapsedTime, 0, 1, duration);
+            } else {
+                scale = easing.easeIn(elapsedTime, 1, -1, duration);
             }
 
             return scale;
@@ -343,15 +325,13 @@
 
         @Override
         public void start(TransitionListener transitionListener) {
-        	TabPane tabPane = (TabPane)getComponent();
-        	Component selectedTab = tabPane.getSelectedTab();
-        	selectedTab.getDecorators().add(clipDecorator);
+            TabPane tabPane = (TabPane)getComponent();
 
-        	int selectedIndex = tabPane.getSelectedIndex();
-        	TabButton tabButton = (TabButton)buttonFlowPane.get(selectedIndex);
-        	tabButton.active = true;
+            if (expand) {
+                tab.setVisible(true);
+            }
 
-        	getComponent().setEnabled(false);
+            tabPane.setEnabled(false);
 
             super.start(transitionListener);
         }
@@ -359,14 +339,12 @@
         @Override
         public void stop() {
         	TabPane tabPane = (TabPane)getComponent();
-        	Component selectedTab = tabPane.getSelectedTab();
-        	selectedTab.getDecorators().remove(clipDecorator);
 
-        	int selectedIndex = tabPane.getSelectedIndex();
-        	TabButton tabButton = (TabButton)buttonFlowPane.get(selectedIndex);
-        	tabButton.active = false;
+            if (!expand) {
+                tab.setVisible(false);
+            }
 
-        	getComponent().setEnabled(true);
+        	tabPane.setEnabled(true);
 
         	super.stop();
         }
@@ -395,13 +373,23 @@
     private boolean collapsible = false;
     private Orientation tabOrientation = Orientation.HORIZONTAL;
 
-    private ExpandTransition expandTransition = null;
+    private SelectionChangeTransition selectionChangeTransition = null;
 
-    private static final int EXPAND_DURATION = 250;
-    private static final int EXPAND_RATE = 30;
+    private static final int SELECTION_CHANGE_DURATION = 250;
+    private static final int SELECTION_CHANGE_RATE = 30;
 
 	public static final int GRADIENT_BEVEL_THICKNESS = 4;
-	private static final Button.DataRenderer DEFAULT_DATA_RENDERER = new ButtonDataRenderer();
+	private static final Button.DataRenderer DEFAULT_DATA_RENDERER = new ButtonDataRenderer() {
+	    @Override
+	    public void render(Object data, Button button, boolean highlighted) {
+	        // TODO Create a custom inner renderer class that can display
+	        // the close button (and also avoid the heap allocation every
+	        // time we're called to render())
+	        Component tab = (Component)data;
+	        super.render(new ButtonData(TabPane.getIcon(tab), TabPane.getName(tab)),
+                button, highlighted);
+	    }
+	};
 
     public TerraTabPaneSkin() {
         TerraTheme theme = (TerraTheme)Theme.getTheme();
@@ -416,23 +404,18 @@
         // Set the derived colors
         buttonBevelColor = TerraTheme.brighten(inactiveTabColor);
 
-        tabButtonGroup.getGroupListeners().add(this);
-
-        buttonFlowPane.getStyles().put("spacing", 2);
-    }
+        setButtonSpacing(2);
 
-	@Override
-	public void setSize(int width, int height) {
-		if (expandTransition != null) {
-			if ((tabOrientation == Orientation.HORIZONTAL && width != getWidth())
-				|| (tabOrientation == Orientation.VERTICAL && height != getHeight())) {
-				expandTransition.end();
-				expandTransition = null;
-			}
-		}
+        tabButtonGroup.getGroupListeners().add(new Button.GroupListener() {
+            public void selectionChanged(Group group, Button previousSelection) {
+                Button button = tabButtonGroup.getSelection();
+                int index = (button == null) ? -1 : buttonFlowPane.indexOf(button);
 
-		super.setSize(width, height);
-	}
+                TabPane tabPane = (TabPane)getComponent();
+                tabPane.setSelectedIndex(index);
+            }
+        });
+    }
 
     public void install(Component component) {
         super.install(component);
@@ -452,8 +435,7 @@
 
         // Add buttons for all existing tabs
         for (Component tab : tabPane.getTabs()) {
-            TabButton tabButton = new TabButton(new ButtonData(TabPane.getIcon(tab),
-                TabPane.getName(tab)));
+            TabButton tabButton = new TabButton(tab);
             tabButton.setGroup(tabButtonGroup);
 
             buttonFlowPane.add(tabButton);
@@ -477,189 +459,266 @@
     }
 
     public int getPreferredWidth(int height) {
-        int preferredWidth;
+        int preferredWidth = 0;
 
         TabPane tabPane = (TabPane)getComponent();
-        if (expandTransition == null
-    		|| tabOrientation == Orientation.VERTICAL) {
-        	preferredWidth = 0;
 
-            Component selectedTab = tabPane.getSelectedTab();
-            Component corner = tabPane.getCorner();
-
-            switch (tabOrientation) {
-                case HORIZONTAL: {
-                    if (height != -1) {
-                        if (corner != null
-                            && corner.isDisplayable()) {
-                            height = Math.max(height - Math.max(corner.getPreferredHeight(-1),
-                                Math.max(buttonPanorama.getPreferredHeight(-1) - 1, 0)), 0);
-                        } else {
-                            height = Math.max(height - (buttonPanorama.getPreferredHeight(-1) - 1), 0);
-                        }
+        Component selectedTab = tabPane.getSelectedTab();
+        Component corner = tabPane.getCorner();
 
-                        height = Math.max(height - (padding.top + padding.bottom + 2), 0);
+        switch (tabOrientation) {
+            case HORIZONTAL: {
+                if (height != -1) {
+                    if (corner != null
+                        && corner.isDisplayable()) {
+                        height = Math.max(height - Math.max(corner.getPreferredHeight(-1),
+                            Math.max(buttonPanorama.getPreferredHeight(-1) - 1, 0)), 0);
+                    } else {
+                        height = Math.max(height - (buttonPanorama.getPreferredHeight(-1) - 1), 0);
                     }
 
-                    if (selectedTab != null) {
-                        for (Component tab : tabPane.getTabs()) {
-                            preferredWidth = Math.max(preferredWidth,
-                                tab.getPreferredWidth(height));
-                        }
+                    height = Math.max(height - (padding.top + padding.bottom + 2), 0);
+                }
 
-                        preferredWidth += (padding.left + padding.right + 2);
-                    }
+                preferredWidth = getPreferredTabWidth(height) + (padding.left + padding.right + 2);
 
-                    int buttonAreaPreferredWidth = buttonPanorama.getPreferredWidth(-1);
+                int buttonAreaPreferredWidth = buttonPanorama.getPreferredWidth(-1);
+                if (corner != null
+                    && corner.isDisplayable()) {
+                    buttonAreaPreferredWidth += corner.getPreferredWidth(-1);
+                }
 
-                    if (corner != null
-                        && corner.isDisplayable()) {
-                        buttonAreaPreferredWidth += corner.getPreferredWidth(-1);
-                    }
+                preferredWidth = Math.max(preferredWidth, buttonAreaPreferredWidth);
 
-                    preferredWidth = Math.max(preferredWidth,
-                        buttonAreaPreferredWidth);
+                break;
+            }
 
-                    break;
+            case VERTICAL: {
+                if (height != -1) {
+                    height = Math.max(height - (padding.top + padding.bottom + 2), 0);
                 }
 
-                case VERTICAL: {
-                    if (height != -1) {
-                        height = Math.max(height - (padding.top + padding.bottom + 2), 0);
-                    }
-
-                    if (selectedTab != null) {
-                    	if (expandTransition == null) {
-                        	int contentWidth = 0;
-                            for (Component tab : tabPane.getTabs()) {
-                                contentWidth = Math.max(contentWidth,
-                                    tab.getPreferredWidth(height));
-                            }
-
-                            preferredWidth += (padding.left + padding.right + contentWidth);
-                    	} else {
-                            float scale = expandTransition.getScale();
-                            preferredWidth += (int)(scale * (float)(padding.left + padding.right
-                        		+ selectedTab.getWidth()));
-                    	}
+                if (selectedTab == null
+                    && selectionChangeTransition == null) {
+                    preferredWidth = 1;
+                } else {
+                    preferredWidth = getPreferredTabWidth(height) + (padding.left + padding.right);
 
-                        preferredWidth += 2;
-                    } else {
-                        preferredWidth += 1;
+                    if (selectionChangeTransition != null) {
+                        float scale = selectionChangeTransition.getScale();
+                        preferredWidth = (int)((float)preferredWidth * scale);
                     }
 
-                    if (corner != null
-                        && corner.isDisplayable()) {
-                        preferredWidth += Math.max(corner.getPreferredWidth(-1),
-                            Math.max(buttonPanorama.getPreferredWidth(-1) - 1, 0));
-                    } else {
-                        preferredWidth += Math.max(buttonPanorama.getPreferredWidth(-1) - 1, 0);
-                    }
+                    preferredWidth += 2;
+                }
 
-                    break;
+                if (corner != null
+                    && corner.isDisplayable()) {
+                    preferredWidth += Math.max(corner.getPreferredWidth(-1),
+                        Math.max(buttonPanorama.getPreferredWidth(-1) - 1, 0));
+                } else {
+                    preferredWidth += Math.max(buttonPanorama.getPreferredWidth(-1) - 1, 0);
                 }
+
+                break;
             }
-        } else {
-        	preferredWidth = getWidth();
         }
 
         return preferredWidth;
     }
 
     public int getPreferredHeight(int width) {
-        int preferredHeight;
+        int preferredHeight = 0;
 
         TabPane tabPane = (TabPane)getComponent();
-        if (expandTransition == null
-    		|| tabOrientation == Orientation.HORIZONTAL) {
-        	preferredHeight = 0;
-            Component selectedTab = tabPane.getSelectedTab();
-            Component corner = tabPane.getCorner();
 
-            switch (tabOrientation) {
-                case HORIZONTAL: {
-                    if (width != -1) {
-                        width = Math.max(width - (padding.left + padding.right + 2), 0);
-                    }
+        Component selectedTab = tabPane.getSelectedTab();
+        Component corner = tabPane.getCorner();
+
+        switch (tabOrientation) {
+            case HORIZONTAL: {
+                if (width != -1) {
+                    width = Math.max(width - (padding.left + padding.right + 2), 0);
+                }
 
-                    if (selectedTab != null) {
-                    	if (expandTransition == null) {
-                    		int contentHeight = 0;
-                            for (Component tab : tabPane.getTabs()) {
-                            	contentHeight = Math.max(contentHeight,
-                                    tab.getPreferredHeight(width));
-                            }
-
-                            preferredHeight += (padding.top + padding.bottom + contentHeight);
-                    	} else {
-                    		float scale = expandTransition.getScale();
-                            preferredHeight += (int)(scale * (float)(padding.top + padding.bottom
-                        		+ selectedTab.getHeight()));
-                    	}
+                if (selectedTab == null
+                    && selectionChangeTransition == null) {
+                    preferredHeight = 1;
+                } else {
+                    preferredHeight = getPreferredTabHeight(width) + (padding.top + padding.bottom);
 
-                        preferredHeight += 2;
-                    } else {
-                        preferredHeight += 1;
+                    if (selectionChangeTransition != null) {
+                        float scale = selectionChangeTransition.getScale();
+                        preferredHeight = (int)((float)preferredHeight * scale);
                     }
 
+                    preferredHeight += 2;
+                }
+
+                if (corner != null
+                    && corner.isDisplayable()) {
+                    preferredHeight += Math.max(corner.getPreferredHeight(-1),
+                        Math.max(buttonPanorama.getPreferredHeight(-1) - 1, 0));
+                } else {
+                    preferredHeight += Math.max(buttonPanorama.getPreferredHeight(-1) - 1, 0);
+                }
+
+                break;
+            }
+
+            case VERTICAL: {
+                if (width != -1) {
                     if (corner != null
                         && corner.isDisplayable()) {
-                        preferredHeight += Math.max(corner.getPreferredHeight(-1),
-                            Math.max(buttonPanorama.getPreferredHeight(-1) - 1, 0));
+                        width = Math.max(width - Math.max(corner.getPreferredWidth(-1),
+                            Math.max(buttonPanorama.getPreferredWidth(-1) - 1, 0)), 0);
                     } else {
-                        preferredHeight += Math.max(buttonPanorama.getPreferredHeight(-1) - 1, 0);
+                        width = Math.max(width - (buttonPanorama.getPreferredWidth(-1) - 1), 0);
                     }
 
-                    break;
+                    width = Math.max(width - (padding.left + padding.right + 2), 0);
                 }
 
-                case VERTICAL: {
-                    if (width != -1) {
-                        if (corner != null
-                            && corner.isDisplayable()) {
-                            width = Math.max(width - Math.max(corner.getPreferredWidth(-1),
-                                Math.max(buttonPanorama.getPreferredWidth(-1) - 1, 0)), 0);
-                        } else {
-                            width = Math.max(width - (buttonPanorama.getPreferredWidth(-1) - 1), 0);
-                        }
+                preferredHeight = getPreferredTabHeight(width) + (padding.top + padding.bottom + 2);
 
-                        width = Math.max(width - (padding.left + padding.right + 2), 0);
-                    }
+                int buttonAreaPreferredHeight = buttonPanorama.getPreferredHeight(-1);
+                if (corner != null
+                    && corner.isDisplayable()) {
+                    buttonAreaPreferredHeight += corner.getPreferredHeight(-1);
+                }
 
-                    if (selectedTab != null) {
-                        for (Component tab : tabPane.getTabs()) {
-                            preferredHeight = Math.max(preferredHeight,
-                                tab.getPreferredHeight(width));
-                        }
+                preferredHeight = Math.max(preferredHeight, buttonAreaPreferredHeight);
+
+                break;
+            }
+        }
+
+        return preferredHeight;
+    }
+
+    public Dimensions getPreferredSize() {
+        TabPane tabPane = (TabPane)getComponent();
 
-                        preferredHeight += (padding.top + padding.bottom + 2);
+        int preferredWidth;
+        int preferredHeight;
+
+        Component selectedTab = tabPane.getSelectedTab();
+        Component corner = tabPane.getCorner();
+
+        switch (tabOrientation) {
+            case HORIZONTAL: {
+                if (selectedTab == null
+                    && selectionChangeTransition == null) {
+                    preferredWidth = getPreferredTabWidth(-1) + (padding.left + padding.right + 2);
+                    preferredHeight = 1;
+                } else {
+                    Dimensions preferredTabSize = getPreferredTabSize();
+                    preferredWidth = preferredTabSize.width + (padding.left + padding.right + 2);
+                    preferredHeight = preferredTabSize.height + (padding.top + padding.bottom);
+
+                    if (selectionChangeTransition != null) {
+                        float scale = selectionChangeTransition.getScale();
+                        preferredHeight = (int)((float)preferredHeight * scale);
                     }
 
-                    int buttonAreaPreferredHeight = buttonPanorama.getPreferredHeight(-1);
+                    preferredHeight += 2;
+                }
 
-                    if (corner != null
-                        && corner.isDisplayable()) {
-                        buttonAreaPreferredHeight += corner.getPreferredHeight(-1);
+                int buttonAreaPreferredWidth = buttonPanorama.getPreferredWidth(-1);
+                if (corner != null
+                    && corner.isDisplayable()) {
+                    buttonAreaPreferredWidth += corner.getPreferredWidth(-1);
+                    preferredHeight += Math.max(corner.getPreferredHeight(-1),
+                        Math.max(buttonPanorama.getPreferredHeight(-1) - 1, 0));
+                } else {
+                    preferredHeight += Math.max(buttonPanorama.getPreferredHeight(-1) - 1, 0);
+                }
+
+                preferredWidth = Math.max(preferredWidth, buttonAreaPreferredWidth);
+
+                break;
+            }
+
+            case VERTICAL: {
+                if (selectedTab == null
+                    && selectionChangeTransition == null) {
+                    preferredWidth = 1;
+                    preferredHeight = getPreferredTabHeight(-1) + (padding.top + padding.bottom + 2);
+                } else {
+                    Dimensions preferredTabSize = getPreferredTabSize();
+
+                    preferredWidth = preferredTabSize.width + (padding.left + padding.right);
+                    preferredHeight = preferredTabSize.height + (padding.top + padding.bottom + 2);
+
+                    if (selectionChangeTransition != null) {
+                        float scale = selectionChangeTransition.getScale();
+                        preferredWidth = (int)((float)preferredWidth * scale);
                     }
 
-                    preferredHeight = Math.max(preferredHeight,
-                        buttonAreaPreferredHeight);
+                    preferredWidth += 2;
+                }
 
-                    break;
+                int buttonAreaPreferredHeight = buttonPanorama.getPreferredHeight(-1);
+                if (corner != null
+                    && corner.isDisplayable()) {
+                    preferredWidth += Math.max(corner.getPreferredWidth(-1),
+                        Math.max(buttonPanorama.getPreferredWidth(-1) - 1, 0));
+                    buttonAreaPreferredHeight += corner.getPreferredHeight(-1);
+                } else {
+                    preferredWidth += Math.max(buttonPanorama.getPreferredWidth(-1) - 1, 0);
                 }
+
+                preferredHeight = Math.max(preferredHeight, buttonAreaPreferredHeight);
+
+                break;
+            }
+
+            default: {
+                preferredWidth = 0;
+                preferredHeight = 0;
             }
-        } else {
-        	preferredHeight = getHeight();
         }
 
-        return preferredHeight;
+        return new Dimensions(preferredWidth, preferredHeight);
     }
 
-    public Dimensions getPreferredSize() {
-        // TODO Optimize
-        return new Dimensions(getPreferredWidth(-1), getPreferredHeight(-1));
+    private int getPreferredTabWidth(int height) {
+        int preferredTabWidth = 0;
+
+        TabPane tabPane = (TabPane)getComponent();
+        for (Component tab : tabPane.getTabs()) {
+            preferredTabWidth = Math.max(preferredTabWidth, tab.getPreferredWidth(height));
+        }
+
+        return preferredTabWidth;
     }
 
+    private int getPreferredTabHeight(int width) {
+        int preferredTabHeight = 0;
+
+        TabPane tabPane = (TabPane)getComponent();
+        for (Component tab : tabPane.getTabs()) {
+            preferredTabHeight = Math.max(preferredTabHeight, tab.getPreferredHeight(width));
+        }
+
+        return preferredTabHeight;
+    }
+
+    private Dimensions getPreferredTabSize() {
+        int preferredTabWidth = 0;
+        int preferredTabHeight = 0;
+
+        TabPane tabPane = (TabPane)getComponent();
+        for (Component tab : tabPane.getTabs()) {
+            Dimensions preferredSize = tab.getPreferredSize();
+            preferredTabWidth = Math.max(preferredTabWidth, preferredSize.width);
+            preferredTabHeight = Math.max(preferredTabHeight, preferredSize.height);
+        }
+
+        return new Dimensions(preferredTabWidth, preferredTabHeight);
+    }
+
+
     public void layout() {
         TabPane tabPane = (TabPane)getComponent();
         int width = getWidth();
@@ -670,20 +729,12 @@
         int tabWidth = 0;
         int tabHeight = 0;
 
-        Component selectedTab = tabPane.getSelectedTab();
         Component corner = tabPane.getCorner();
-
-        Dimensions buttonPanoramaSize;
-        if (expandTransition == null) {
-        	buttonPanoramaSize = buttonPanorama.getPreferredSize();
-        } else {
-        	buttonPanoramaSize = buttonPanorama.getSize();
-        }
+        Dimensions buttonPanoramaSize = buttonPanorama.getPreferredSize();
 
         switch (tabOrientation) {
             case HORIZONTAL: {
-                int buttonPanoramaWidth = Math.min(width,
-                    buttonPanoramaSize.width);
+                int buttonPanoramaWidth = Math.min(width, buttonPanoramaSize.width);
                 int buttonPanoramaHeight = buttonPanoramaSize.height;
                 int buttonPanoramaX = 0;
                 int buttonPanoramaY = 0;
@@ -754,51 +805,10 @@
             }
         }
 
-        if (expandTransition == null) {
-            for (Component tab : tabPane.getTabs()) {
-                if (tab == selectedTab) {
-                    // Show the selected tab
-                    tab.setVisible(true);
-
-                    // Set the tab's size and location
-                    tab.setLocation(tabX, tabY);
-                    tab.setSize(tabWidth, tabHeight);
-                } else {
-                    tab.setVisible(false);
-                }
-            }
-        } else {
-        	if (expandTransition.isRunning()) {
-            	// Update the clip
-            	expandTransition.clipDecorator.setWidth(tabWidth);
-            	expandTransition.clipDecorator.setHeight(tabHeight);
-        	} else {
-            	switch (tabOrientation) {
-            		case HORIZONTAL: {
-            			tabHeight = 0;
-            			for (Component tab : tabPane.getTabs()) {
-            				tabHeight = Math.max(tab.getPreferredHeight(tabWidth), tabHeight);
-            			}
-
-            			break;
-            		}
-
-            		case VERTICAL: {
-            			tabWidth = 0;
-            			for (Component tab : tabPane.getTabs()) {
-            				tabWidth = Math.max(tab.getPreferredWidth(tabHeight), tabWidth);
-            			}
-
-            			break;
-            		}
-            	}
-
-                selectedTab.setLocation(tabX, tabY);
-                selectedTab.setSize(tabWidth, tabHeight);
-
-            	selectedTab.setVisible(true);
-        	}
-
+        // Lay out the tabs
+        for (Component tab : tabPane.getTabs()) {
+            tab.setLocation(tabX, tabY);
+            tab.setSize(tabWidth, tabHeight);
         }
     }
 
@@ -837,13 +847,12 @@
             }
         }
 
-        Bounds contentBounds = new Bounds(x, y, width, height);
+        int selectedIndex = tabPane.getSelectedIndex();
+        if (selectedIndex != -1
+            || selectionChangeTransition != null) {
+            Bounds contentBounds = new Bounds(x, y, width, height);
 
-        if (!contentBounds.isEmpty()) {
-            // If a tab is selected, paint the active background color; otherwise,
-            // paint the inactive background color
-            int selectedIndex = tabPane.getSelectedIndex();
-            graphics.setPaint((selectedIndex == -1) ? inactiveTabColor : activeTabColor);
+            graphics.setPaint(activeTabColor);
             graphics.fillRect(contentBounds.x, contentBounds.y,
                 contentBounds.width, contentBounds.height);
 
@@ -1040,17 +1049,16 @@
 
         buttonFlowPane.setOrientation(tabOrientation);
 
-        Component.StyleDictionary buttonFlowPaneStyles = buttonFlowPane.getStyles();
         switch (tabOrientation) {
             case HORIZONTAL: {
-                buttonFlowPaneStyles.put("horizontalAlignment", HorizontalAlignment.LEFT);
-                buttonFlowPaneStyles.put("verticalAlignment", VerticalAlignment.JUSTIFY);
+                buttonFlowPane.getStyles().put("horizontalAlignment", HorizontalAlignment.LEFT);
+                buttonFlowPane.getStyles().put("verticalAlignment", VerticalAlignment.JUSTIFY);
                 break;
             }
 
             case VERTICAL: {
-                buttonFlowPaneStyles.put("horizontalAlignment", HorizontalAlignment.JUSTIFY);
-                buttonFlowPaneStyles.put("verticalAlignment", VerticalAlignment.TOP);
+                buttonFlowPane.getStyles().put("horizontalAlignment", HorizontalAlignment.JUSTIFY);
+                buttonFlowPane.getStyles().put("verticalAlignment", VerticalAlignment.TOP);
                 break;
             }
         }
@@ -1072,46 +1080,35 @@
         this.collapsible = collapsible;
     }
 
-    protected void updateButtonData(Component tab) {
-        TabPane tabPane = (TabPane)getComponent();
-        int tabIndex = tabPane.getTabs().indexOf(tab);
-
-        if (tabIndex != -1) {
-            TabButton tabButton =
-                (TabButton)buttonFlowPane.get(tabIndex);
-
-            tabButton.setButtonData(new ButtonData(TabPane.getIcon(tab),
-                TabPane.getName(tab)));
-        }
-    }
-
     // Tab pane events
     public void tabInserted(TabPane tabPane, int index) {
-    	if (expandTransition != null) {
-    		expandTransition.stop();
-    		expandTransition = null;
+    	if (selectionChangeTransition != null) {
+    	    selectionChangeTransition.end();
     	}
 
         // Create a new button for the tab
         Component tab = tabPane.getTabs().get(index);
-        TabButton tabButton = new TabButton(new ButtonData(TabPane.getIcon(tab),
-            TabPane.getName(tab)));
+        tab.setVisible(false);
+
+        TabButton tabButton = new TabButton(tab);
         tabButton.setGroup(tabButtonGroup);
 
         buttonFlowPane.insert(tabButton, index);
     }
 
-    public void tabsRemoved(TabPane tabPane, int index, Sequence<Component> tabs) {
-    	if (expandTransition != null) {
-    		expandTransition.stop();
-    		expandTransition = null;
+    public void tabsRemoved(TabPane tabPane, int index, Sequence<Component> removed) {
+    	if (selectionChangeTransition != null) {
+    	    selectionChangeTransition.end();
     	}
 
     	// Remove the buttons
-        Sequence<Component> removed = buttonFlowPane.remove(index, tabs.getLength());
+        Sequence<Component> removedButtons = buttonFlowPane.remove(index, removed.getLength());
 
         for (int i = 0, n = removed.getLength(); i < n; i++) {
-            TabButton tabButton = (TabButton)removed.get(i);
+            Component tab = removed.get(i);
+            tab.setVisible(true);
+
+            TabButton tabButton = (TabButton)removedButtons.get(i);
             tabButton.setGroup((Group)null);
         }
     }
@@ -1121,46 +1118,69 @@
     }
 
     // Tab pane selection events
-	public Vote previewSelectedIndexChange(final TabPane tabPane, final int selectedIndex) {
-        Vote vote = Vote.APPROVE;
+	public Vote previewSelectedIndexChange(TabPane tabPane, int selectedIndex) {
+        Vote vote;
 
-        if (tabPane.isShowing()) {
-            if (expandTransition == null) {
-                if (selectedIndex == -1) {
-                    expandTransition = new ExpandTransition(true, EXPAND_DURATION, EXPAND_RATE);
-
-                    layout();
-                    expandTransition.start(new TransitionListener() {
-                        public void transitionCompleted(Transition transition) {
-                            tabPane.setSelectedIndex(-1);
-                            expandTransition = null;
+        if (tabPane.isShowing()
+            && selectionChangeTransition == null) {
+            int previousSelectedIndex = tabPane.getSelectedIndex();
+
+            if (selectedIndex == -1) {
+                // Collapse
+                Component tab = tabPane.getTabs().get(previousSelectedIndex);
+                selectionChangeTransition = new SelectionChangeTransition(tab, false);
+            } else {
+                if (previousSelectedIndex == -1) {
+                    // Expand
+                    Component tab = tabPane.getTabs().get(selectedIndex);
+                    selectionChangeTransition = new SelectionChangeTransition(tab, true);
+                }
+            }
+
+            if (selectionChangeTransition != null) {
+                selectionChangeTransition.start(new TransitionListener() {
+                    public void transitionCompleted(Transition transition) {
+                        TabPane tabPane = (TabPane)getComponent();
+
+                        SelectionChangeTransition selectionChangeTransition =
+                            (SelectionChangeTransition)transition;
+
+                        int selectedIndex;
+                        if (selectionChangeTransition.isExpand()) {
+                            Component tab = selectionChangeTransition.getTab();
+                            selectedIndex = tabPane.getTabs().indexOf(tab);
+                        } else {
+                            selectedIndex = -1;
                         }
-                    });
 
-                    vote = Vote.DEFER;
-                }
-            } else {
-            	if (expandTransition.isRunning()) {
-                	vote = Vote.DEFER;
-            	}
+                        tabPane.setSelectedIndex(selectedIndex);
+
+                        TerraTabPaneSkin.this.selectionChangeTransition = null;
+                    }
+                });
             }
         }
 
+        if (selectionChangeTransition == null
+            || !selectionChangeTransition.isRunning()) {
+            vote = Vote.APPROVE;
+        } else {
+            vote = Vote.DEFER;
+        }
+
         return vote;
 	}
 
 	public void selectedIndexChangeVetoed(TabPane tabPane, Vote reason) {
         if (reason == Vote.DENY
-            && expandTransition != null) {
-            expandTransition.stop();
-            expandTransition = null;
-            invalidateComponent();
+            && selectionChangeTransition != null) {
+            selectionChangeTransition.stop();
+            selectionChangeTransition = null;
         }
 	}
 
 	public void selectedIndexChanged(TabPane tabPane, int previousSelectedIndex) {
-        int selectedIndex = tabPane.getSelectedIndex();
-
+	    int selectedIndex = tabPane.getSelectedIndex();
         if (selectedIndex == -1) {
             Button button = tabButtonGroup.getSelection();
             if (button != null) {
@@ -1170,42 +1190,31 @@
             Button button = (Button)buttonFlowPane.get(selectedIndex);
             button.setSelected(true);
 
-            if (previousSelectedIndex == -1) {
-                if (tabPane.isShowing()) {
-                    expandTransition = new ExpandTransition(false, EXPAND_DURATION, EXPAND_RATE);
-
-                    layout();
-                    expandTransition.start(new TransitionListener() {
-                        public void transitionCompleted(Transition transition) {
-                            expandTransition = null;
-                        }
-                    });
-                }
-            }
+            Component selectedTab = tabPane.getTabs().get(selectedIndex);
+            selectedTab.setVisible(true);
         }
 
-        invalidateComponent();
+        if (previousSelectedIndex != -1) {
+            Component previousSelectedTab = tabPane.getTabs().get(previousSelectedIndex);
+            previousSelectedTab.setVisible(false);
+        }
+
+        if (selectedIndex == -1
+            || previousSelectedIndex == -1) {
+            invalidateComponent();
+        }
     }
 
     // Tab pane attribute events
     public void nameChanged(TabPane tabPane, Component component, String previousName) {
-        updateButtonData(component);
+        invalidateComponent();
     }
 
     public void iconChanged(TabPane tabPane, Component component, Image previousIcon) {
-        updateButtonData(component);
+        invalidateComponent();
     }
 
     public void closeableChanged(TabPane tabPane, Component component) {
-        // TODO
-    }
-
-    // Button group events
-    public void selectionChanged(Group group, Button previousSelection) {
-        Button button = tabButtonGroup.getSelection();
-        int index = (button == null) ? -1 : buttonFlowPane.indexOf(button);
-
-        TabPane tabPane = (TabPane)getComponent();
-        tabPane.setSelectedIndex(index);
+        invalidateComponent();
     }
 }

Modified: incubator/pivot/trunk/wtk/test/pivot/wtk/test/AccordionTest.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/test/pivot/wtk/test/AccordionTest.java?rev=776701&r1=776700&r2=776701&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/test/pivot/wtk/test/AccordionTest.java (original)
+++ incubator/pivot/trunk/wtk/test/pivot/wtk/test/AccordionTest.java Wed May 20 13:45:43 2009
@@ -18,6 +18,7 @@
 
 import pivot.collections.Dictionary;
 import pivot.wtk.Application;
+import pivot.wtk.DesktopApplicationContext;
 import pivot.wtk.Display;
 import pivot.wtk.Frame;
 import pivot.wtkx.WTKXSerializer;
@@ -42,4 +43,8 @@
 
     public void resume() {
     }
+
+    public static void main(String[] args) {
+        DesktopApplicationContext.main(AccordionTest.class, args);
+    }
 }

Modified: incubator/pivot/trunk/wtk/test/pivot/wtk/text/test/TextAreaTest.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/test/pivot/wtk/text/test/TextAreaTest.java?rev=776701&r1=776700&r2=776701&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/test/pivot/wtk/text/test/TextAreaTest.java (original)
+++ incubator/pivot/trunk/wtk/test/pivot/wtk/text/test/TextAreaTest.java Wed May 20 13:45:43 2009
@@ -21,6 +21,7 @@
 import pivot.collections.Sequence.Tree.Path;
 import pivot.wtk.Application;
 import pivot.wtk.Component;
+import pivot.wtk.DesktopApplicationContext;
 import pivot.wtk.Display;
 import pivot.wtk.Frame;
 import pivot.wtk.Label;
@@ -145,4 +146,8 @@
             charactersLabel.setText(Integer.toString(selectedNode.getCharacterCount()));
         }
     }
+
+    public static void main(String[] args) {
+        DesktopApplicationContext.main(TextAreaTest.class, args);
+    }
 }