From: Matti Tahvonen Date: Tue, 26 Jun 2007 11:48:18 +0000 (+0000) Subject: somewhat scrolling scrolltable X-Git-Tag: 6.7.0.beta1~6211 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=34892c9deb793ca6cfd581c09c300b980297b6a2;p=vaadin-framework.git somewhat scrolling scrolltable svn changeset:1788/svn branch:trunk --- diff --git a/src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetFactory.java b/src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetFactory.java index 8fb04329bb..0a22858281 100644 --- a/src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetFactory.java +++ b/src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetFactory.java @@ -16,7 +16,6 @@ import com.itmill.toolkit.terminal.gwt.client.ui.IPanel; import com.itmill.toolkit.terminal.gwt.client.ui.IPasswordField; import com.itmill.toolkit.terminal.gwt.client.ui.ISelect; import com.itmill.toolkit.terminal.gwt.client.ui.ITablePaging; -import com.itmill.toolkit.terminal.gwt.client.ui.ITableScrollingByComposition; import com.itmill.toolkit.terminal.gwt.client.ui.ITabsheet; import com.itmill.toolkit.terminal.gwt.client.ui.ITextArea; import com.itmill.toolkit.terminal.gwt.client.ui.ITextField; @@ -25,6 +24,7 @@ import com.itmill.toolkit.terminal.gwt.client.ui.ITwinColSelect; import com.itmill.toolkit.terminal.gwt.client.ui.IUnknownComponent; import com.itmill.toolkit.terminal.gwt.client.ui.IVerticalLayout; import com.itmill.toolkit.terminal.gwt.client.ui.IWindow; +import com.itmill.toolkit.terminal.gwt.client.ui.scrolltable.IScrollTable; public class DefaultWidgetFactory implements WidgetFactory { @@ -75,8 +75,13 @@ public class DefaultWidgetFactory implements WidgetFactory { return new IPasswordField(); return new ITextField(); } - if ("table".equals(tag)) + if ("table".equals(tag)) { + if(uidl.hasAttribute("style")) { + if("scrolling".equals(uidl.getStringAttribute("style"))) + return new IScrollTable(); + } return new ITablePaging(); + } if("datefield".equals(tag)) return new IDateField(); diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/ITableScrollingByComposition.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/ITableScrollingByComposition.java deleted file mode 100644 index ecddab1467..0000000000 --- a/src/com/itmill/toolkit/terminal/gwt/client/ui/ITableScrollingByComposition.java +++ /dev/null @@ -1,425 +0,0 @@ -package com.itmill.toolkit.terminal.gwt.client.ui; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import com.google.gwt.user.client.Command; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.DeferredCommand; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Timer; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.FlexTable; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.ScrollListener; -import com.google.gwt.user.client.ui.ScrollPanel; -import com.google.gwt.user.client.ui.VerticalPanel; -import com.google.gwt.user.client.ui.Widget; -import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter; -import com.itmill.toolkit.terminal.gwt.client.Client; -import com.itmill.toolkit.terminal.gwt.client.Paintable; -import com.itmill.toolkit.terminal.gwt.client.UIDL; - -public class ITableScrollingByComposition extends Composite implements Paintable, ScrollListener { - - /** - * multiple of pagelenght which component will - * cache when requesting more rows - */ - private static final double CACHE_RATE = 3; - /** - * fraction of pageLenght which can be scrolled without - * making new request - */ - private static final double CACHE_REACT_RATE = 1; - - private int firstRendered = -1; - private int lastRendered = -1; - private int firstRowInViewPort = 0; - private int pageLength = 15; - - private boolean rowHeaders = false; - - private Map columnOrder = new HashMap(); - - private Client client; - private String id; - private boolean immediate; - - private FlexTable tHead = new FlexTable(); - private FlexTable tBody = new FlexTable(); - - private ScrollPanel bodyContainer = new ScrollPanel(); - private VerticalPanel bodyContent = new VerticalPanel(); - - private ScrollPanel headerContainer = new ScrollPanel(); - - private HTML preSpacer = new HTML(); - private HTML postSpacer = new HTML(); - - private boolean colWidthsInitialized = false; - private int totalRows; - private HashMap columnWidths = new HashMap(); - - private int rowHeight = 0; - private RowRequestHandler rowRequestHandler; - - public ITableScrollingByComposition() { - headerContainer.add(tHead); - DOM.setStyleAttribute(headerContainer.getElement(), "overflow", "hidden"); - - tBody.setStyleName("itable-tbody"); - - bodyContent.add(preSpacer); - bodyContent.add(tBody); - bodyContent.add(postSpacer); - //TODO remove debug color - DOM.setStyleAttribute(postSpacer.getElement(), "background", "gray"); - bodyContainer.add(bodyContent); - bodyContainer.addScrollListener(this); - - VerticalPanel panel = new VerticalPanel(); - panel.add(headerContainer); - panel.add(bodyContainer); - - rowRequestHandler = new RowRequestHandler(); - - initWidget(panel); - } - - public void updateFromUIDL(UIDL uidl, Client client) { - if (client.updateComponent(this, uidl, true)) - return; - - this.client = client; - this.id = uidl.getStringAttribute("id"); - this.immediate = uidl.getBooleanAttribute("immediate"); - this.totalRows = uidl.getIntAttribute("totalrows"); - this.pageLength = uidl.getIntAttribute("pagelength"); - if(uidl.hasAttribute("rowheaders")) - rowHeaders = true; - - UIDL columnInfo = null; - UIDL rowData = null; - for(Iterator it = uidl.getChildIterator(); it.hasNext();) { - UIDL c = (UIDL) it.next(); - if(c.getTag().equals("cols")) - columnInfo = c; - else if(c.getTag().equals("rows")) - rowData = c; - else if(c.getTag().equals("actions")) - updateActionMap(c); - else if(c.getTag().equals("visiblecolumns")) - ; - } - updateHeader(columnInfo); - - updateBody(rowData, uidl.getIntAttribute("firstrow"),uidl.getIntAttribute("rows")); - - if(!colWidthsInitialized) { - DeferredCommand.add(new Command() { - public void execute() { - initSize(); - updateSpacers(); - bodyContainer.setScrollPosition(getRowHeight()*(firstRowInViewPort -1)); - colWidthsInitialized = true; - if(totalRows - 1 > lastRendered) { - // fetch cache rows - rowRequestHandler.setReqFirstRow(lastRendered+1); - rowRequestHandler.setReqRows((int) (pageLength*CACHE_RATE)); - rowRequestHandler.deferRowFetch(); - } - } - }); - } - } - - private void updateActionMap(UIDL c) { - // TODO Auto-generated method stub - - } - - private void updateHeader(UIDL uidl) { - if(uidl == null) - return; - for(Iterator it = uidl.getChildIterator();it.hasNext();) { - UIDL col = (UIDL) it.next(); - String cid = col.getStringAttribute("cid"); - int colIndex = getColIndexByKey(cid); - if(colIndex > -1) - setHeaderText(colIndex, col.getStringAttribute("caption")); - DOM.setAttribute(tHead.getFlexCellFormatter().getElement(0, colIndex), "cid", cid); - } - } - - /** - * Updates row data from uidl. UpdateFromUIDL delegates updating - * tBody to this method. - * - * Updates may be to different part of tBody, depending on update type. - * It can be initial row data, scroll up, scroll down... - * - * @param uidl which contains row data - * @param firstRow first row in data set - * @param reqRows amount of rows in data set - */ - private void updateBody(UIDL uidl, int firstRow, int reqRows) { - if(uidl == null || reqRows < 1) - return; - - Iterator it = uidl.getChildIterator(); - - if(firstRendered == -1 || firstRow == lastRendered + 1) { - //initial data to body or appending rows to table - while(it.hasNext()) { - appendRow( (UIDL) it.next() ); - if(colWidthsInitialized) - updateSpacers(); - } -// lastRendered = firstRow + reqRows - 1; - if(firstRendered == -1) { - firstRendered = firstRow; - } - } else if(firstRendered == firstRow + reqRows) { - // add received rows before old ones - int rowsAdded = 0; - while(it.hasNext()){ - tBody.insertRow(rowsAdded); - updateSpacers(); - updateRow( (UIDL) it.next(), rowsAdded); - } - firstRendered = firstRow; - } else { - // complitely new set received, truncate body and recurse - tBody.clear(); - firstRendered = -1; - lastRendered = -1; - updateBody(uidl, firstRow, reqRows); - } - trimBody(); - } - - /** - * Returns calculated height of row. - * @return height in pixels - */ - private int getRowHeight() { - if(rowHeight == 0) - rowHeight = tBody.getOffsetHeight()/getRenderedRowCount(); - return rowHeight; - } - - /** - * This method removes rows from body which are "out of - * cache area" to keep amount of rendered rows sane. - */ - private void trimBody() { - int toBeRemovedFromTheBeginning = (int) (firstRowInViewPort - CACHE_RATE*pageLength) - firstRendered; - int toBeRemovedFromTheEnd = lastRendered - (int) (firstRowInViewPort + CACHE_RATE*pageLength + pageLength); - if(toBeRemovedFromTheBeginning > 0) { - // remove extra rows from the beginning of the table - while(toBeRemovedFromTheBeginning > 0) { - tBody.removeRow(0); - firstRendered++; - toBeRemovedFromTheBeginning--; - updateSpacers(); - } - } - if(toBeRemovedFromTheEnd > 0) { - // remove extra rows from the end of the table - while(toBeRemovedFromTheEnd > 0) { - tBody.removeRow(tBody.getRowCount() - 1); - toBeRemovedFromTheEnd--; - lastRendered--; - updateSpacers(); - } - } -// bodyContainer.setScrollPosition(getRowHeight()*firstRowInViewPort); - } - - private void appendRow(UIDL uidl) { - lastRendered++; - updateRow(uidl, lastRendered); - } - - private void updateRow(UIDL uidl, int rowIndex) { - int colIndex = 0; - if(rowHeaders) { - setCellContent(rowIndex, colIndex, uidl.getStringAttribute("caption")); - colIndex++; - } - - for(Iterator it = uidl.getChildIterator(); it.hasNext();) { - Object cell = it.next(); - if (cell instanceof String) { - setCellContent(rowIndex, colIndex, (String) cell); - } else { - setCellContent(rowIndex, colIndex, (UIDL) cell); - } - colIndex++; - } - Element row = tBody.getRowFormatter().getElement(rowIndex); - DOM.setIntAttribute(row, "key", uidl.getIntAttribute("key")); - } - - private int getColIndexByKey(String colKey) { - return Integer.parseInt(colKey) - 1 + (rowHeaders ? 1 : 0); - } - - private String getColKeyByIndex(int index) { - return DOM.getAttribute(tHead.getCellFormatter().getElement(0, index), "cid"); - } - - public void setHeaderText(int colIndex, String text) { - tHead.setText(0, colIndex, text); - } - - public void setCellContent(int rowId, int colId, UIDL cell) { - if(cell == null) - return; - Widget cellContent = client.getWidget(cell); - tBody.setWidget(rowId, colId, cellContent); - ((Paintable)cell).updateFromUIDL(cell, client); - tBody.getCellFormatter().setWordWrap(rowId, colId, false); - } - - public void setCellContent(int rowId, int colId, String text) { - HTML cellContent = new HTML(); - cellContent.setText(text); - tBody.setWidget(rowId, colId, cellContent); - } - - /** - * Run when receices its initial content. Syncs headers and bodys - * "natural widths and saves the values. - */ - private void initSize() { - int cols = tHead.getCellCount(0); - FlexCellFormatter hf = tHead.getFlexCellFormatter(); - FlexCellFormatter bf = tBody.getFlexCellFormatter(); - for (int i = 0; i < cols; i++) { - Element hCell = hf.getElement(0, i); - Element bCell = bf.getElement(1, i); - int hw = DOM.getIntAttribute(hCell, "offsetWidth"); - int cw = DOM.getIntAttribute(bCell, "offsetWidth"); - setColWidth(i , hw > cw ? hw : cw); - } - - bodyContainer.setHeight(tBody.getOffsetHeight() + "px"); - bodyContainer.setWidth((tBody.getOffsetWidth() + 20) + "px"); - - } - - private void setColWidth(int colIndex, int w) { - String cid = getColKeyByIndex(colIndex); - tHead.getCellFormatter().setWidth(0, colIndex, w + "px"); - tBody.getCellFormatter().setWidth(0, colIndex, w + "px"); - columnWidths.put(cid,new Integer(w)); - } - - private int getColWidth(String colKey) { - return ( (Integer) this.columnWidths.get(colKey)).intValue(); - } - - private void updateSpacers() { - int preSpacerHeight = (firstRendered)*getRowHeight(); - int postSpacerHeight = (totalRows - 1 - lastRendered)*getRowHeight(); - preSpacer.setHeight(preSpacerHeight+"px"); - postSpacer.setHeight(postSpacerHeight + "px"); - } - - private int getRenderedRowCount() { - return lastRendered-firstRendered; - } - - /** - * This method has logick which rows needs to be requested from - * server when user scrolls - * - */ - public void onScroll(Widget widget, int scrollLeft, int scrollTop) { - rowRequestHandler.cancel(); - - firstRowInViewPort = (int) Math.ceil( scrollTop / rowHeight ); - client.console.log("At scrolltop: " + scrollTop + " At row " + firstRowInViewPort); - - int postLimit = (int) (firstRowInViewPort + pageLength + pageLength*CACHE_REACT_RATE); - if(postLimit > totalRows) - postLimit = totalRows; - int preLimit = (int) (firstRowInViewPort - pageLength*CACHE_REACT_RATE); - if(preLimit < 0) - preLimit = 0; - if( - (postLimit <= lastRendered && preLimit >= firstRendered ) - ) { - client.updateVariable(this.id, "firstvisible", firstRowInViewPort, false); - return; // scrolled withing "non-react area" - } - - if(firstRowInViewPort - pageLength*CACHE_RATE > lastRendered || - firstRowInViewPort + pageLength + pageLength*CACHE_RATE < firstRendered ) { - // need a totally new set - client.console.log("Table: need a totally new set"); - rowRequestHandler.setReqFirstRow((int) (firstRowInViewPort - pageLength*CACHE_RATE)); - rowRequestHandler.setReqRows((int) (2*CACHE_RATE*pageLength + pageLength)); - rowRequestHandler.deferRowFetch(); - return; - } - if(preLimit < firstRendered ) { - // need some rows to the beginning of the rendered area - client.console.log("Table: need some rows to the beginning of the rendered area"); - rowRequestHandler.setReqFirstRow((int) (firstRowInViewPort - pageLength*CACHE_RATE)); - rowRequestHandler.setReqRows(firstRendered - rowRequestHandler.getReqFirstRow()); - rowRequestHandler.deferRowFetch(); - - return; - } - if(postLimit > lastRendered) { - // need some rows to the end of the rendered area - client.console.log("need some rows to the end of the rendered area"); - rowRequestHandler.setReqFirstRow(lastRendered + 1); - rowRequestHandler.setReqRows((int) ((firstRowInViewPort + pageLength + pageLength*CACHE_RATE) - lastRendered)); - rowRequestHandler.deferRowFetch(); - } - } - - private class RowRequestHandler extends Timer { - - private int reqFirstRow = 0; - private int reqRows = 0; - - public void deferRowFetch() { - if(reqRows > 0 && reqFirstRow < totalRows) - schedule(250); - } - - public void setReqFirstRow(int reqFirstRow) { - if(reqFirstRow < 0) - reqFirstRow = 0; - else if(reqFirstRow >= totalRows) - reqFirstRow = totalRows - 1; - this.reqFirstRow = reqFirstRow; - } - - public void setReqRows(int reqRows) { - this.reqRows = reqRows; - } - - public void run() { - client.console.log("Getting " + reqRows + " rows from " + reqFirstRow); - client.updateVariable(id, "firstvisible", firstRowInViewPort, false); - client.updateVariable(id, "reqfirstrow", reqFirstRow, false); - client.updateVariable(id, "reqrows", reqRows, true); - } - - public int getReqFirstRow() { - return reqFirstRow; - } - - public int getReqRows() { - return reqRows; - } - - } -} diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/scrolltable/IScrollTable.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/scrolltable/IScrollTable.java new file mode 100644 index 0000000000..34a5ade2a3 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/scrolltable/IScrollTable.java @@ -0,0 +1,358 @@ +package com.itmill.toolkit.terminal.gwt.client.ui.scrolltable; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import com.google.gwt.user.client.Command; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.DeferredCommand; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlexTable; +import com.google.gwt.user.client.ui.ScrollListener; +import com.google.gwt.user.client.ui.ScrollPanel; +import com.google.gwt.user.client.ui.VerticalPanel; +import com.google.gwt.user.client.ui.Widget; +import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter; +import com.itmill.toolkit.terminal.gwt.client.Client; +import com.itmill.toolkit.terminal.gwt.client.Paintable; +import com.itmill.toolkit.terminal.gwt.client.UIDL; +import com.itmill.toolkit.terminal.gwt.client.ui.ITable; + +public class IScrollTable extends Composite implements Paintable, ITable, ScrollListener { + + /** + * multiple of pagelenght which component will + * cache when requesting more rows + */ + private static final double CACHE_RATE = 3; + /** + * fraction of pageLenght which can be scrolled without + * making new request + */ + private static final double CACHE_REACT_RATE = 1; + + private int firstRowInViewPort = 0; + private int pageLength = 15; + + private boolean rowHeaders = false; + + private Map columnOrder = new HashMap(); + + private Client client; + private String id; + private boolean immediate; + + private boolean initializedAndAttached = false; + + private FlexTable tHead = new FlexTable(); + + private ScrollPanel bodyContainer = new ScrollPanel(); + + private ScrollPanel headerContainer = new ScrollPanel(); + + private boolean colWidthsInitialized = false; + private int totalRows; + private HashMap columnWidths = new HashMap(); + + private RowRequestHandler rowRequestHandler; + private IScrollTableBody tBody; + private int width = -1; + private int height = -1; + private int firstvisible; + + public IScrollTable() { + headerContainer.setStyleName("iscrolltable-header"); + headerContainer.add(tHead); + DOM.setStyleAttribute(headerContainer.getElement(), "overflow", "hidden"); + + bodyContainer.addScrollListener(this); + + VerticalPanel panel = new VerticalPanel(); + panel.add(headerContainer); + panel.add(bodyContainer); + + rowRequestHandler = new RowRequestHandler(); + + initWidget(panel); + } + + public void updateFromUIDL(UIDL uidl, Client client) { + if (client.updateComponent(this, uidl, true)) + return; + + this.client = client; + this.id = uidl.getStringAttribute("id"); + this.immediate = uidl.getBooleanAttribute("immediate"); + this.totalRows = uidl.getIntAttribute("totalrows"); + this.pageLength = uidl.getIntAttribute("pagelength"); + this.firstvisible = uidl.getIntVariable("firstvisible"); + if(uidl.hasAttribute("rowheaders")) + rowHeaders = true; + if(uidl.hasAttribute("width")) + width = uidl.getIntAttribute("width"); + if(uidl.hasAttribute("height")) + width = uidl.getIntAttribute("height"); + + UIDL columnInfo = null; + UIDL rowData = null; + for(Iterator it = uidl.getChildIterator(); it.hasNext();) { + UIDL c = (UIDL) it.next(); + if(c.getTag().equals("cols")) + columnInfo = c; + else if(c.getTag().equals("rows")) + rowData = c; + else if(c.getTag().equals("actions")) + updateActionMap(c); + else if(c.getTag().equals("visiblecolumns")) + ; + } + updateHeader(columnInfo); + + if(initializedAndAttached) { + updateBody(rowData, uidl.getIntAttribute("firstrow"),uidl.getIntAttribute("rows")); + } else { + getTBody().renderInitialRows(rowData, + uidl.getIntAttribute("firstrow"), + uidl.getIntAttribute("rows"), + totalRows); + bodyContainer.add(tBody); + initializedAndAttached = true; + } + } + + private IScrollTableBody getTBody() { + if(tBody == null || totalRows != tBody.getTotalRows()) { + if(tBody != null) + tBody.removeFromParent(); + tBody = new IScrollTableBody(client); + } + return tBody; + } + + private void updateActionMap(UIDL c) { + // TODO Auto-generated method stub + + } + + private void updateHeader(UIDL uidl) { + if(uidl == null) + return; + for(Iterator it = uidl.getChildIterator();it.hasNext();) { + UIDL col = (UIDL) it.next(); + String cid = col.getStringAttribute("cid"); + int colIndex = getColIndexByKey(cid); + if(colIndex > -1) + setHeaderText(colIndex, col.getStringAttribute("caption")); + DOM.setAttribute(tHead.getFlexCellFormatter().getElement(0, colIndex), "cid", cid); + } + } + + /** + * @param uidl which contains row data + * @param firstRow first row in data set + * @param reqRows amount of rows in data set + */ + private void updateBody(UIDL uidl, int firstRow, int reqRows) { + if(uidl == null || reqRows < 1) + return; + + tBody.renderRows(uidl, firstRow, reqRows); + + int optimalFirstRow = (int) (firstRowInViewPort - pageLength*CACHE_RATE); + while(tBody.getFirstRendered() < optimalFirstRow) { +// client.console.log("removing row from start"); + tBody.unlinkRow(true); + } + int optimalLastRow = (int) (firstRowInViewPort + pageLength + pageLength*CACHE_RATE); + while(tBody.getLastRendered() > optimalLastRow) { +// client.console.log("removing row from the end"); + tBody.unlinkRow(false); + } + + } + + + private int getColIndexByKey(String colKey) { + return Integer.parseInt(colKey) - 1 + (rowHeaders ? 1 : 0); + } + + private String getColKeyByIndex(int index) { + return DOM.getAttribute(tHead.getCellFormatter().getElement(0, index), "cid"); + } + + public void setHeaderText(int colIndex, String text) { + tHead.setText(0, colIndex, text); + } + + private void setColWidth(int colIndex, int w) { + String cid = getColKeyByIndex(colIndex); + tHead.getCellFormatter().setWidth(0, colIndex, w + "px"); + tBody.setColWidth(colIndex, w); + columnWidths.put(cid,new Integer(w)); + } + + private int getColWidth(String colKey) { + return ( (Integer) this.columnWidths.get(colKey)).intValue(); + } + + private int getRenderedRowCount() { + return tBody.getLastRendered()-tBody.getFirstRendered(); + } + + /** + * This method has logick which rows needs to be requested from + * server when user scrolls + * + */ + public void onScroll(Widget widget, int scrollLeft, int scrollTop) { + if(!initializedAndAttached) + return; + + rowRequestHandler.cancel(); + + firstRowInViewPort = (int) Math.ceil( scrollTop / tBody.getRowHeight() ); + client.console.log("At scrolltop: " + scrollTop + " At row " + firstRowInViewPort); + + int postLimit = (int) (firstRowInViewPort + pageLength + pageLength*CACHE_REACT_RATE); + if(postLimit > totalRows) + postLimit = totalRows; + int preLimit = (int) (firstRowInViewPort - pageLength*CACHE_REACT_RATE); + if(preLimit < 0) + preLimit = 0; + int lastRendered = tBody.getLastRendered(); + int firstRendered = tBody.getFirstRendered(); + if( + (postLimit <= lastRendered && preLimit >= firstRendered ) + ) { + client.updateVariable(this.id, "firstvisible", firstRowInViewPort, false); + return; // scrolled withing "non-react area" + } + + if(firstRowInViewPort - pageLength*CACHE_RATE > lastRendered || + firstRowInViewPort + pageLength + pageLength*CACHE_RATE < firstRendered ) { + // need a totally new set + client.console.log("Table: need a totally new set"); + rowRequestHandler.setReqFirstRow((int) (firstRowInViewPort - pageLength*CACHE_RATE)); + rowRequestHandler.setReqRows((int) (2*CACHE_RATE*pageLength + pageLength)); + rowRequestHandler.deferRowFetch(); + return; + } + if(preLimit < firstRendered ) { + // need some rows to the beginning of the rendered area + client.console.log("Table: need some rows to the beginning of the rendered area"); + rowRequestHandler.setReqFirstRow((int) (firstRowInViewPort - pageLength*CACHE_RATE)); + rowRequestHandler.setReqRows(firstRendered - rowRequestHandler.getReqFirstRow()); + rowRequestHandler.deferRowFetch(); + + return; + } + if(postLimit > lastRendered) { + // need some rows to the end of the rendered area + client.console.log("need some rows to the end of the rendered area"); + rowRequestHandler.setReqFirstRow(lastRendered + 1); + rowRequestHandler.setReqRows((int) ((firstRowInViewPort + pageLength + pageLength*CACHE_RATE) - lastRendered)); + rowRequestHandler.deferRowFetch(); + } + } + + + + protected void onAttach() { + + super.onAttach(); + + // sync column widths + initColumnWidths(); + + if(height < 0) { + bodyContainer.setHeight((tBody.getRowHeight()*pageLength) + "px"); + } else { + bodyContainer.setHeight(height + "px"); + } + + if(width < 0) { + bodyContainer.setWidth((tBody.getOffsetWidth() + getScrollBarWidth() ) + "px"); + } else { + bodyContainer.setWidth(width + "px"); + } + + if(firstvisible > 0) + bodyContainer.setScrollPosition(firstvisible*tBody.getRowHeight()); + + DeferredCommand.add(new Command() { + public void execute() { + if(totalRows - 1 > tBody.getLastRendered()) { + // fetch cache rows + rowRequestHandler.setReqFirstRow(tBody.getLastRendered()+1); + rowRequestHandler.setReqRows((int) (pageLength*CACHE_RATE)); + rowRequestHandler.deferRowFetch(); + } + } + }); + + + } + + /** + * Run when receices its initial content. Syncs headers and bodys + * "natural widths and saves the values. + */ + private void initColumnWidths() { + int cols = tHead.getCellCount(0); + FlexCellFormatter hf = tHead.getFlexCellFormatter(); + for (int i = 0; i < cols; i++) { + Element hCell = hf.getElement(0, i); + int hw = DOM.getIntAttribute(hCell, "offsetWidth"); + int cw = tBody.getColWidth(i); + int w = (hw > cw ? hw : cw) + IScrollTableBody.CELL_EXTRA_WIDTH; + setColWidth(i , w); + } + } + + private int getScrollBarWidth() { + // TODO Auto-generated method stub + return 30; + } + + private class RowRequestHandler extends Timer { + + private int reqFirstRow = 0; + private int reqRows = 0; + + public void deferRowFetch() { + if(reqRows > 0 && reqFirstRow < totalRows) + schedule(250); + } + + public void setReqFirstRow(int reqFirstRow) { + if(reqFirstRow < 0) + reqFirstRow = 0; + else if(reqFirstRow >= totalRows) + reqFirstRow = totalRows - 1; + this.reqFirstRow = reqFirstRow; + } + + public void setReqRows(int reqRows) { + this.reqRows = reqRows; + } + + public void run() { + client.console.log("Getting " + reqRows + " rows from " + reqFirstRow); + client.updateVariable(id, "firstvisible", firstRowInViewPort, false); + client.updateVariable(id, "reqfirstrow", reqFirstRow, false); + client.updateVariable(id, "reqrows", reqRows, true); + } + + public int getReqFirstRow() { + return reqFirstRow; + } + + public int getReqRows() { + return reqRows; + } + + } +} diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/scrolltable/IScrollTableBody.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/scrolltable/IScrollTableBody.java new file mode 100644 index 0000000000..7b2b54b498 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/scrolltable/IScrollTableBody.java @@ -0,0 +1,235 @@ +package com.itmill.toolkit.terminal.gwt.client.ui.scrolltable; + +import java.util.Iterator; +import java.util.List; +import java.util.Vector; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.ui.Panel; +import com.google.gwt.user.client.ui.Widget; +import com.itmill.toolkit.terminal.gwt.client.Client; +import com.itmill.toolkit.terminal.gwt.client.UIDL; + +/** + * This Panel can only contain IScrollTAbleRow type of + * widgets. This "simulates" very large table, keeping + * spacers which take room of unrendered rows. + * + * @author mattitahvonen + * + */ +public class IScrollTableBody extends Panel { + + public static final int CELL_EXTRA_WIDTH = 20; + + protected static int DEFAULT_ROW_HEIGHT = 25; + + private int rowHeight = -1; + + private List renderedRows = new Vector(); + + private boolean initDone = false; + + private int totalRows; + + Element preSpacer = DOM.createDiv(); + Element postSpacer = DOM.createDiv(); + + Element container = DOM.createDiv(); + + Element tBody = DOM.createTBody(); + Element table = DOM.createTable(); + + private int firstRendered; + + private int lastRendered; + + private Client client; + + + IScrollTableBody(Client client) { + this.client = client; + + constructDOM(); + + setElement(container); + + } + + private void constructDOM() { + DOM.setAttribute(table, "className", "iscrolltable-table"); + DOM.setAttribute(preSpacer, "className", "iscrolltable-rowspacer"); + DOM.setAttribute(postSpacer, "className", "iscrolltable-rowspacer"); + + DOM.appendChild(table, tBody); + DOM.appendChild(container, preSpacer); + DOM.appendChild(container, table); + DOM.appendChild(container, postSpacer); + + } + + + public void renderInitialRows(UIDL rowData, int firstIndex, int rows, int totalRows) { + this.totalRows = totalRows; + this.firstRendered = firstIndex; + this.lastRendered = firstIndex + rows - 1 ; + Iterator it = rowData.getChildIterator(); + while(it.hasNext()) { + IScrollTableRow row = new IScrollTableRow((UIDL) it.next(), client); + addRow(row); + } + if(isAttached()) + fixSpacers(); + } + + public void renderRows(UIDL rowData, int firstIndex, int rows) { + Iterator it = rowData.getChildIterator(); + if(firstIndex == lastRendered + 1) { + while(it.hasNext()) { + IScrollTableRow row = createRow((UIDL) it.next()); + addRow(row); + lastRendered++; + } + fixSpacers(); + } else if(firstIndex + rows == firstRendered) { + IScrollTableRow[] rowArray = new IScrollTableRow[rows]; + int i = rows; + while(it.hasNext()) { + i--; + rowArray[i] = createRow((UIDL) it.next()); + } + for(i = 0 ; i < rows; i++) { + addRowBeforeFirstRendered(rowArray[i]); + firstRendered--; + } + } else if (firstIndex > lastRendered || firstIndex + rows < firstRendered) { + // complitely new set of rows + // create one row before truncating row + IScrollTableRow row = createRow((UIDL) it.next()); + while(lastRendered + 1 > firstRendered) + unlinkRow(false); + + addRow(row); + firstRendered = firstIndex; + this.lastRendered = firstIndex + rows - 1 ; + while(it.hasNext()) + addRow(createRow((UIDL) it.next())); + fixSpacers(); + } else { + client.console.log("Bad update" + firstIndex + "/"+ rows); + } + } + + /** + * This mehtod is used to instantiate new rows for this table. + * It automatically sets correct widths to rows cells and assigns + * correct client reference for child widgets. + * + * This method can be called only after table has been initialized + * + * @param uidl + * @param client2 + */ + private IScrollTableRow createRow(UIDL uidl) { + IScrollTableRow row = new IScrollTableRow(uidl, client); + int cells = DOM.getChildCount(row.getElement()); + for(int i = 0; i < cells; i++) { + Element cell = DOM.getChild(row.getElement(), i); + int w = getColWidth(i); + DOM.setStyleAttribute(cell, "width", w + "px"); + DOM.setStyleAttribute(DOM.getFirstChild(cell), "width", w + "px"); + } + return row; + } + + private void addRowBeforeFirstRendered(IScrollTableRow row) { + DOM.insertChild(tBody, row.getElement(), 0); + adopt(row, null); + renderedRows.add(0, row); + } + + private void addRow(IScrollTableRow row) { + DOM.appendChild(tBody, row.getElement()); + adopt(row, null); + renderedRows.add(row); + } + + public Iterator iterator() { + return renderedRows.iterator(); + } + + public void unlinkRow(boolean fromBeginning) { + if(lastRendered - firstRendered < 0) + return; + int index; + if(fromBeginning) { + index = 0; + firstRendered++; + } else { + index = renderedRows.size() - 1; + lastRendered--; + } + IScrollTableRow toBeRemoved = (IScrollTableRow) renderedRows.get(index); + this.disown(toBeRemoved); + renderedRows.remove(index); + fixSpacers(); + } + + public boolean remove(Widget w) { + throw new UnsupportedOperationException(); + } + + protected void onAttach() { + super.onAttach(); + fixSpacers(); + } + + private void fixSpacers() { + int tBodyHeight = DOM.getIntAttribute(table, "offsetHeight"); + DOM.setStyleAttribute(preSpacer, "height", getRowHeight()*firstRendered + "px"); + DOM.setStyleAttribute(postSpacer, "height", getRowHeight()*(totalRows - 1 - lastRendered) + "px"); + + } + + public int getTotalRows() { + return totalRows; + } + + public int getRowHeight() { + if(initDone) + return rowHeight; + else { + if(DOM.getChildCount(tBody) > 0) { + rowHeight = DOM.getIntAttribute(tBody, "offsetHeight")/DOM.getChildCount(tBody); + } else { + return DEFAULT_ROW_HEIGHT; + } + initDone = true; + return rowHeight; + } + } + + public int getColWidth(int i) { + Element e = DOM.getChild(DOM.getChild(tBody, 0), i); + return DOM.getIntAttribute(e, "offsetWidth"); + } + + public void setColWidth(int colIndex, int w) { + int rows = DOM.getChildCount(tBody); + for(int i = 0; i < rows; i++) { + Element cell = DOM.getChild(DOM.getChild(tBody, i), colIndex); + DOM.setStyleAttribute(cell, "width", w + "px"); + DOM.setStyleAttribute(DOM.getFirstChild(cell), "width", w + "px"); + } + } + + public int getLastRendered() { + return lastRendered; + } + + public int getFirstRendered() { + return firstRendered; + } + +} diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/scrolltable/IScrollTableRow.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/scrolltable/IScrollTableRow.java new file mode 100644 index 0000000000..9608dcd397 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/scrolltable/IScrollTableRow.java @@ -0,0 +1,62 @@ +package com.itmill.toolkit.terminal.gwt.client.ui.scrolltable; + +import java.util.Iterator; +import java.util.Vector; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.Panel; +import com.google.gwt.user.client.ui.Widget; +import com.itmill.toolkit.terminal.gwt.client.Client; +import com.itmill.toolkit.terminal.gwt.client.Paintable; +import com.itmill.toolkit.terminal.gwt.client.UIDL; + +public class IScrollTableRow extends Panel { + + Vector childWidgets = new Vector(); + + private IScrollTableRow() { + setElement(DOM.createElement("tr")); + } + + public IScrollTableRow(UIDL uidl, Client client) { + this(); + if(uidl.hasAttribute("caption")) + addCell(uidl.getStringAttribute("caption")); + Iterator cells = uidl.getChildIterator(); + while(cells.hasNext()) { + Object cell = cells.next(); + if (cell instanceof String) { + addCell(cell.toString()); + } else { + Widget cellContent = client.getWidget((UIDL) cell); + (( Paintable) cellContent).updateFromUIDL((UIDL) cell, client); + } + } + } + + public void addCell(String text) { + addCell(new Label(text)); + } + + public void addCell(Widget w) { + Element td = DOM.createTD(); + Element container = DOM.createDiv(); + DOM.setAttribute(container, "className", "iscrolltable-cellContent"); + DOM.appendChild(td, container); + DOM.appendChild(getElement(), td); + adopt(w, container); + childWidgets.add(w); + } + + public Iterator iterator() { + return childWidgets.iterator(); + } + + public boolean remove(Widget w) { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/src/com/itmill/toolkit/terminal/gwt/public/component-themes/common/css/common.css b/src/com/itmill/toolkit/terminal/gwt/public/component-themes/common/css/common.css index 7cd3202b4d..0e70e15a0b 100644 --- a/src/com/itmill/toolkit/terminal/gwt/public/component-themes/common/css/common.css +++ b/src/com/itmill/toolkit/terminal/gwt/public/component-themes/common/css/common.css @@ -14,4 +14,55 @@ input, select, textarea, button { select { padding: 0; margin: 0; +} + +/* TODO move table styles to separate file */ + +.iscrolltable-header table { + border-collapse:collapse; + margin:0; + padding:0; + border:0; + background: yellow; + table-layout: fixed; +} + +.iscrolltable-header table td { + margin:0; + padding:0; + border:0; +} + + + +.iscrolltable-table { + border-collapse:collapse; + margin:0; + padding:0; + border:0; + background: yellow; + table-layout: fixed; +} + +.iscrolltable-table td { + border:0; + margin:0; + padding:0; + background: green; +} +iscrolltable-table tr { + background: blue; + border:0; + margin:0; + padding:0; +} + +.iscrolltable-rowspacer { + height: 10px; + background: brown; +} + +.iscrolltable-table .iscrolltable-cellContent { + white-space: nowrap; + overflow: hidden; } \ No newline at end of file