Added the DecoratorPanel, which is a Widget that wraps another Widget in a 9-box that can be used to create rounded corners.

Issue: 883
Patch by: jlabanca, ecc
Review by: ecc



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1875 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/ui/DecoratorPanel.java b/user/src/com/google/gwt/user/client/ui/DecoratorPanel.java
new file mode 100644
index 0000000..5ed9464
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/DecoratorPanel.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * Licensed 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 com.google.gwt.user.client.ui;
+
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+
+/**
+ * <p>
+ * A {@link SimplePanel} that wraps its contents in stylized boxes, which can be
+ * used to add rounded corners to a {@link Widget}.
+ * </p>
+ * <p>
+ * Wrapping a {@link Widget} in a "9-box" allows users to specify images in each
+ * of the corners and along the four borders. This method allows the content
+ * within the {@link DecoratorPanel} to resize without disrupting the look of
+ * the border. In addition, rounded corners can generally be combined into a
+ * single image file, which reduces the number of downloaded files at startup.
+ * This class also simplifies the process of using AlphaImageLoaders to support
+ * 8-bit transparencies (anti-aliasing and shadows) in ie6, which does not
+ * support them normally.
+ * </p>
+ * 
+ * <h3>CSS Style Rules</h3>
+ * <ul class='css'>
+ * <li>.gwt-DecoratorPanel { the panel }</li>
+ * <li>.gwt-DecoratorPanel .top { the top row }</li>
+ * <li>.gwt-DecoratorPanel .topLeft { the top left cell }</li>
+ * <li>.gwt-DecoratorPanel .topLeftInner { the inner element of the cell }</li>
+ * <li>.gwt-DecoratorPanel .topCenter { the top center cell }</li>
+ * <li>.gwt-DecoratorPanel .topCenterInner { the inner element of the cell }</li>
+ * <li>.gwt-DecoratorPanel .topRight { the top right cell }</li>
+ * <li>.gwt-DecoratorPanel .topRightInner { the inner element of the cell }</li>
+ * <li>.gwt-DecoratorPanel .middle { the middle row }</li>
+ * <li>.gwt-DecoratorPanel .middleLeft { the middle left cell }</li>
+ * <li>.gwt-DecoratorPanel .middleLeftInner { the inner element of the cell }</li>
+ * <li>.gwt-DecoratorPanel .middleCenter { the middle center cell }</li>
+ * <li>.gwt-DecoratorPanel .middleCenterInner { the inner element of the cell }</li>
+ * <li>.gwt-DecoratorPanel .middleRight { the middle right cell }</li>
+ * <li>.gwt-DecoratorPanel .middleRightInner { the inner element of the cell }</li>
+ * <li>.gwt-DecoratorPanel .bottom { the bottom row }</li>
+ * <li>.gwt-DecoratorPanel .bottomLeft { the bottom left cell }</li>
+ * <li>.gwt-DecoratorPanel .bottomLeftInner { the inner element of the cell }</li>
+ * <li>.gwt-DecoratorPanel .bottomCenter { the bottom center cell }</li>
+ * <li>.gwt-DecoratorPanel .bottomCenterInner { the inner element of the cell }</li>
+ * <li>.gwt-DecoratorPanel .bottomRight { the bottom right cell }</li>
+ * <li>.gwt-DecoratorPanel .bottomRightInner { the inner element of the cell }</li>
+ * </ul>
+ */
+public class DecoratorPanel extends SimplePanel {
+  /**
+   * The default style name.
+   */
+  private static final String DEFAULT_STYLENAME = "gwt-DecoratorPanel";
+
+  /**
+   * The default styles applied to each row.
+   */
+  private static final String[] DEFAULT_ROW_STYLENAMES = {
+      "top", "middle", "bottom"};
+
+  /**
+   * Create a new row with a specific style name. The row will contain three
+   * cells (Left, Center, and Right), each prefixed with the specified style
+   * name.
+   * 
+   * This method allows Widgets to reuse the code on a DOM level, without
+   * creating a DecoratorPanel Widget.
+   * 
+   * @param styleName the style name
+   * @return the new row {@link Element}
+   */
+  static Element createTR(String styleName) {
+    Element trElem = DOM.createTR();
+    setStyleName(trElem, styleName);
+    DOM.appendChild(trElem, createTD(styleName + "Left"));
+    DOM.appendChild(trElem, createTD(styleName + "Center"));
+    DOM.appendChild(trElem, createTD(styleName + "Right"));
+    return trElem;
+  }
+
+  /**
+   * Create a new table cell with a specific style name.
+   * 
+   * @param styleName the style name
+   * @return the new cell {@link Element}
+   */
+  private static Element createTD(String styleName) {
+    Element tdElem = DOM.createTD();
+    Element inner = DOM.createDiv();
+    DOM.appendChild(tdElem, inner);
+    setStyleName(tdElem, styleName);
+    setStyleName(inner, styleName + "Inner");
+    return tdElem;
+  }
+
+  /**
+   * The container element at the center of the panel.
+   */
+  private Element containerElem;
+
+  /**
+   * The table body element.
+   */
+  private Element tbody;
+
+  /**
+   * Create a new {@link DecoratorPanel}.
+   */
+  public DecoratorPanel() {
+    this(DEFAULT_ROW_STYLENAMES, 1);
+  }
+
+  /**
+   * Creates a new panel using the specified style names to apply to each row.
+   * Each row will contain three cells (Left, Center, and Right). The Center
+   * cell in the containerIndex row will contain the {@link Widget}.
+   * 
+   * @param rowStyles an array of style names to apply to each row
+   * @param containerIndex the index of the container row
+   */
+  DecoratorPanel(String[] rowStyles, int containerIndex) {
+    super(DOM.createTable());
+
+    // Add a tbody
+    Element table = getElement();
+    tbody = DOM.createTBody();
+    DOM.appendChild(table, tbody);
+    DOM.setElementPropertyInt(table, "cellSpacing", 0);
+    DOM.setElementPropertyInt(table, "cellPadding", 0);
+
+    // Add each row
+    for (int i = 0; i < rowStyles.length; i++) {
+      Element row = createTR(rowStyles[i]);
+      DOM.appendChild(tbody, row);
+      if (i == containerIndex) {
+        containerElem = DOM.getFirstChild(DOM.getChild(row, 1));
+      }
+    }
+
+    // Set the overall style name
+    setStyleName(DEFAULT_STYLENAME);
+  }
+
+  @Override
+  protected Element getContainerElement() {
+    return containerElem;
+  }
+
+  /**
+   * Get a specific Element from the panel.
+   * 
+   * @param row the row index
+   * @param cell the cell index
+   * @return the Element at the given row and cell
+   */
+  Element getCellElement(int row, int cell) {
+    Element tr = DOM.getChild(tbody, row);
+    return DOM.getChild(tr, cell);
+  }
+}
diff --git a/user/test/com/google/gwt/user/UISuite.java b/user/test/com/google/gwt/user/UISuite.java
index a26f39c..16b09ab 100644
--- a/user/test/com/google/gwt/user/UISuite.java
+++ b/user/test/com/google/gwt/user/UISuite.java
@@ -19,6 +19,7 @@
 import com.google.gwt.user.client.ui.CheckBoxTest;
 import com.google.gwt.user.client.ui.CustomButtonTest;
 import com.google.gwt.user.client.ui.DOMTest;
+import com.google.gwt.user.client.ui.DecoratorPanelTest;
 import com.google.gwt.user.client.ui.DelegatingKeyboardListenerCollectionTest;
 import com.google.gwt.user.client.ui.DialogBoxTest;
 import com.google.gwt.user.client.ui.DisclosurePanelTest;
@@ -68,6 +69,7 @@
     suite.addTestSuite(AbsolutePanelTest.class);
     suite.addTestSuite(CheckBoxTest.class);
     suite.addTestSuite(CustomButtonTest.class);
+    suite.addTestSuite(DecoratorPanelTest.class);
     suite.addTestSuite(DelegatingKeyboardListenerCollectionTest.class);
     suite.addTestSuite(DialogBoxTest.class);
     suite.addTestSuite(DisclosurePanelTest.class);
diff --git a/user/test/com/google/gwt/user/client/ui/DecoratorPanelTest.java b/user/test/com/google/gwt/user/client/ui/DecoratorPanelTest.java
new file mode 100644
index 0000000..630106d
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/ui/DecoratorPanelTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * Licensed 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 com.google.gwt.user.client.ui;
+
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+
+/**
+ * Test for {@link DecoratorPanel}.
+ */
+public class DecoratorPanelTest extends GWTTestCase {
+  /**
+   * Assert that an element has the specified class name.
+   * 
+   * @param elem the DOM {@link Element}
+   * @param styleName the style name the element should have
+   */
+  private static void assertStyleName(Element elem, String styleName) {
+    assertEquals(styleName, DOM.getElementProperty(elem, "className"));
+  }
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.user.User";
+  }
+
+  /**
+   * Test addition and removal of widgets.
+   */
+  public void testAddRemoveWidget() {
+    DecoratorPanel panel = new DecoratorPanel();
+    Label contents = new Label("test");
+    panel.setWidget(contents);
+    assertEquals(contents, panel.getWidget());
+  }
+
+  /**
+   * Test the ability to define custom rows.
+   */
+  public void testCustomRows() {
+    String[] rowStyles = {"rowA", "rowB", "rowC", "rowD"};
+    DecoratorPanel panel = new DecoratorPanel(rowStyles, 2);
+
+    // Check the styles of each row
+    for (int i = 0; i < rowStyles.length; i++) {
+      String rowStyle = rowStyles[i];
+      assertStyleName(DOM.getParent(panel.getCellElement(i, 0)), rowStyle);
+      assertStyleName(panel.getCellElement(i, 0), rowStyle + "Left");
+      assertStyleName(panel.getCellElement(i, 1), rowStyle + "Center");
+      assertStyleName(panel.getCellElement(i, 2), rowStyle + "Right");
+    }
+
+    // Check the container element
+    assertEquals(DOM.getFirstChild(panel.getCellElement(2, 1)),
+        panel.getContainerElement());
+  }
+
+  /**
+   * Test the default styles.
+   */
+  public void testDefaultStyles() {
+    String[] rowStyles = {"top", "middle", "bottom"};
+    DecoratorPanel panel = new DecoratorPanel();
+
+    // Check the styles of each row
+    for (int i = 0; i < rowStyles.length; i++) {
+      String rowStyle = rowStyles[i];
+      assertStyleName(DOM.getParent(panel.getCellElement(i, 0)), rowStyle);
+      assertStyleName(panel.getCellElement(i, 0), rowStyle + "Left");
+      assertStyleName(panel.getCellElement(i, 1), rowStyle + "Center");
+      assertStyleName(panel.getCellElement(i, 2), rowStyle + "Right");
+    }
+
+    // Check the container element
+    assertEquals(DOM.getFirstChild(panel.getCellElement(1, 1)),
+        panel.getContainerElement());
+  }
+}