Revision: 9748
Author: jlaba...@google.com
Date: Thu Feb 17 05:39:09 2011
Log: Fixing insert/remove bugs in StackLayoutPanel. Insert always appends to the end. Remove does not redraw the panel. Neither adjusted the selectedIndex when inserting/removing at a lower index. This patch also makes StackLayoutPanel implement AnimatedLayout so users can change the default animation duration or force layout.

Issue: 4926, 5304

Review at http://gwt-code-reviews.appspot.com/1359803

Review by: rchan...@google.com
http://code.google.com/p/google-web-toolkit/source/detail?r=9748

Modified:
 /trunk/user/src/com/google/gwt/user/client/ui/StackLayoutPanel.java
 /trunk/user/test/com/google/gwt/user/client/ui/StackLayoutPanelTest.java

=======================================
--- /trunk/user/src/com/google/gwt/user/client/ui/StackLayoutPanel.java Wed Feb 16 10:10:39 2011 +++ /trunk/user/src/com/google/gwt/user/client/ui/StackLayoutPanel.java Thu Feb 17 05:39:09 2011
@@ -30,6 +30,7 @@
 import com.google.gwt.event.logical.shared.SelectionEvent;
 import com.google.gwt.event.logical.shared.SelectionHandler;
 import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.layout.client.Layout.AnimationCallback;
 import com.google.gwt.safehtml.shared.SafeHtml;

 import java.util.ArrayList;
@@ -95,7 +96,7 @@
  * </pre>
  */
public class StackLayoutPanel extends ResizeComposite implements HasWidgets,
-    ProvidesResize, IndexedPanel.ForIsWidget,
+    ProvidesResize, IndexedPanel.ForIsWidget, AnimatedLayout,
     HasBeforeSelectionHandlers<Integer>, HasSelectionHandlers<Integer> {

   private class Header extends Composite implements HasClickHandlers {
@@ -135,6 +136,7 @@

   private static final int ANIMATION_TIME = 250;

+  private int animationDuration = ANIMATION_TIME;
   private LayoutPanel layoutPanel;
   private final Unit unit;
private final ArrayList<LayoutData> layoutData = new ArrayList<LayoutData>();
@@ -231,12 +233,68 @@
       SelectionHandler<Integer> handler) {
     return addHandler(handler, SelectionEvent.getType());
   }
+
+  public void animate(int duration) {
+    animate(duration, null);
+  }
+
+  public void animate(int duration, AnimationCallback callback) {
+    // Don't try to animate zero widgets.
+    if (layoutData.size() == 0) {
+      if (callback != null) {
+        callback.onAnimationComplete();
+      }
+      return;
+    }
+
+    double top = 0, bottom = 0;
+    int i = 0;
+    for (; i < layoutData.size(); ++i) {
+      LayoutData data = layoutData.get(i);
+ layoutPanel.setWidgetTopHeight(data.header, top, unit, data.headerSize,
+          unit);
+
+      top += data.headerSize;
+
+      layoutPanel.setWidgetTopHeight(data.widget, top, unit, 0, unit);
+
+      if (i == selectedIndex) {
+        break;
+      }
+    }
+
+    for (int j = layoutData.size() - 1; j > i; --j) {
+      LayoutData data = layoutData.get(j);
+      layoutPanel.setWidgetBottomHeight(data.header, bottom, unit,
+          data.headerSize, unit);
+ layoutPanel.setWidgetBottomHeight(data.widget, bottom, unit, 0, unit);
+      bottom += data.headerSize;
+    }
+
+    LayoutData data = layoutData.get(selectedIndex);
+    layoutPanel.setWidgetTopBottom(data.widget, top, unit, bottom, unit);
+
+    layoutPanel.animate(duration, callback);
+  }

   public void clear() {
     layoutPanel.clear();
     layoutData.clear();
     selectedIndex = -1;
   }
+
+  public void forceLayout() {
+    layoutPanel.forceLayout();
+  }
+
+  /**
+   * Get the duration of the animated transition between children.
+   *
+   * @return the duration in milliseconds
+   */
+  public int getAnimationDuration() {
+    return animationDuration;
+  }

   /**
    * Gets the widget in the stack header at the given index.
@@ -421,6 +479,11 @@
           if (layoutData.size() > 0) {
             showWidget(layoutData.get(0).widget);
           }
+        } else {
+          if (i <= selectedIndex) {
+            selectedIndex--;
+          }
+          animate(animationDuration);
         }
         return true;
       }
@@ -428,6 +491,15 @@

     return false;
   }
+
+  /**
+   * Set the duration of the animated transition between children.
+   *
+   * @param duration the duration in milliseconds.
+   */
+  public void setAnimationDuration(int duration) {
+    this.animationDuration = duration;
+  }

   /**
    * Sets a stack header's HTML contents.
@@ -491,7 +563,7 @@
    */
   public void showWidget(int index, boolean fireEvents) {
     checkIndex(index);
-    showWidget(index, ANIMATION_TIME, fireEvents);
+    showWidget(index, animationDuration, fireEvents);
   }

   /**
@@ -510,7 +582,7 @@
    * @param fireEvents true to fire events, false not to
    */
   public void showWidget(Widget child, boolean fireEvents) {
-    showWidget(getWidgetIndex(child), ANIMATION_TIME, fireEvents);
+    showWidget(getWidgetIndex(child), animationDuration, fireEvents);
   }

   @Override
@@ -518,42 +590,6 @@
     // When the widget becomes attached, update its layout.
     animate(0);
   }
-
-  private void animate(int duration) {
-    // Don't try to animate zero widgets.
-    if (layoutData.size() == 0) {
-      return;
-    }
-
-    double top = 0, bottom = 0;
-    int i = 0;
-    for (; i < layoutData.size(); ++i) {
-      LayoutData data = layoutData.get(i);
- layoutPanel.setWidgetTopHeight(data.header, top, unit, data.headerSize,
-          unit);
-
-      top += data.headerSize;
-
-      layoutPanel.setWidgetTopHeight(data.widget, top, unit, 0, unit);
-
-      if (i == selectedIndex) {
-        break;
-      }
-    }
-
-    for (int j = layoutData.size() - 1; j > i; --j) {
-      LayoutData data = layoutData.get(j);
-      layoutPanel.setWidgetBottomHeight(data.header, bottom, unit,
-          data.headerSize, unit);
- layoutPanel.setWidgetBottomHeight(data.widget, bottom, unit, 0, unit);
-      bottom += data.headerSize;
-    }
-
-    LayoutData data = layoutData.get(selectedIndex);
-    layoutPanel.setWidgetTopBottom(data.widget, top, unit, bottom, unit);
-
-    layoutPanel.animate(duration);
-  }

   private void checkChild(Widget child) {
     assert layoutPanel.getChildren().contains(child);
@@ -577,15 +613,15 @@
       }
     }

-    beforeIndex *= 2;
-    layoutPanel.insert(child, beforeIndex);
-    layoutPanel.insert(header, beforeIndex);
+    int widgetIndex = beforeIndex * 2;
+    layoutPanel.insert(child, widgetIndex);
+    layoutPanel.insert(header, widgetIndex);

     layoutPanel.setWidgetLeftRight(header, 0, Unit.PX, 0, Unit.PX);
     layoutPanel.setWidgetLeftRight(child, 0, Unit.PX, 0, Unit.PX);

     LayoutData data = new LayoutData(child, header, headerSize);
-    layoutData.add(data);
+    layoutData.add(beforeIndex, data);

     header.addStyleName(HEADER_STYLE);
     child.addStyleName(CONTENT_STYLE);
@@ -612,12 +648,15 @@
// If there's no visible widget, display the first one. The layout will
       // be updated onLoad().
       showWidget(0);
+    } else if (beforeIndex <= selectedIndex) {
+      // If we inserted an item before the selected index, increment it.
+      selectedIndex++;
     }

// If the widget is already attached, we must call animate() to update the
     // layout (if it's not yet attached, then onLoad() will do this).
     if (isAttached()) {
-      animate(ANIMATION_TIME);
+      animate(animationDuration);
     }
   }

=======================================
--- /trunk/user/test/com/google/gwt/user/client/ui/StackLayoutPanelTest.java Wed Feb 16 10:10:39 2011 +++ /trunk/user/test/com/google/gwt/user/client/ui/StackLayoutPanelTest.java Thu Feb 17 05:39:09 2011
@@ -204,14 +204,27 @@
     wc.add(new Label("First"));
     wc.add(new Label("Second"));

-    p.add(new Label("Content C"), wc, 1);
-    p.insert(new Label("Content B"), wb, 1, 0);
-    p.insert(new Label("Content A"), wa, 1, 0);
+    Label contentA = new Label("Content A");
+    Label contentB = new Label("Content B");
+    Label contentC = new Label("Content C");
+
+    p.add(contentC, wc, 1);
+    p.insert(contentB, wb, 1, 0);
+    p.showWidget(1);
+
+    // Insert before the visible widget.
+    p.insert(contentA, wa, 1, 0);
+
+    // Check that the visible widget index has been incremented.
+    assertEquals(2, p.getVisibleIndex());

     // Call these to ensure we don't throw an exception.
-    assertNotNull(p.getHeaderWidget(0));
-    assertNotNull(p.getHeaderWidget(1));
-    assertNotNull(p.getHeaderWidget(2));
+    assertEquals(wa, p.getHeaderWidget(0));
+    assertEquals(wb, p.getHeaderWidget(1));
+    assertEquals(wc, p.getHeaderWidget(2));
+    assertEquals(contentA, p.getWidget(0));
+    assertEquals(contentB, p.getWidget(1));
+    assertEquals(contentC, p.getWidget(2));
     assertEquals(3, p.getWidgetCount());
   }

@@ -243,6 +256,32 @@
     assertTrue(p.getWidget(0) == bar);
     assertTrue(p.getWidget(1) == baz);
   }
+
+  public void testRemoveBeforeSelectedWidget() {
+    StackLayoutPanel p = new StackLayoutPanel(Unit.EM);
+    p.add(new Label("Content 0"), "Header 0", 1);
+    p.add(new Label("Content 1"), "Header 0", 1);
+    p.add(new Label("Content 2"), "Header 2", 1);
+    p.showWidget(2);
+    assertEquals(2, p.getVisibleIndex());
+
+    // Remove a widget before the selected index.
+    p.remove(1);
+    assertEquals(1, p.getVisibleIndex());
+  }
+
+  public void testRemoveSelectedWidget() {
+    StackLayoutPanel p = new StackLayoutPanel(Unit.EM);
+    p.add(new Label("Content 0"), "Header 0", 1);
+    p.add(new Label("Content 1"), "Header 0", 1);
+    p.add(new Label("Content 2"), "Header 2", 1);
+    p.showWidget(1);
+    assertEquals(1, p.getVisibleIndex());
+
+    // Remove the selected widget.
+    p.remove(1);
+    assertEquals(0, p.getVisibleIndex());
+  }

   public void testSelectionEvents() {
     StackLayoutPanel p = new StackLayoutPanel(Unit.EM);

--
http://groups.google.com/group/Google-Web-Toolkit-Contributors

Reply via email to