Adds basic support for headers and footers in the table view widget. In its
current form, it removes the next/prev-page buttons; I'll re-add those in a
separate pass.
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7696 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/bikeshed/src/com/google/gwt/bikeshed/cells/client/CheckboxCell.java b/bikeshed/src/com/google/gwt/bikeshed/cells/client/CheckboxCell.java
index fe98524..7920642 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/cells/client/CheckboxCell.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/cells/client/CheckboxCell.java
@@ -40,7 +40,7 @@
@Override
public void render(Boolean data, StringBuilder sb) {
sb.append("<input type=\"checkbox\"");
- if (data == true) {
+ if ((data != null) && (data == true)) {
sb.append(" checked");
}
sb.append("/>");
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 90a9164..271cd97 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java
@@ -15,8 +15,6 @@
*/
package com.google.gwt.bikeshed.list.client;
-import com.google.gwt.bikeshed.cells.client.ButtonCell;
-import com.google.gwt.bikeshed.cells.client.ValueUpdater;
import com.google.gwt.bikeshed.list.shared.ListEvent;
import com.google.gwt.bikeshed.list.shared.ListHandler;
import com.google.gwt.bikeshed.list.shared.ListModel;
@@ -31,6 +29,7 @@
import com.google.gwt.dom.client.TableCellElement;
import com.google.gwt.dom.client.TableElement;
import com.google.gwt.dom.client.TableRowElement;
+import com.google.gwt.dom.client.TableSectionElement;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Widget;
@@ -52,16 +51,27 @@
private int totalSize;
private List<Column<T, ?>> columns = new ArrayList<Column<T, ?>>();
private ArrayList<T> data = new ArrayList<T>();
- private ButtonCell prevButton = new ButtonCell();
- private ButtonCell nextButton = new ButtonCell();
+
+ private List<Header<?>> headers = new ArrayList<Header<?>>();
+ private List<Header<?>> footers = new ArrayList<Header<?>>();
+
+ private TableElement table;
+ private TableSectionElement thead;
+ private TableSectionElement tfoot;
+ private TableSectionElement tbody;
public PagingTableListView(ListModel<T> listModel, final int pageSize) {
this.pageSize = pageSize;
- setElement(Document.get().createTableElement());
+ setElement(table = Document.get().createTableElement());
+ thead = table.createTHead();
+ table.appendChild(tbody = Document.get().createTBodyElement());
+ tfoot = table.createTFoot();
createRows();
- // TODO: total hack.
- sinkEvents(Event.MOUSEEVENTS | Event.KEYEVENTS);
+ // TODO: Total hack. It would almost definitely be preferable to sink only
+ // those events actually needed by cells.
+ sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS | Event.KEYEVENTS
+ | Event.ONCHANGE);
// Attach to the list model.
listReg = listModel.addListHandler(new ListHandler<T>() {
@@ -82,8 +92,19 @@
listReg.setRangeOfInterest(0, pageSize);
}
- // TODO: remove(Column)
public void addColumn(Column<T, ?> col) {
+ addColumn(col, null, null);
+ }
+
+ public void addColumn(Column<T, ?> col, Header<?> header) {
+ addColumn(col, header, null);
+ }
+
+ // TODO: remove(Column)
+ public void addColumn(Column<T, ?> col, Header<?> header, Header<?> footer) {
+ headers.add(header);
+ footers.add(footer);
+ createHeadersAndFooters(); // TODO: defer header recreation
columns.add(col);
createRows();
setPage(curPage); // TODO: better way to refresh?
@@ -106,44 +127,29 @@
public void onBrowserEvent(Event event) {
EventTarget target = event.getEventTarget();
Node node = Node.as(target);
- while (node != null) {
- if (Element.is(node)) {
- Element elem = Element.as(node);
+ TableCellElement cell = findNearestParentCell(node);
+ if (cell == null) {
+ return;
+ }
- // TODO: We need is() implementations in all Element subclasses.
- String tagName = elem.getTagName();
- if ("td".equalsIgnoreCase(tagName)) {
- TableCellElement td = TableCellElement.as(elem);
- TableRowElement tr = TableRowElement.as(td.getParentElement());
-
- // TODO: row/col assertions.
- int row = tr.getRowIndex(), col = td.getCellIndex();
- if (row < pageSize) {
- T value = data.get(row);
- Column<T, ?> column = columns.get(col);
- column.onBrowserEvent(elem, value, event);
- } else if (row == pageSize) {
- if (col == 0) {
- prevButton.onBrowserEvent(elem, null, event,
- new ValueUpdater<String>() {
- public void update(String value) {
- previousPage();
- }
- });
- } else if (col == 2) {
- nextButton.onBrowserEvent(elem, null, event,
- new ValueUpdater<String>() {
- public void update(String value) {
- nextPage();
- }
- });
- }
- }
- break;
- }
+ TableRowElement tr = TableRowElement.as(cell.getParentElement());
+ TableSectionElement section = TableSectionElement.as(tr.getParentElement());
+ int col = cell.getCellIndex();
+ if (section == thead) {
+ Header<?> header = headers.get(col);
+ if (header != null) {
+ header.onBrowserEvent(cell, event);
}
-
- node = node.getParentNode();
+ } else if (section == tfoot) {
+ Header<?> footer = footers.get(col);
+ if (footer != null) {
+ footer.onBrowserEvent(cell, event);
+ }
+ } else if (section == tbody) {
+ int row = tr.getSectionRowIndex();
+ T value = data.get(row);
+ Column<T, ?> column = columns.get(col);
+ column.onBrowserEvent(cell, value, event);
}
}
@@ -187,11 +193,10 @@
}
protected void render(int start, int length, List<T> values) {
- TableElement table = getElement().cast();
int numCols = columns.size();
int pageStart = curPage * pageSize;
- NodeList<TableRowElement> rows = table.getRows();
+ NodeList<TableRowElement> rows = tbody.getRows();
for (int r = start; r < start + length; ++r) {
TableRowElement row = rows.getItem(r - pageStart);
T q = values.get(r - start);
@@ -203,29 +208,47 @@
columns.get(c).render(q, sb);
cell.setInnerHTML(sb.toString());
- // TODO: really total hack!
+ // TODO: Really total hack! There's gotta be a better way...
Element child = cell.getFirstChildElement();
if (child != null) {
- Event.sinkEvents(child, Event.ONCHANGE | Event.ONFOCUS | Event.ONBLUR);
+ Event.sinkEvents(child, Event.ONFOCUS | Event.ONBLUR);
}
}
}
}
+ private void createHeaders(List<Header<?>> headers, TableSectionElement section) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("<tr>");
+ for (Header<?> header : headers) {
+ sb.append("<th>");
+ if (header != null) {
+ header.render(sb);
+ }
+ sb.append("</th>");
+ }
+ sb.append("</tr>");
+
+ section.setInnerHTML(sb.toString());
+ }
+
+ private void createHeadersAndFooters() {
+ createHeaders(headers, thead);
+ createHeaders(footers, tfoot);
+ }
+
private void createRows() {
- TableElement table = getElement().cast();
int numCols = columns.size();
// TODO - only delete as needed
- int numRows = table.getRows().getLength();
+ int numRows = tbody.getRows().getLength();
while (numRows-- > 0) {
- table.deleteRow(0);
+ tbody.deleteRow(0);
}
for (int r = 0; r < pageSize; ++r) {
- TableRowElement row = table.insertRow(0);
- row.setClassName("pagingTableListView "
- + ((r & 0x1) == 0 ? "evenRow" : "oddRow"));
+ TableRowElement row = tbody.insertRow(0);
+ row.setClassName("pagingTableListView " + ((r & 0x1) == 0 ? "evenRow" : "oddRow"));
// TODO: use cloneNode() to make this even faster.
for (int c = 0; c < numCols; ++c) {
@@ -233,26 +256,6 @@
}
}
- // Add the final row containing paging buttons
- TableRowElement pageRow = table.insertRow(pageSize);
- pageRow.insertCell(0);
- pageRow.insertCell(1);
- pageRow.insertCell(2);
-
- StringBuilder sb;
-
- sb = new StringBuilder();
- prevButton.render("Previous", sb);
- pageRow.getCells().getItem(0).setInnerHTML(sb.toString());
-
- pageRow.getCells().getItem(1).setAttribute("colspan", "" + (numCols - 2));
- pageRow.getCells().getItem(1).setAttribute("align", "center");
-
- sb = new StringBuilder();
- nextButton.render("Next", sb);
- pageRow.getCells().getItem(2).setInnerHTML(sb.toString());
- pageRow.getCells().getItem(2).setAttribute("align", "right");
-
// Make room for the data cache
data.ensureCapacity(pageSize);
while (data.size() < pageSize) {
@@ -260,24 +263,37 @@
}
}
+ private TableCellElement findNearestParentCell(Node node) {
+ while ((node != null) && (node != table)) {
+ if (Element.is(node)) {
+ Element elem = Element.as(node);
+
+ // TODO: We need is() implementations in all Element subclasses.
+ // This would allow us to use TableCellElement.is() -- much cleaner.
+ String tagName = elem.getTagName();
+ if ("td".equalsIgnoreCase(tagName) || "th".equalsIgnoreCase(tagName)) {
+ return elem.cast();
+ }
+ }
+ node = node.getParentNode();
+ }
+ return null;
+ }
+
/**
* Update the text that shows the current page.
*
* @param page the current page
*/
private void updatePageText(int page) {
- TableElement table = getElement().cast();
- NodeList<TableRowElement> rows = table.getRows();
- String text = "Page " + (page + 1) + " of " + numPages;
- rows.getItem(rows.getLength() - 1).getCells().getItem(1).setInnerText(text);
+ // TODO: Update external paging widget.
}
private void updateRowVisibility() {
int visible = Math.min(pageSize, totalSize - curPage * pageSize);
- TableElement table = getElement().cast();
for (int r = 0; r < pageSize; ++r) {
- Style rowStyle = table.getRows().getItem(r).getStyle();
+ Style rowStyle = tbody.getRows().getItem(r).getStyle();
if (r < visible) {
rowStyle.clearDisplay();
} else {
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/client/SimpleCellList.java b/bikeshed/src/com/google/gwt/bikeshed/list/client/SimpleCellList.java
index e1dba47..aaef117 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/list/client/SimpleCellList.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/client/SimpleCellList.java
@@ -24,6 +24,7 @@
import com.google.gwt.bikeshed.list.shared.SizeChangeEvent;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.Node;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Widget;
@@ -113,19 +114,25 @@
Element parent = getElement();
int childCount = parent.getChildCount();
- // Update existing cells with new values.
- int i, existing = Math.min(len, childCount);
- for (i = start; i < existing; ++i) {
- Element elem = parent.getChild(i).cast();
- cell.setValue(elem, values.get(i));
+ // Create innerHTML for the new items.
+ int end = start + len;
+ StringBuilder html = new StringBuilder();
+
+ // Empty items to fill any gaps.
+ int totalToAdd = 0;
+ for (int i = childCount; i < start; ++i) {
+ html.append("<div __idx='" + i + "'>");
+ cell.render(null, html);
+ html.append("</div>");
+ ++totalToAdd;
}
- // Create new cells if necessary.
- StringBuilder html = new StringBuilder();
- for (; i < len; ++i) {
+ // Items rendered from data.
+ for (int i = start; i < end; ++i) {
html.append("<div __idx='" + i + "'>");
- cell.render(values.get(i), html);
+ cell.render(values.get(i - start), html);
html.append("</div>");
+ ++totalToAdd;
}
if (childCount == 0) {
@@ -136,9 +143,27 @@
// in a temporary element, then move the cells back to the main element.
tmpElem.setInnerHTML(html.toString());
+ // Clear out old cells that overlap the new cells.
+ if (start < childCount) {
+ int toRemove = Math.min(end, childCount) - start;
+ for (int i = 0; i < toRemove; ++i) {
+ parent.removeChild(parent.getChild(start));
+ }
+ childCount = parent.getChildCount();
+ }
+
// Move the new cells over from the temp element.
- for (i = 0; i < len - childCount; ++i) {
- parent.appendChild(tmpElem.getChild(0));
+ if (start >= childCount) {
+ // Just append to the end.
+ for (int i = 0; i < totalToAdd; ++i) {
+ parent.appendChild(tmpElem.getChild(0));
+ }
+ } else {
+ // Insert them in the middle somewhere.
+ Node before = parent.getChild(start);
+ for (int i = 0; i < totalToAdd; ++i) {
+ parent.insertBefore(tmpElem.getChild(0), before);
+ }
}
}
}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockQueryWidget.java b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockQueryWidget.java
index 13f44e0..3fb325e 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockQueryWidget.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockQueryWidget.java
@@ -16,6 +16,7 @@
package com.google.gwt.bikeshed.sample.stocks.client;
import com.google.gwt.bikeshed.list.client.PagingTableListView;
+import com.google.gwt.bikeshed.list.client.TextHeader;
import com.google.gwt.bikeshed.list.shared.ListModel;
import com.google.gwt.bikeshed.sample.stocks.shared.StockQuote;
import com.google.gwt.dom.client.Style.Unit;
@@ -40,9 +41,9 @@
// Create the results table.
resultsTable = new PagingTableListView<StockQuote>(searchListModel, 10);
resultsTable.addColumn(Columns.favoriteColumn);
- resultsTable.addColumn(Columns.tickerColumn);
- resultsTable.addColumn(Columns.nameColumn);
- resultsTable.addColumn(Columns.priceColumn);
+ resultsTable.addColumn(Columns.tickerColumn, new TextHeader("ticker"));
+ resultsTable.addColumn(Columns.nameColumn, new TextHeader("name"));
+ resultsTable.addColumn(Columns.priceColumn, new TextHeader("price"));
resultsTable.addColumn(Columns.buyColumn);
// Focus the cursor on the name field when the app loads
diff --git a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockSample.java b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockSample.java
index ad5b85a..a16284e 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockSample.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockSample.java
@@ -17,6 +17,7 @@
import com.google.gwt.bikeshed.cells.client.FieldUpdater;
import com.google.gwt.bikeshed.list.client.PagingTableListView;
+import com.google.gwt.bikeshed.list.client.TextHeader;
import com.google.gwt.bikeshed.list.shared.AsyncListModel;
import com.google.gwt.bikeshed.list.shared.ListListModel;
import com.google.gwt.bikeshed.list.shared.Range;
@@ -135,10 +136,10 @@
// Create the favorites table.
favoritesTable = new PagingTableListView<StockQuote>(favoritesListModel, 10);
- favoritesTable.addColumn(Columns.tickerColumn);
- favoritesTable.addColumn(Columns.priceColumn);
- favoritesTable.addColumn(Columns.sharesColumn);
- favoritesTable.addColumn(Columns.dollarsColumn);
+ favoritesTable.addColumn(Columns.tickerColumn, new TextHeader("ticker"));
+ favoritesTable.addColumn(Columns.priceColumn, new TextHeader("price"));
+ favoritesTable.addColumn(Columns.sharesColumn, new TextHeader("shares"));
+ favoritesTable.addColumn(Columns.dollarsColumn, new TextHeader("value"));
favoritesTable.addColumn(Columns.buyColumn);
favoritesTable.addColumn(Columns.sellColumn);