Reorganizes styles in bikeshed to use CssResource and ClientBundle.
Moves all styles in stock and cookbook samples to one place.
Removes the "hovering boxes" we stole from Wave for a slightly flatter look.

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


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7964 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java b/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java
index 756ccfd..6a1e047 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java
@@ -52,7 +52,8 @@
     }
   }
 
-  protected int curPage;
+  private static final int DEFAULT_SIZE = 10;
+
   private List<Column<T, ?, ?>> columns = new ArrayList<Column<T, ?, ?>>();
   private ArrayList<Boolean> dataSelected = new ArrayList<Boolean>();
   private ArrayList<T> dataValues = new ArrayList<T>();
@@ -60,8 +61,8 @@
   private List<Header<?>> footers = new ArrayList<Header<?>>();
   private List<Header<?>> headers = new ArrayList<Header<?>>();
   private TableRowElement hoveringRow;
-  private int numPages;
-  private int pageSize;
+  private int pageSize = -1;
+  private int pageStart = 0;
 
   /**
    * If null, each T will be used as its own key.
@@ -74,20 +75,28 @@
   private TableSectionElement tbody;
   private TableSectionElement tfoot;
   private TableSectionElement thead;
-  private int totalSize;
+  private int size = 0;
+
+  /**
+   * Constructs a table with a default page size of 10.
+   */
+  public PagingTableListView() {
+    this(DEFAULT_SIZE);
+  }
 
   /**
    * Constructs a table with the given page size.
-
+   *
    * @param pageSize the page size
    */
   public PagingTableListView(final int pageSize) {
-    this.pageSize = pageSize;
     setElement(table = Document.get().createTableElement());
+    table.setCellSpacing(0);
     thead = table.createTHead();
     table.appendChild(tbody = Document.get().createTBodyElement());
     tfoot = table.createTFoot();
-    createRows();
+
+    setPageSize(pageSize);
 
     // TODO: Total hack. It would almost definitely be preferable to sink only
     // those events actually needed by cells.
@@ -118,7 +127,8 @@
     createHeadersAndFooters(); // TODO: defer header recreation
     columns.add(col);
     createRows();
-    setPage(curPage); // TODO: better way to refresh?
+
+    refresh();
   }
 
   /**
@@ -128,9 +138,24 @@
     addColumn(col, new TextHeader(headerString), null);
   }
 
+  /**
+   * Returns true if it there is enough data to allow a given number of
+   * additional rows to be displayed.
+   */
+  public boolean canAddRows(int rows) {
+    return size - pageSize >= rows;
+  }
+
+  /**
+   * Returns true if the page size is sufficient to allow a given number of rows
+   * to be removed.
+   */
+  public boolean canRemoveRows(int rows) {
+    return pageSize > rows;
+  }
+
   // TODO: remove(Column)
 
-  // TODO - bounds check
   public T getDisplayedItem(int indexOnPage) {
     if (indexOnPage < 0 || indexOnPage >= getNumDisplayedItems()) {
       throw new IndexOutOfBoundsException("indexOnPage = " + indexOnPage);
@@ -143,32 +168,52 @@
   }
 
   public int getNumDisplayedItems() {
-    return Math.min(getPageSize(), totalSize - curPage * pageSize);
-  }
-
-  /**
-   * Get the current page.
-   *
-   * @return the current page
-   */
-  public int getPage() {
-    return curPage;
+    return Math.min(getPageSize(), size - pageStart);
   }
 
   public int getPageSize() {
     return pageSize;
   }
 
+  public int getPageStart() {
+    return pageStart;
+  }
+
   public ProvidesKey<T> getProvidesKey() {
     return providesKey;
   }
 
   public Range getRange() {
-    return new DefaultRange(curPage * pageSize, pageSize);
+    return new DefaultRange(pageStart, pageSize);
   }
 
+  public int getSize() {
+    return size;
+  }
+
+  /**
+   * Returns true if there is enough data such that a call to
+   * {@link #nextPage()} will succeed in moving the starting point of the table
+   * forward.
+   */
+  public boolean hasNextPage() {
+    return pageStart + pageSize < size;
+  }
+
+  /**
+   * Returns true if there is enough data such that a call to
+   * {@link #previousPage()} will succeed in moving the starting point of the
+   * table backward.
+   */
+  public boolean hasPreviousPage() {
+    return pageStart > 0 && size > 0;
+  }
+
+  /**
+   * Advance the starting row by {@link #getPageSize()} rows.
+   */
   public void nextPage() {
-    setPage(curPage + 1);
+    setPageStart(Math.min(getSize() - 1, getPageStart() + getPageSize()));
   }
 
   @Override
@@ -210,15 +255,20 @@
       T value = dataValues.get(row);
       Column<T, ?, ?> column = columns.get(col);
 
-      column.onBrowserEvent(cell, curPage * pageSize + row, value, event,
-          providesKey);
+      column.onBrowserEvent(cell, pageStart + row, value, event, providesKey);
     }
   }
 
+  /**
+   * Move the starting row back by {@link #getPageSize()} rows.
+   */
   public void previousPage() {
-    setPage(curPage - 1);
+    setPageStart(Math.max(0, getPageStart() - getPageSize()));
   }
 
+  /**
+   * Redraw the table, requesting data from the delegate.
+   */
   public void refresh() {
     if (delegate != null) {
       delegate.onRangeChanged(this);
@@ -226,6 +276,10 @@
     updateRowVisibility();
   }
 
+  /**
+   * Refresh those portions of the table that depend on the state of the
+   * {@link SelectionModel}.
+   */
   public void refreshSelection() {
     // Refresh headers
     Element th = thead.getFirstChild().getFirstChild().cast();
@@ -276,102 +330,7 @@
   }
 
   public void setData(int start, int length, List<T> values) {
-    render(start, length, values);
-  }
-
-  public void setDataSize(int size, boolean isExact) {
-    totalSize = size;
-    if (totalSize <= 0) {
-      numPages = 0;
-    } else {
-      numPages = 1 + (totalSize - 1) / pageSize;
-    }
-    setPage(curPage);
-  }
-
-  public void setDelegate(Delegate<T> delegate) {
-    this.delegate = delegate;
-  }
-
-  /**
-   * Set the current visible page.
-   *
-   * @param page the page index
-   */
-  public void setPage(int page) {
-    int newPage = Math.min(page, numPages - 1);
-    newPage = Math.max(0, newPage);
-
-    // Update the text showing the page number.
-    updatePageText(newPage);
-
-    // Early exit if we are already on the right page.
-    if (curPage != newPage) {
-      curPage = newPage;
-      if (delegate != null) {
-        delegate.onRangeChanged(this);
-      }
-    }
-
-    updateRowVisibility();
-  }
-
-  /**
-   * Set the number of rows per page.
-   *
-   * @param pageSize the page size
-   */
-  public void setPageSize(int pageSize) {
-    if (this.pageSize == pageSize) {
-      return;
-    }
-    this.pageSize = pageSize;
-    curPage = -1;
-    setPage(curPage);
-  }
-
-  /**
-   * Sets the {@link ProvidesKey} instance that will be used to generate keys
-   * for each record object as needed.
-   *
-   * @param providesKey an instance of {@link ProvidesKey<T>} used to generate
-   *          keys for record objects.
-   */
-  public void setProvidesKey(ProvidesKey<T> providesKey) {
-    this.providesKey = providesKey;
-  }
-
-  public void setSelectionModel(SelectionModel<T> selectionModel) {
-    if (selectionHandler != null) {
-      selectionHandler.removeHandler();
-      selectionHandler = null;
-    }
-    this.selectionModel = selectionModel;
-    if (selectionModel != null && isAttached()) {
-      selectionHandler = selectionModel.addSelectionChangeHandler(new TableSelectionHandler());
-    }
-  }
-
-  @Override
-  protected void onLoad() {
-    // Attach a selection handler.
-    if (selectionModel != null) {
-      selectionHandler = selectionModel.addSelectionChangeHandler(new TableSelectionHandler());
-    }
-  }
-
-  @Override
-  protected void onUnload() {
-    // Detach the selection handler.
-    if (selectionHandler != null) {
-      selectionHandler.removeHandler();
-      selectionHandler = null;
-    }
-  }
-
-  protected void render(int start, int length, List<T> values) {
     int numCols = columns.size();
-    int pageStart = curPage * pageSize;
 
     NodeList<TableRowElement> rows = tbody.getRows();
     for (int r = start; r < start + length; ++r) {
@@ -401,6 +360,98 @@
     }
   }
 
+  public void setDataSize(int size, boolean isExact) {
+    this.size = size;
+    refresh();
+  }
+
+  public void setDelegate(Delegate<T> delegate) {
+    this.delegate = delegate;
+  }
+
+  /**
+   * Set the number of rows per page and refresh the table.
+   *
+   * @param pageSize the page size
+   *
+   * @throw {@link IllegalArgumentException} if pageSize is negative or 0
+   */
+  public void setPageSize(int pageSize) {
+    if (pageSize <= 0) {
+      throw new IllegalArgumentException("pageSize = " + pageSize);
+    }
+    if (this.pageSize == pageSize) {
+      return;
+    }
+    this.pageSize = pageSize;
+
+    // If on last page and page size increases, move the page start upwards
+    if (pageStart + pageSize > size) {
+      pageStart = Math.max(0, size - pageSize);
+    }
+
+    // TODO - avoid requesting data if the page size has decreased
+    createRows();
+    refresh();
+  }
+
+  /**
+   * Set the starting index of the current visible page.  The actual page
+   * start will be clamped in the range [0, getSize() - 1].
+   *
+   * @param pageStart the index of the row that should appear at the start of
+   *          the page
+   */
+  public void setPageStart(int pageStart) {
+    this.pageStart = Math.max(Math.min(pageStart, size - 1), 0);
+    refresh();
+  }
+
+  /**
+   * Sets the {@link ProvidesKey} instance that will be used to generate keys
+   * for each record object as needed.
+   *
+   * @param providesKey an instance of {@link ProvidesKey<T>} used to generate
+   *          keys for record objects.
+   */
+  // TODO - when is this valid?  Do we rehash column view data if it changes?
+  public void setProvidesKey(ProvidesKey<T> providesKey) {
+    this.providesKey = providesKey;
+  }
+
+  /**
+   * Sets the selection model.
+   */
+  public void setSelectionModel(SelectionModel<T> selectionModel) {
+    if (selectionHandler != null) {
+      selectionHandler.removeHandler();
+      selectionHandler = null;
+    }
+    this.selectionModel = selectionModel;
+    if (selectionModel != null && isAttached()) {
+      selectionHandler = selectionModel.addSelectionChangeHandler(new TableSelectionHandler());
+    }
+
+    refreshSelection();
+  }
+
+  @Override
+  protected void onLoad() {
+    // Attach a selection handler.
+    if (selectionModel != null) {
+      selectionHandler = selectionModel.addSelectionChangeHandler(new TableSelectionHandler());
+    }
+  }
+
+  @Override
+  protected void onUnload() {
+    // Detach the selection handler.
+    if (selectionHandler != null) {
+      selectionHandler.removeHandler();
+      selectionHandler = null;
+    }
+  }
+
   private void createHeaders(List<Header<?>> headers,
       TableSectionElement section) {
     StringBuilder sb = new StringBuilder();
@@ -468,17 +519,8 @@
     return null;
   }
 
-  /**
-   * Update the text that shows the current page.
-   *
-   * @param page the current page
-   */
-  private void updatePageText(int page) {
-    // TODO: Update external paging widget.
-  }
-
   private void updateRowVisibility() {
-    int visible = Math.min(pageSize, totalSize - curPage * pageSize);
+    int visible = Math.min(pageSize, size - pageStart);
 
     for (int r = 0; r < pageSize; ++r) {
       Style rowStyle = tbody.getRows().getItem(r).getStyle();
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/Cookbook.gwt.xml b/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/Cookbook.gwt.xml
index 46f0103..4220ca6 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/Cookbook.gwt.xml
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/Cookbook.gwt.xml
@@ -1,5 +1,6 @@
 <module rename-to='cookbook'>
   <inherits name='com.google.gwt.user.User'/>
+  <inherits name='com.google.gwt.sample.bikeshed.style.Style'/>
   <inherits name='com.google.gwt.bikeshed.list.List'/>
   <inherits name='com.google.gwt.bikeshed.tree.Tree'/>
 
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/Cookbook.java b/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/Cookbook.java
index 61fc940..8d12fd1 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/Cookbook.java
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/Cookbook.java
@@ -28,6 +28,7 @@
 import com.google.gwt.uibinder.client.UiFactory;
 import com.google.gwt.uibinder.client.UiField;
 import com.google.gwt.user.client.ui.DockLayoutPanel;
+import com.google.gwt.user.client.ui.LayoutPanel;
 import com.google.gwt.user.client.ui.RootLayoutPanel;
 import com.google.gwt.user.client.ui.Widget;
 
@@ -86,6 +87,7 @@
 
   @UiField DockLayoutPanel dock;
   @UiField StandardTreeView recipeTree;
+  @UiField LayoutPanel container;
 
   private RecipeTreeModel recipeTreeModel = new RecipeTreeModel();
   private SingleSelectionModel<Object> treeSelection;
@@ -137,9 +139,9 @@
 
   private void showRecipe(Recipe recipe) {
     if (curRecipe != null) {
-      dock.remove(curRecipe.getWidget());
+      container.remove(curRecipe.getWidget());
     }
-    dock.add(recipe.getWidget());
+    container.add(recipe.getWidget());
     curRecipe = recipe;
   }
 }
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/Cookbook.ui.xml b/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/Cookbook.ui.xml
index 0ca6e08..932e99d 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/Cookbook.ui.xml
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/Cookbook.ui.xml
@@ -3,14 +3,18 @@
 	xmlns:l='urn:import:com.google.gwt.bikeshed.list.client'
 	xmlns:t='urn:import:com.google.gwt.bikeshed.tree.client'>
 
-	<ui:style field='common' src='common.css' />
+  <ui:with field='styles'
+    type='com.google.gwt.sample.bikeshed.style.client.Styles'/>
 
 	<g:DockLayoutPanel unit='EM' ui:field='dock'>
-		<g:north size="2">
-			<g:HTML>Table Sample</g:HTML>
+		<g:north size="3.5">
+			<g:HTML styleName='{styles.common.header}'>Google Web Toolkit Cookbook</g:HTML>
 		</g:north>
 		<g:west size="16">
-			<t:StandardTreeView ui:field='recipeTree'/>
+			<t:StandardTreeView styleName='{styles.common.box}' ui:field='recipeTree'/>
 		</g:west>
+		<g:center>
+		  <g:LayoutPanel styleName='{styles.common.box}' ui:field='container'/>
+		</g:center>
 	</g:DockLayoutPanel>
 </ui:UiBinder>
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/MailRecipe.java b/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/MailRecipe.java
index e0643f9..13b8bd4 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/MailRecipe.java
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/MailRecipe.java
@@ -34,6 +34,7 @@
 import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.event.dom.client.KeyUpEvent;
 import com.google.gwt.event.dom.client.KeyUpHandler;
+import com.google.gwt.sample.bikeshed.style.client.Styles;
 import com.google.gwt.user.client.Window;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.FlowPanel;
@@ -341,6 +342,7 @@
     sortMessages(idComparator, true);
 
     table = new PagingTableListView<Message>(10);
+    table.setStyleName(Styles.common().table());
     table.setSelectionModel(selectionModel);
     adapter.addView(table);
 
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/Recipe.java b/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/Recipe.java
index 4f450da..acb5b73 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/Recipe.java
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/Recipe.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.sample.bikeshed.cookbook.client;
 
+import com.google.gwt.dom.client.Style.Unit;
 import com.google.gwt.user.client.ui.Widget;
 
 /**
@@ -35,7 +36,12 @@
   public final Widget getWidget() {
     if (widget == null) {
       widget = createWidget();
+
+      // Minor style hack: we need to give a little margin to all recipes,
+      // so we just do it here instead of on all the individual recipes.
+      widget.getElement().getStyle().setMargin(0.5, Unit.EM);
     }
+
     return widget;
   }
 
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/ValidationRecipe.java b/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/ValidationRecipe.java
index a1dda9a..cdfeada 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/ValidationRecipe.java
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/ValidationRecipe.java
@@ -116,7 +116,12 @@
           public void run() {
             String pendingValue = viewData.getValue();
 
-            int zip = Integer.parseInt(pendingValue);
+            int zip;
+            try {
+              zip = Integer.parseInt(pendingValue);
+            } catch (NumberFormatException e) {
+              zip = -1;
+            }
             boolean zipInvalid = ValidationRecipe.zipInvalid(zip);
 
             final Address newValue = new Address(object);
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/common.css b/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/common.css
deleted file mode 100644
index 93aa1c2..0000000
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/cookbook/client/common.css
+++ /dev/null
@@ -1,49 +0,0 @@
-.bg {
-  border: 8px solid white;
-  border-right: 12px solid white;
-  border-bottom: 14px solid white;
-  -webkit-border-image: url(border.png) 8 12 14 8 round round;
-  -moz-border-image: url(border.png) 8 12 14 8 round round;
-}
-
-.header {
-  font-weight: light;
-  font-size: 12pt;
-  text-align: center;
-  margin-bottom: 0.5em;
-}
-
-.header-left {
-  font-weight: light;
-  font-size: 12pt;
-  margin-bottom: 0.5em;
-}
-
-.header-main {
-  font-weight: light;
-  font-size: 18pt;
-}
-
-.table {
-  width: 95%;
-  font-size: 8pt;
-  border-spacing: 0px 0px;
-  border-collapse: collapse;
-}
-
-.table th {
-  border-bottom: 1px solid #ccc;
-}
-
-.table td {
-  border-left: 1px solid #ccc;
-  border-right: 1px solid #ccc;
-}
-
-.table tfoot th {
-  border-width: 0px;
-}
-
-.padded {
-  padding: 5px;
-}
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/StocksCommon.gwt.xml b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/StocksCommon.gwt.xml
index c69470f..394d1d2 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/StocksCommon.gwt.xml
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/StocksCommon.gwt.xml
@@ -2,6 +2,7 @@
 <!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 0.0.999//EN" "http://google-web-toolkit.googlecode.com/svn/tags/0.0.999/distro-source/core/src/gwt-module.dtd">
 <module>
   <inherits name='com.google.gwt.user.User'/>
+  <inherits name='com.google.gwt.sample.bikeshed.style.Style'/>
   <inherits name='com.google.gwt.regexp.RegExp'/>
   <inherits name='com.google.gwt.bikeshed.list.List'/>
   <inherits name='com.google.gwt.bikeshed.tree.Tree'/>
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/FavoritesWidget.ui.xml b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/FavoritesWidget.ui.xml
index 9381b4d..06f00a5 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/FavoritesWidget.ui.xml
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/FavoritesWidget.ui.xml
@@ -5,16 +5,17 @@
   xmlns:t='urn:import:com.google.gwt.bikeshed.tree.client'
   xmlns:s='urn:import:com.google.gwt.sample.bikeshed.stocks.client'>
 
-  <ui:style field='common' src='common.css'/>
+  <ui:with field='styles'
+    type='com.google.gwt.sample.bikeshed.style.client.Styles'/>
 
   <g:DockLayoutPanel unit='EM'>
     <g:north size='2'>
-      <g:Label styleName='{common.header-left}'>Portfolio / Favorites</g:Label>
+      <g:Label styleName='{styles.common.headerLeft}'>Portfolio / Favorites</g:Label>
     </g:north>
 
     <g:center>
       <g:ScrollPanel>
-        <l:PagingTableListView ui:field='listView' styleName='{common.table}'/>
+        <l:PagingTableListView ui:field='listView' styleName='{styles.common.table}'/>
       </g:ScrollPanel>
     </g:center>
   </g:DockLayoutPanel>
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/PlayerScoresWidget.ui.xml b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/PlayerScoresWidget.ui.xml
index cda1e9a..33b7a22 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/PlayerScoresWidget.ui.xml
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/PlayerScoresWidget.ui.xml
@@ -5,15 +5,16 @@
   xmlns:t='urn:import:com.google.gwt.bikeshed.tree.client'
   xmlns:s='urn:import:com.google.gwt.sample.bikeshed.stocks.client'>
 
-  <ui:style field='common' src='common.css'/>
+  <ui:with field='styles'
+    type='com.google.gwt.sample.bikeshed.style.client.Styles'/>
 
   <g:DockLayoutPanel unit='EM'>
     <g:north size='2'>
-      <g:Label styleName='{common.header}'>Player Status</g:Label>
+      <g:Label styleName='{styles.common.header}'>Player Status</g:Label>
     </g:north>
 
     <g:center>
-      <g:ScrollPanel styleName="{common.padded}">
+      <g:ScrollPanel styleName="{styles.common.padded}">
         <l:SimpleCellList ui:field='listView'/>
       </g:ScrollPanel>
     </g:center>
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/StockQueryWidget.ui.xml b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/StockQueryWidget.ui.xml
index 5abfbae..235f154 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/StockQueryWidget.ui.xml
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/StockQueryWidget.ui.xml
@@ -5,19 +5,20 @@
   xmlns:t='urn:import:com.google.gwt.bikeshed.tree.client'
   xmlns:s='urn:import:com.google.gwt.sample.bikeshed.stocks.client'>
 
-  <ui:style field='common' src='common.css'/>
+  <ui:with field='styles'
+    type='com.google.gwt.sample.bikeshed.style.client.Styles'/>
 
   <g:DockLayoutPanel unit='EM'>
     <g:north size='2'>
       <g:HTMLPanel>
-        <span class='{common.header-left}'>Enter query:</span>
+        <span class='{styles.common.headerLeft}'>Enter query:</span>
         <g:TextBox ui:field='queryField'/>
       </g:HTMLPanel>
     </g:north>
 
     <g:center>
       <g:ScrollPanel>
-        <l:PagingTableListView ui:field='listView' styleName='{common.table}'/>
+        <l:PagingTableListView ui:field='listView' styleName='{styles.common.table}'/>
       </g:ScrollPanel>
     </g:center>
   </g:DockLayoutPanel>
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/StocksDesktop.ui.xml b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/StocksDesktop.ui.xml
index 6f129bb..9e22821 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/StocksDesktop.ui.xml
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/StocksDesktop.ui.xml
@@ -5,18 +5,19 @@
   xmlns:t='urn:import:com.google.gwt.bikeshed.tree.client'
   xmlns:s='urn:import:com.google.gwt.sample.bikeshed.stocks.client'>
 
-  <ui:style field='common' src='common.css'/>
+  <ui:with field='styles'
+    type='com.google.gwt.sample.bikeshed.style.client.Styles'/>
 
   <g:DockLayoutPanel unit='EM'>
     <g:north size='4'>
-      <g:HTML styleName='{common.bg} {common.header-main}'>Day Trader</g:HTML>
+      <g:HTML styleName='{styles.common.headerMain}'>Day Trader</g:HTML>
     </g:north>
 
     <g:west size='16'>
       <g:DockLayoutPanel unit='EM'>
         <g:north size='12'>
-          <g:HTMLPanel styleName='{common.bg}'>
-            <div class='{common.header}'>Your Stats</div>
+          <g:HTMLPanel styleName='{styles.common.box}'>
+            <div class='{styles.common.header}'>Your Stats</div>
             <table>
               <tr><td>Available cash:</td><td><g:InlineLabel ui:field='cashLabel'/></td></tr>
               <tr><td>Net worth:</td><td><g:InlineLabel ui:field='netWorthLabel'/></td></tr>
@@ -25,13 +26,13 @@
         </g:north>
 
         <g:center>
-          <s:PlayerScoresWidget styleName='{common.bg}' ui:field='playerScoresWidget'/>
+          <s:PlayerScoresWidget styleName='{styles.common.box}' ui:field='playerScoresWidget'/>
         </g:center>
       </g:DockLayoutPanel>
     </g:west>
 
     <g:north size="18">
-      <g:LayoutPanel styleName='{common.bg}'>
+      <g:LayoutPanel styleName='{styles.common.box}'>
         <g:layer>
           <t:SideBySideTreeView ui:field='transactionTree'/>
         </g:layer>
@@ -41,11 +42,11 @@
     <g:center>
       <g:DockLayoutPanel unit='PCT'>
         <g:west size="50">
-          <s:StockQueryWidget styleName='{common.bg}' ui:field='queryWidget'/>
+          <s:StockQueryWidget styleName='{styles.common.box}' ui:field='queryWidget'/>
         </g:west>
 
         <g:center>
-          <s:FavoritesWidget styleName='{common.bg}' ui:field='favoritesWidget'/>
+          <s:FavoritesWidget styleName='{styles.common.box}' ui:field='favoritesWidget'/>
         </g:center>
       </g:DockLayoutPanel>
     </g:center>
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/StocksMobile.ui.xml b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/StocksMobile.ui.xml
index 1c7ee0c..901ce55 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/StocksMobile.ui.xml
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/StocksMobile.ui.xml
@@ -5,10 +5,11 @@
   xmlns:t='urn:import:com.google.gwt.bikeshed.tree.client'
   xmlns:s='urn:import:com.google.gwt.sample.bikeshed.stocks.client'>
 
-  <ui:style field='common' src='common.css'/>
+  <ui:with field='styles'
+    type='com.google.gwt.sample.bikeshed.style.client.Styles'/>
 
   <g:HTMLPanel>
-    <div styleName='{common.header-left}'>Portfolio / Favorites</div>
-    <l:PagingTableListView ui:field='listView' styleName='{common.table}'/>
+    <div styleName='{styles.common.headerLeft}'>Portfolio / Favorites</div>
+    <l:PagingTableListView ui:field='listView' styleName='{styles.common.table}'/>
   </g:HTMLPanel>
 </ui:UiBinder>
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/common.css b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/common.css
deleted file mode 100644
index 717e19e..0000000
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/common.css
+++ /dev/null
@@ -1,51 +0,0 @@
-/* text-overflow: ellipsis; */
-
-.bg {
-  border: 8px solid white;
-  border-right: 12px solid white;
-  border-bottom: 14px solid white;
-  -webkit-border-image: url(border.png) 8 12 14 8 round round;
-  -moz-border-image: url(border.png) 8 12 14 8 round round;
-}
-
-.header {
-  font-weight: light;
-  font-size: 12pt;
-  text-align: center;
-  margin-bottom: 0.5em;
-}
-
-.header-left {
-  font-weight: light;
-  font-size: 12pt;
-  margin-bottom: 0.5em;
-}
-
-.header-main {
-  font-weight: light;
-  font-size: 18pt;
-}
-
-.table {
-  width: 95%;
-  font-size: 8pt;
-  border-spacing: 0px 0px;
-  border-collapse: collapse;
-}
-
-.table th {
-  border-bottom: 1px solid #ccc;
-}
-
-.table td {
-  border-left: 1px solid #ccc;
-  border-right: 1px solid #ccc;
-}
-
-.table tfoot th {
-  border-width: 0px;
-}
-
-.padded {
-  padding: 5px;
-}
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/style/Style.gwt.xml b/bikeshed/src/com/google/gwt/sample/bikeshed/style/Style.gwt.xml
new file mode 100644
index 0000000..05ad12f
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/style/Style.gwt.xml
@@ -0,0 +1,4 @@
+<module>
+  <inherits name="com.google.gwt.resources.Resources"/>
+  <source path='client'/>
+</module>
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/style/client/Styles.java b/bikeshed/src/com/google/gwt/sample/bikeshed/style/client/Styles.java
new file mode 100644
index 0000000..0e823a0
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/style/client/Styles.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2010 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.sample.bikeshed.style.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.resources.client.ClientBundle;
+import com.google.gwt.resources.client.CssResource;
+import com.google.gwt.resources.client.CssResource.NotStrict;
+
+public class Styles {
+
+  public interface Common extends CssResource {
+    String box();
+    String header();
+    String headerLeft();
+    String headerMain();
+    String padded();
+    String table();
+  }
+
+  public interface Resources extends ClientBundle {
+    @NotStrict
+    @Source("common.css")
+    Common common();
+  }
+
+  private static Resources resources;
+
+  static {
+    resources = GWT.create(Resources.class);
+    resources.common().ensureInjected();
+  }
+
+  public static Common common() {
+    return resources.common();
+  }
+}
diff --git a/bikeshed/war/Cookbook.css b/bikeshed/src/com/google/gwt/sample/bikeshed/style/client/common.css
similarity index 68%
rename from bikeshed/war/Cookbook.css
rename to bikeshed/src/com/google/gwt/sample/bikeshed/style/client/common.css
index de7f1b6..5551e36 100644
--- a/bikeshed/war/Cookbook.css
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/style/client/common.css
@@ -1,119 +1,99 @@
+/* global */
 body {
-  background: url(bg.png) repeat-x;
-  background-color: rgb(216, 220, 224);
+  background-color: #eee;
   color: black;
   margin: 8px;
   margin-top: 3px;
 }
 
 body, table {
-  font-family: Helvetica Neue Light, Helvetica, Arial, sans-serif; */
+  font-family: Arial, sans-serif;
   font-weight: light;
-  font-size: small;
+  font-size: 12px;
 }
 
-tr.selected {
+/* odds and ends */
+.box {
+  border: 1px solid #ccc;
+  background-color: white;
+  margin: 0.25em;
+  padding: 0.5em;
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+}
+
+.padded {
+  padding: 5px;
+}
+
+.header {
+  font-weight: bold;
+  font-size: 12pt;
+  margin: 0.5em;
+}
+
+.headerLeft {
+  font-weight: light;
+  font-size: 12pt;
+  margin-bottom: 0.5em;
+}
+
+.headerMain {
+  font-weight: light;
+  font-size: 18pt;
+}
+
+/* tables */
+.table {
+  width: 100%;
+  font-size: 8pt;
+  border-spacing: 0px 0px;
+  border-collapse: collapse;
+}
+
+.table th {
+  border-bottom: 1px solid #ccc;
+}
+
+.table td {
+  border-bottom: 1px solid #ccc;
+}
+
+.table tfoot th {
+  border-width: 0px;
+}
+
+.table tr.hover {
+  background-color: lightblue;
+}
+
+.table tr.selected {
   background-color: rgb(56, 117, 215);
 }
 
-tr.evenRow {
-  background-color: rgb(255, 255, 255);
+/* ss tree */
+.gwt-sstree {
 }
 
-tr.oddRow {
-  background-color: rgb(220, 220, 220);
-}
-
-tr.hover {
-  background-color: rgb(255, 0, 0);
-}
-
-div.gwt-sstree-column {
+.gwt-sstree-column {
   overflow-y: scroll;
   overflow-x: auto;
   position: relative;
 }
 
-div.gwt-sstree {
-}
-
-div.gwt-sstree-selectedItem {
+.gwt-sstree-selectedItem {
   background-color: rgb(56, 117, 215);
 }
 
-div.gwt-sstree-evenRow {
+.gwt-sstree-evenRow {
   background-color: rgb(255, 255, 255);
 }
 
-div.gwt-sstree-oddRow {
+.gwt-sstree-oddRow {
   background-color: rgb(220, 220, 220);
 }
 
-/** Example rules used by the template application (remove for your app) */
-h1 {
-  font-size: 2em;
-  font-weight: bold;
-  color: #777777;
-  margin: 40px 0px 70px;
-  text-align: center;
-}
-
-.sendButton {
-  display: block;
-  font-size: 16pt;
-}
-
-/** Most GWT widgets already have a style name defined */
-.gwt-DialogBox {
-  width: 400px;
-}
-
-.dialogVPanel {
-  margin: 5px;
-}
-
-.serverResponseLabelError {
-  color: red;
-}
-
-.playerScoreBox {
-  border: 1px solid #777;
-  padding:3px;
-  background: #eee;
-  margin-bottom: 5px;
-}
-
-/** Set ids using widget.getElement().setId("idOfElement") */
-#closeButton {
-  margin: 15px 6px 6px;
-}
-
-.gwt-DialogBox {
-  border: 8px solid white;
-  border-right: 11px solid white;
-  border-bottom: 11px solid white;
-  -webkit-border-image: url(blueborder.png) 8 11 11 8 round round;
-  -moz-border-image: url(blueborder.png) 8 11 11 8 round round;
-}
-
-.gwt-DialogBox .Caption {
-  font-weight: light;
-  font-size: 12pt;
-  text-align: center;
-  margin-bottom: 0.5em;
-  border-bottom: 1px solid #ccc;
-}
-
-.gwt-SplitLayoutPanel-HDragger {
-  background: transparent url(hsplitter-grip.png) center center no-repeat;
-}
-
-.gwt-SplitLayoutPanel-VDragger {
-  background: transparent url(vsplitter-grip.png) center center no-repeat;
-}
-
-/* Date picker */
-
+/* date picker */
 .gwt-DatePicker {
   border: 1px solid #A2BBDD;
   cursor: default;
@@ -189,3 +169,29 @@
   cursor: pointer;
   padding: 0px 4px;
 }
+
+/* dialog box */
+.gwt-DialogBox {
+  border: 8px solid white;
+  border-right: 11px solid white;
+  border-bottom: 11px solid white;
+  -webkit-border-image: url(blueborder.png) 8 11 11 8 round round;
+  -moz-border-image: url(blueborder.png) 8 11 11 8 round round;
+}
+
+.gwt-DialogBox .Caption {
+  font-weight: light;
+  font-size: 12pt;
+  text-align: center;
+  margin-bottom: 0.5em;
+  border-bottom: 1px solid #ccc;
+}
+
+/* splitters */
+.gwt-SplitLayoutPanel-HDragger {
+  background: transparent url(hsplitter-grip.png) center center no-repeat;
+}
+
+.gwt-SplitLayoutPanel-VDragger {
+  background: transparent url(vsplitter-grip.png) center center no-repeat;
+}
diff --git a/bikeshed/war/Cookbook.html b/bikeshed/war/Cookbook.html
index e5a4ab9..3afcbf9 100644
--- a/bikeshed/war/Cookbook.html
+++ b/bikeshed/war/Cookbook.html
@@ -2,7 +2,6 @@
 <html>
   <head>
     <meta http-equiv="content-type" content="text/html; charset=UTF-8">
-    <link type="text/css" rel="stylesheet" href="Cookbook.css">
     <title>Cookbook</title>
     <script type="text/javascript" language="javascript" src="cookbook/cookbook.nocache.js"></script>
   </head>
diff --git a/bikeshed/war/Stocks.css b/bikeshed/war/Stocks.css
deleted file mode 100644
index c26862c..0000000
--- a/bikeshed/war/Stocks.css
+++ /dev/null
@@ -1,97 +0,0 @@
-body {
-  background: url(bg.png) repeat-x;
-  background-color: rgb(216, 220, 224);
-  color: black;
-  margin: 8px;
-  margin-top: 3px;
-}
-
-body, table {
-  font-family: Helvetica Neue Light, Helvetica, Arial, sans-serif; */
-  font-weight: light;
-  font-size: small;
-}
-
-div.gwt-sstree-column {
-  overflow-y: scroll;
-  overflow-x: auto;
-  position: relative;
-}
-
-div.gwt-sstree {
-}
-
-div.gwt-sstree-selectedItem {
-  background-color: rgb(56, 117, 215);
-}
-
-div.gwt-sstree-evenRow {
-  background-color: rgb(255, 255, 255);
-}
-
-div.gwt-sstree-oddRow {
-  background-color: rgb(220, 220, 220);
-}
-
-/** Example rules used by the template application (remove for your app) */
-h1 {
-  font-size: 2em;
-  font-weight: bold;
-  color: #777777;
-  margin: 40px 0px 70px;
-  text-align: center;
-}
-
-.sendButton {
-  display: block;
-  font-size: 16pt;
-}
-
-/** Most GWT widgets already have a style name defined */
-.gwt-DialogBox {
-  width: 400px;
-}
-
-.dialogVPanel {
-  margin: 5px;
-}
-
-.serverResponseLabelError {
-  color: red;
-}
-
-.playerScoreBox {
-  border: 1px solid #777;
-  padding:3px;
-  background: #eee;
-  margin-bottom: 5px;
-}
-
-/** Set ids using widget.getElement().setId("idOfElement") */
-#closeButton {
-  margin: 15px 6px 6px;
-}
-
-.gwt-DialogBox {
-  border: 8px solid white;
-  border-right: 11px solid white;
-  border-bottom: 11px solid white;
-  -webkit-border-image: url(blueborder.png) 8 11 11 8 round round;
-  -moz-border-image: url(blueborder.png) 8 11 11 8 round round;
-}
-
-.gwt-DialogBox .Caption {
-  font-weight: light;
-  font-size: 12pt;
-  text-align: center;
-  margin-bottom: 0.5em;
-  border-bottom: 1px solid #ccc;
-}
-
-.gwt-SplitLayoutPanel-HDragger {
-  background: transparent url(hsplitter-grip.png) center center no-repeat;
-}
-
-.gwt-SplitLayoutPanel-VDragger {
-  background: transparent url(vsplitter-grip.png) center center no-repeat;
-}
diff --git a/bikeshed/war/Stocks.html b/bikeshed/war/Stocks.html
index 85c4b5d..0c01bb1 100644
--- a/bikeshed/war/Stocks.html
+++ b/bikeshed/war/Stocks.html
@@ -2,7 +2,6 @@
 <html>
   <head>
     <meta http-equiv="content-type" content="text/html; charset=UTF-8">
-    <link type="text/css" rel="stylesheet" href="Stocks.css">
     <title>Stock Game Sample</title>
     <script type="text/javascript" language="javascript" src="stocksdesktop/stocksdesktop.nocache.js"></script>
   </head>
diff --git a/bikeshed/war/StocksMobile.html b/bikeshed/war/StocksMobile.html
index 442ed18..c9173d7 100644
--- a/bikeshed/war/StocksMobile.html
+++ b/bikeshed/war/StocksMobile.html
@@ -2,7 +2,6 @@
 <html>
   <head>
     <meta http-equiv="content-type" content="text/html; charset=UTF-8">
-    <link type="text/css" rel="stylesheet" href="Stocks.css">
     <title>Stock Game Sample</title>
     <script type="text/javascript" language="javascript" src="stocksmobile/stocksmobile.nocache.js"></script>
   </head>