]> source.dussan.org Git - vaadin-framework.git/commitdiff
add support for variable column widths (#12645)
authorHenrik Paul <henrik@vaadin.com>
Wed, 4 Dec 2013 11:39:28 +0000 (13:39 +0200)
committerHenrik Paul <henrik@vaadin.com>
Wed, 4 Dec 2013 11:39:28 +0000 (13:39 +0200)
Change-Id: I7c723234c9d183e5217e0eef5062ec4fa8e58069

client/src/com/vaadin/client/ui/grid/ColumnConfiguration.java
client/src/com/vaadin/client/ui/grid/Escalator.java
client/src/com/vaadin/client/ui/grid/FlyweightCell.java
client/src/com/vaadin/client/ui/grid/FlyweightRow.java
uitest/src/com/vaadin/tests/components/grid/BasicEscalator.java
uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridClientRpc.java
uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridConnector.java
uitest/src/com/vaadin/tests/widgetset/client/grid/VTestGrid.java
uitest/src/com/vaadin/tests/widgetset/server/grid/TestGrid.java

index cfbd2b1ea5985b3ef99b512a4dc84bfbe5990fdc..64104164cd79eea99408c907ba9e75d4b8e6c988 100644 (file)
@@ -106,4 +106,41 @@ public interface ColumnConfiguration {
      * @return the number of frozen columns
      */
     public int getFrozenColumnCount();
+
+    /**
+     * Sets (or unsets) an explicit width for a column.
+     * 
+     * @param index
+     *            the index of the column for which to set a width
+     * @param px
+     *            the number of pixels the indicated column should be, or a
+     *            negative number to let the escalator decide
+     * @throws IllegalArgumentException
+     *             if <code>index</code> is not a valid column index
+     */
+    public void setColumnWidth(int index, int px)
+            throws IllegalArgumentException;
+
+    /**
+     * Returns the user-defined width of a column.
+     * 
+     * @param index
+     *            the index of the column for which to retrieve the width
+     * @return the column's width in pixels, or a negative number if the width
+     *         is implicitly decided by the escalator
+     * @throws IllegalArgumentException
+     *             if <code>index</code> is not a valid column index
+     */
+    public int getColumnWidth(int index) throws IllegalArgumentException;
+
+    /**
+     * Returns the actual width of a column.
+     * 
+     * @param index
+     *            the index of the column for which to retrieve the width
+     * @return the column's actual width in pixels
+     * @throws IllegalArgumentException
+     *             if <code>index</code> is not a valid column index
+     */
+    public int getColumnWidthActual(int index) throws IllegalArgumentException;
 }
\ No newline at end of file
index e3aae2eb13240867b8195dc49efec31c40e683cb..1b7dbf51a407c75160c6bdc57ceb79079be7e279 100644 (file)
@@ -33,6 +33,7 @@ import com.google.gwt.dom.client.NativeEvent;
 import com.google.gwt.dom.client.Node;
 import com.google.gwt.dom.client.NodeList;
 import com.google.gwt.dom.client.Style;
+import com.google.gwt.dom.client.Style.Display;
 import com.google.gwt.dom.client.Style.Unit;
 import com.google.gwt.event.logical.shared.AttachEvent;
 import com.google.gwt.user.client.DOM;
@@ -230,10 +231,10 @@ public class Escalator extends Widget {
      * re-measure)
      */
     /*
-     * [[rowwidth]] [[colwidth]]: This code will require alterations that are
-     * relevant for being able to support variable row heights or column widths.
-     * NOTE: these bits can most often also be identified by searching for code
-     * reading the ROW_HEIGHT_PX and COL_WIDTH_PX constans.
+     * [[rowheight]]: This code will require alterations that are relevant for
+     * being able to support variable row heights. NOTE: these bits can most
+     * often also be identified by searching for code reading the ROW_HEIGHT_PX
+     * constant.
      */
     /*
      * [[API]]: Implementing this suggestion would require a change in the
@@ -516,7 +517,6 @@ public class Escalator extends Widget {
     }
 
     private static final int ROW_HEIGHT_PX = 20;
-    static final int COLUMN_WIDTH_PX = 100;
 
     /** An inner class that handles all logic related to scrolling. */
     private class Scroller extends JsniWorkaround {
@@ -577,11 +577,12 @@ public class Escalator extends Widget {
                     .setBottom(vScrollBottom, Unit.PX);
             verticalScrollbar.setScrollSize(innerScrollerHeight);
 
-            // TODO [[colwidth]]: adjust for variable column widths.
-            int columnsToScroll = columnConfiguration.getColumnCount()
-                    - columnConfiguration.getFrozenColumnCount();
-            horizontalScrollbar
-                    .setScrollSize(COLUMN_WIDTH_PX * columnsToScroll);
+            final Range unfrozenRange = Range.between(
+                    columnConfiguration.getFrozenColumnCount(),
+                    columnConfiguration.getColumnCount());
+            final int scrollwidth = columnConfiguration
+                    .getCalculatedColumnsWidth(unfrozenRange);
+            horizontalScrollbar.setScrollSize(scrollwidth);
 
             final Style hScrollbarStyle = horizontalScrollbar.getElement()
                     .getStyle();
@@ -600,8 +601,9 @@ public class Escalator extends Widget {
              * adjusted.
              */
             final int scrollPos = horizontalScrollbar.getScrollPos();
-            final int leftPos = getColumnConfiguration().getFrozenColumnCount()
-                    * COLUMN_WIDTH_PX;
+            final int leftPos = columnConfiguration
+                    .getCalculatedColumnsWidth(Range.withLength(0,
+                            columnConfiguration.frozenColumns));
             horizontalScrollbar.getElement().getStyle()
                     .setLeft(leftPos, Unit.PX);
             horizontalScrollbar.setScrollPos(scrollPos);
@@ -835,7 +837,6 @@ public class Escalator extends Widget {
                 final ScrollDestination destination, final int padding) {
             assert columnIndex >= columnConfiguration.frozenColumns : "Can't scroll to a frozen column";
 
-            // TODO [[colwidth]]
             /*
              * To cope with frozen columns, we just pretend those columns are
              * not there at all when calculating the position of the target
@@ -844,12 +845,15 @@ public class Escalator extends Widget {
              * structure effectively means that scrollLeft also ignores the
              * frozen columns.
              */
-            final int frozenPixels = columnConfiguration.frozenColumns
-                    * COLUMN_WIDTH_PX;
+            final int frozenPixels = columnConfiguration
+                    .getCalculatedColumnsWidth(Range.withLength(0,
+                            columnConfiguration.frozenColumns));
 
-            final int targetStartPx = COLUMN_WIDTH_PX * columnIndex
+            final int targetStartPx = columnConfiguration
+                    .getCalculatedColumnsWidth(Range.withLength(0, columnIndex))
                     - frozenPixels;
-            final int targetEndPx = targetStartPx + COLUMN_WIDTH_PX;
+            final int targetEndPx = targetStartPx
+                    + columnConfiguration.getColumnWidthActual(columnIndex);
 
             final int viewportStartPx = getScrollLeft();
             int viewportEndPx = viewportStartPx + getElement().getOffsetWidth()
@@ -1080,6 +1084,7 @@ public class Escalator extends Widget {
             }
 
             for (int row = visualIndex; row < visualIndex + numberOfRows; row++) {
+                final int rowHeight = ROW_HEIGHT_PX;
                 final Element tr = DOM.createTR();
                 addedRows.add(tr);
                 tr.addClassName(CLASS_NAME + "-row");
@@ -1087,7 +1092,10 @@ public class Escalator extends Widget {
                         referenceRow);
 
                 for (int col = 0; col < columnConfiguration.getColumnCount(); col++) {
-                    final Element cellElem = createCellElement();
+                    final int colWidth = columnConfiguration
+                            .getColumnWidthActual(col);
+                    final Element cellElem = createCellElement(rowHeight,
+                            colWidth);
                     tr.appendChild(cellElem);
 
                     // Set stylename and position if new cell is frozen
@@ -1098,15 +1106,8 @@ public class Escalator extends Widget {
                 }
 
                 refreshRow(tr, row);
-
-                /*
-                 * TODO [[optimize]] [[rowwidth]]: When this method is updated
-                 * to measure things instead of using hardcoded values, it would
-                 * be better to do everything at once after all rows have been
-                 * updated to reduce the number of reflows.
-                 */
-                recalculateRowWidth(tr);
             }
+            reapplyRowWidths();
 
             recalculateSectionHeight();
 
@@ -1172,11 +1173,6 @@ public class Escalator extends Widget {
                  * TODO [[rowheight]]: nudge rows down with
                  * refreshRowPositions() as needed
                  */
-                /*
-                 * TODO [[colwidth]]: reapply column and colspan widths as
-                 * needed
-                 */
-
                 for (int row = index; row < index + numberOfRows; row++) {
                     final Node tr = getTrByVisualIndex(row);
                     refreshRow(tr, row);
@@ -1187,7 +1183,8 @@ public class Escalator extends Widget {
         }
 
         void refreshRow(final Node tr, final int logicalRowIndex) {
-            flyweightRow.setup((Element) tr, logicalRowIndex);
+            flyweightRow.setup((Element) tr, logicalRowIndex,
+                    columnConfiguration.getCalculatedColumnWidths());
             updater.updateCells(flyweightRow, flyweightRow.getCells());
 
             /*
@@ -1200,12 +1197,18 @@ public class Escalator extends Widget {
         /**
          * Create and setup an empty cell element.
          * 
+         * @param width
+         *            the width of the cell, in pixels
+         * @param height
+         *            the height of the cell, in pixels
+         * 
          * @return a set-up empty cell element
          */
-        public Element createCellElement() {
+        @SuppressWarnings("hiding")
+        public Element createCellElement(final int height, final int width) {
             final Element cellElem = DOM.createElement(getCellElementTagName());
-            cellElem.getStyle().setHeight(ROW_HEIGHT_PX, Unit.PX);
-            cellElem.getStyle().setWidth(COLUMN_WIDTH_PX, Unit.PX);
+            cellElem.getStyle().setHeight(height, Unit.PX);
+            cellElem.getStyle().setWidth(width, Unit.PX);
             cellElem.addClassName(CLASS_NAME + "-cell");
             return cellElem;
         }
@@ -1225,7 +1228,8 @@ public class Escalator extends Widget {
         abstract protected int getTopVisualRowLogicalIndex();
 
         protected void paintRemoveColumns(final int offset,
-                final int numberOfColumns) {
+                final int numberOfColumns,
+                final List<ColumnConfigurationImpl.Column> removedColumns) {
             final NodeList<Node> childNodes = root.getChildNodes();
             for (int visualRowIndex = 0; visualRowIndex < childNodes
                     .getLength(); visualRowIndex++) {
@@ -1236,15 +1240,19 @@ public class Escalator extends Widget {
                     detachPossibleWidgetFromCell(cellElement);
                     cellElement.removeFromParent();
                 }
-                recalculateRowWidth((Element) tr);
             }
+            reapplyRowWidths();
 
-            final int firstRemovedColumnLeft = offset * COLUMN_WIDTH_PX;
+            final int firstRemovedColumnLeft = columnConfiguration
+                    .getCalculatedColumnsWidth(Range.withLength(0, offset));
             final boolean columnsWereRemovedFromLeftOfTheViewport = scroller.lastScrollLeft > firstRemovedColumnLeft;
 
             if (columnsWereRemovedFromLeftOfTheViewport) {
-                final int removedColumnsPxAmount = numberOfColumns
-                        * COLUMN_WIDTH_PX;
+                int removedColumnsPxAmount = 0;
+                for (ColumnConfigurationImpl.Column removedColumn : removedColumns) {
+                    removedColumnsPxAmount += removedColumn
+                            .getCalculatedWidth();
+                }
                 final int leftByDiff = (int) (scroller.lastScrollLeft - removedColumnsPxAmount);
                 final int newScrollLeft = Math.max(firstRemovedColumnLeft,
                         leftByDiff);
@@ -1283,6 +1291,7 @@ public class Escalator extends Widget {
             final NodeList<Node> childNodes = root.getChildNodes();
 
             for (int row = 0; row < childNodes.getLength(); row++) {
+                final int rowHeight = ROW_HEIGHT_PX;
                 final Element tr = getTrByVisualIndex(row);
 
                 Node referenceCell;
@@ -1293,19 +1302,15 @@ public class Escalator extends Widget {
                 }
 
                 for (int col = offset; col < offset + numberOfColumns; col++) {
-                    final Element cellElem = createCellElement();
+                    final int colWidth = columnConfiguration
+                            .getColumnWidthActual(col);
+                    final Element cellElem = createCellElement(rowHeight,
+                            colWidth);
                     referenceCell = insertAfterReferenceAndUpdateIt(tr,
                             cellElem, referenceCell);
                 }
-
-                /*
-                 * TODO [[optimize]] [[colwidth]]: When this method is updated
-                 * to measure things instead of using hardcoded values, it would
-                 * be better to do everything at once after all rows have been
-                 * updated to reduce the number of reflows.
-                 */
-                recalculateRowWidth(tr);
             }
+            reapplyRowWidths();
 
             if (frozen) {
                 for (int col = offset; col < offset + numberOfColumns; col++) {
@@ -1316,13 +1321,16 @@ public class Escalator extends Widget {
             // this needs to be before the scrollbar adjustment.
             scroller.recalculateScrollbarsForVirtualViewport();
 
-            final boolean columnsWereAddedToTheLeftOfViewport = scroller.lastScrollLeft > offset
-                    * COLUMN_WIDTH_PX;
+            int pixelsToInsertedColumn = columnConfiguration
+                    .getCalculatedColumnsWidth(Range.withLength(0, offset));
+            final boolean columnsWereAddedToTheLeftOfViewport = scroller.lastScrollLeft > pixelsToInsertedColumn;
 
             if (columnsWereAddedToTheLeftOfViewport) {
+                int insertedColumnsWidth = columnConfiguration
+                        .getCalculatedColumnsWidth(Range.withLength(offset,
+                                numberOfColumns));
                 horizontalScrollbar
-                        .setScrollPos((int) (scroller.lastScrollLeft + numberOfColumns
-                                * COLUMN_WIDTH_PX));
+                        .setScrollPos((int) (scroller.lastScrollLeft + insertedColumnsWidth));
             }
 
             /*
@@ -1367,6 +1375,92 @@ public class Escalator extends Widget {
                 position.set(cell, scrollLeft, 0);
             }
         }
+
+        /**
+         * Iterates through all the cells in a column and returns the width of
+         * the widest element in this RowContainer.
+         * 
+         * @param index
+         *            the index of the column to inspect
+         * @return the pixel width of the widest element in the indicated column
+         */
+        public int calculateMaxColWidth(int index) {
+            Element row = (Element) root.getFirstChildElement();
+            int maxWidth = 0;
+            while (row != null) {
+                final Element cell = (Element) row.getChild(index);
+                final boolean isVisible = !cell.getStyle().getDisplay()
+                        .equals(Display.NONE.getCssName());
+                if (isVisible) {
+                    maxWidth = Math.max(maxWidth, cell.getScrollWidth());
+                }
+                row = (Element) row.getNextSiblingElement();
+            }
+            return maxWidth;
+        }
+
+        /**
+         * Reapplies all the cells' widths according to the calculated widths in
+         * the column configuration.
+         */
+        public void reapplyColumnWidths() {
+            Element row = (Element) root.getFirstChildElement();
+            while (row != null) {
+                Element cell = (Element) row.getFirstChildElement();
+                int columnIndex = 0;
+                while (cell != null) {
+                    @SuppressWarnings("hiding")
+                    final int width = getCalculatedColumnWidthWithColspan(cell,
+                            columnIndex);
+
+                    /*
+                     * TODO Should Escalator implement ProvidesResize at some
+                     * point, this is where we need to do that.
+                     */
+                    cell.getStyle().setWidth(width, Unit.PX);
+
+                    cell = (Element) cell.getNextSiblingElement();
+                    columnIndex++;
+                }
+                row = (Element) row.getNextSiblingElement();
+            }
+
+            reapplyRowWidths();
+        }
+
+        private int getCalculatedColumnWidthWithColspan(final Element cell,
+                final int columnIndex) {
+            final int colspan = cell.getPropertyInt(FlyweightCell.COLSPAN_ATTR);
+            Range spannedColumns = Range.withLength(columnIndex, colspan);
+
+            /*
+             * Since browsers don't explode with overflowing colspans, escalator
+             * shouldn't either.
+             */
+            if (spannedColumns.getEnd() > columnConfiguration.getColumnCount()) {
+                spannedColumns = Range.between(columnIndex,
+                        columnConfiguration.getColumnCount());
+            }
+            return columnConfiguration
+                    .getCalculatedColumnsWidth(spannedColumns);
+        }
+
+        /**
+         * Applies the total length of the columns to each row element.
+         * <p>
+         * <em>Note:</em> In contrast to {@link #reapplyColumnWidths()}, this
+         * method only modifies the width of the {@code <tr>} element, not the
+         * cells within.
+         */
+        protected void reapplyRowWidths() {
+            int rowWidth = columnConfiguration.calculateRowWidth();
+
+            com.google.gwt.dom.client.Element row = root.getFirstChildElement();
+            while (row != null) {
+                row.getStyle().setWidth(rowWidth, Unit.PX);
+                row = row.getNextSiblingElement();
+            }
+        }
     }
 
     private abstract class AbstractStaticRowContainer extends
@@ -2488,9 +2582,36 @@ public class Escalator extends Widget {
     }
 
     private class ColumnConfigurationImpl implements ColumnConfiguration {
-        private int columns = 0;
+        public class Column {
+            private static final int DEFAULT_COLUMN_WIDTH_PX = 100;
+
+            private int definedWidth = -1;
+            private int calculatedWidth = DEFAULT_COLUMN_WIDTH_PX;
+
+            public void setWidth(int px) {
+                definedWidth = px;
+                calculatedWidth = (px >= 0) ? px : DEFAULT_COLUMN_WIDTH_PX;
+            }
+
+            public int getDefinedWidth() {
+                return definedWidth;
+            }
+
+            public int getCalculatedWidth() {
+                return calculatedWidth;
+            }
+        }
+
+        private final List<Column> columns = new ArrayList<Column>();
         private int frozenColumns = 0;
 
+        /**
+         * A cached array of all the calculated column widths.
+         * 
+         * @see #getCalculatedColumnWidths()
+         */
+        private int[] widthsArray = null;
+
         /**
          * {@inheritDoc}
          * <p>
@@ -2525,15 +2646,29 @@ public class Escalator extends Widget {
                 }
             }
 
-            columns -= numberOfColumns;
+            flyweightRow.removeCells(index, numberOfColumns);
+            List<Column> removedColumns = new ArrayList<Column>();
+            for (int i = 0; i < numberOfColumns; i++) {
+                removedColumns.add(columns.remove(i));
+            }
 
             if (hasSomethingInDom()) {
                 for (final AbstractRowContainer rowContainer : rowContainers) {
-                    rowContainer.paintRemoveColumns(index, numberOfColumns);
+                    rowContainer.paintRemoveColumns(index, numberOfColumns,
+                            removedColumns);
                 }
             }
         }
 
+        /**
+         * Calculate the width of a row, as the sum of columns' widths.
+         * 
+         * @return the width of a row, in pixels
+         */
+        public int calculateRowWidth() {
+            return getCalculatedColumnsWidth(Range.between(0, getColumnCount()));
+        }
+
         private void assertArgumentsAreValidAndWithinRange(final int index,
                 final int numberOfColumns) {
             if (numberOfColumns < 1) {
@@ -2575,7 +2710,10 @@ public class Escalator extends Widget {
             }
 
             flyweightRow.addCells(index, numberOfColumns);
-            columns += numberOfColumns;
+
+            for (int i = 0; i < numberOfColumns; i++) {
+                columns.add(index, new Column());
+            }
 
             // Either all or none of the new columns are frozen
             boolean frozen = index < frozenColumns;
@@ -2593,13 +2731,13 @@ public class Escalator extends Widget {
 
         @Override
         public int getColumnCount() {
-            return columns;
+            return columns.size();
         }
 
         @Override
         public void setFrozenColumnCount(int count)
                 throws IllegalArgumentException {
-            if (count < 0 || count > columns) {
+            if (count < 0 || count > getColumnCount()) {
                 throw new IllegalArgumentException(
                         "count must be between 0 and the current number of columns ("
                                 + columns + ")");
@@ -2640,6 +2778,85 @@ public class Escalator extends Widget {
         public int getFrozenColumnCount() {
             return frozenColumns;
         }
+
+        @Override
+        public void setColumnWidth(int index, int px)
+                throws IllegalArgumentException {
+            checkValidColumnIndex(index);
+
+            columns.get(index).setWidth(px);
+            widthsArray = null;
+
+            /*
+             * TODO [[optimize]]: only modify the elements that are actually
+             * modified.
+             */
+            header.reapplyColumnWidths();
+            body.reapplyColumnWidths();
+            footer.reapplyColumnWidths();
+            recalculateElementSizes();
+        }
+
+        private void checkValidColumnIndex(int index)
+                throws IllegalArgumentException {
+            if (!Range.withLength(0, getColumnCount()).contains(index)) {
+                throw new IllegalArgumentException(
+                        "The given column index does not exist");
+            }
+        }
+
+        @Override
+        public int getColumnWidth(int index) throws IllegalArgumentException {
+            checkValidColumnIndex(index);
+            return columns.get(index).getDefinedWidth();
+        }
+
+        @Override
+        public int getColumnWidthActual(int index) {
+            return columns.get(index).getCalculatedWidth();
+        }
+
+        /**
+         * Calculates the width of the columns in a given range.
+         * 
+         * @param columns
+         *            the columns to calculate
+         * @return the total width of the columns in the given
+         *         <code>columns</code>
+         */
+        int getCalculatedColumnsWidth(@SuppressWarnings("hiding")
+        final Range columns) {
+            /*
+             * This is an assert instead of an exception, since this is an
+             * internal method.
+             */
+            assert columns.isSubsetOf(Range.between(0, getColumnCount())) : "Range "
+                    + "was outside of current column range (i.e.: "
+                    + Range.between(0, getColumnCount())
+                    + ", but was given :"
+                    + columns;
+
+            int sum = 0;
+            for (int i = columns.getStart(); i < columns.getEnd(); i++) {
+                sum += getColumnWidthActual(i);
+            }
+            return sum;
+        }
+
+        void setCalculatedColumnWidth(int index, int width) {
+            columns.get(index).calculatedWidth = width;
+            widthsArray = null;
+        }
+
+        int[] getCalculatedColumnWidths() {
+            if (widthsArray == null) {
+                widthsArray = new int[getColumnCount()];
+                for (int i = 0; i < columns.size(); i++) {
+                    widthsArray[i] = columns.get(i).getCalculatedWidth();
+                }
+            }
+            return widthsArray;
+        }
     }
 
     // abs(atan(y/x))*(180/PI) = n deg, x = 1, solve y
@@ -3115,13 +3332,7 @@ public class Escalator extends Widget {
     }
 
     private boolean needsHorizontalScrollbars() {
-        // TODO [[colwidth]]: take variable column widths into account
-        return width < COLUMN_WIDTH_PX * columnConfiguration.getColumnCount();
-    }
-
-    private static void recalculateRowWidth(final Element tr) {
-        // TODO [[colwidth]]: adjust for variable column widths
-        tr.getStyle().setWidth(tr.getChildCount() * COLUMN_WIDTH_PX, Unit.PX);
+        return width < columnConfiguration.calculateRowWidth();
     }
 
     /**
@@ -3224,4 +3435,48 @@ public class Escalator extends Widget {
         }
         return null;
     }
+
+    /**
+     * Forces the escalator to recalculate the widths of its columns.
+     * <p>
+     * All columns that haven't been assigned an explicit width will be resized
+     * to fit all currently visible contents.
+     * 
+     * @see ColumnConfiguration#setColumnWidth(int, int)
+     */
+    public void calculateColumnWidths() {
+        boolean widthsHaveChanged = false;
+        for (int colIndex = 0; colIndex < columnConfiguration.getColumnCount(); colIndex++) {
+            if (columnConfiguration.getColumnWidth(colIndex) >= 0) {
+                continue;
+            }
+
+            final int oldColumnWidth = columnConfiguration
+                    .getColumnWidthActual(colIndex);
+
+            int maxColumnWidth = 0;
+            maxColumnWidth = Math.max(maxColumnWidth,
+                    header.calculateMaxColWidth(colIndex));
+            maxColumnWidth = Math.max(maxColumnWidth,
+                    body.calculateMaxColWidth(colIndex));
+            maxColumnWidth = Math.max(maxColumnWidth,
+                    footer.calculateMaxColWidth(colIndex));
+
+            Logger.getLogger("Escalator.calculateColumnWidths").info(
+                    "#" + colIndex + ": " + maxColumnWidth + "px");
+
+            if (oldColumnWidth != maxColumnWidth) {
+                columnConfiguration.setCalculatedColumnWidth(colIndex,
+                        maxColumnWidth);
+                widthsHaveChanged = true;
+            }
+        }
+
+        if (widthsHaveChanged) {
+            header.reapplyColumnWidths();
+            body.reapplyColumnWidths();
+            footer.reapplyColumnWidths();
+            recalculateElementSizes();
+        }
+    }
 }
index 3613e73affe7dc4016ea9f6fffd98454019e46f9..752b8f793f259c93f73d55b09a92538a8ef2f8bc 100644 (file)
@@ -38,7 +38,7 @@ import com.vaadin.client.ui.grid.FlyweightRow.CellIterator;
  * @see FlyweightRow#removeCells(int, int)
  */
 class FlyweightCell implements Cell {
-    private static final String COLSPAN_ATTR = "colSpan";
+    static final String COLSPAN_ATTR = "colSpan";
 
     private final int column;
     private final FlyweightRow row;
@@ -76,7 +76,7 @@ class FlyweightCell implements Cell {
 
         final Element e = getElement();
         e.setPropertyInt(COLSPAN_ATTR, 1);
-        e.getStyle().setWidth(Escalator.COLUMN_WIDTH_PX, Unit.PX);
+        e.getStyle().setWidth(row.getColumnWidth(column), Unit.PX);
         e.getStyle().clearDisplay();
     }
 
@@ -128,11 +128,14 @@ class FlyweightCell implements Cell {
     }
 
     private void adjustCellWidthForSpan(final int numberOfCells) {
-        final List<FlyweightCell> cellsToTheRight = currentIterator
-                .rawPeekNext(numberOfCells - 1);
-        final int widthsOfColumnsToTheRight = cellsToTheRight.size()
-                * Escalator.COLUMN_WIDTH_PX;
-        final int selfWidth = Escalator.COLUMN_WIDTH_PX;
+        final int cellsToTheRight = currentIterator.rawPeekNext(
+                numberOfCells - 1).size();
+
+        final int selfWidth = row.getColumnWidth(column);
+        int widthsOfColumnsToTheRight = 0;
+        for (int i = 0; i < cellsToTheRight; i++) {
+            widthsOfColumnsToTheRight += row.getColumnWidth(column + i + 1);
+        }
         getElement().getStyle().setWidth(selfWidth + widthsOfColumnsToTheRight,
                 Unit.PX);
     }
index c386b7dd1fbbd22a13ac668c5bed76ecba49c0eb..a9d638a7368478ea5d95c91132cfa222a345baff 100644 (file)
@@ -103,6 +103,7 @@ class FlyweightRow implements Row {
 
     private int row;
     private Element element;
+    private int[] columnWidths = null;
     private final Escalator escalator;
     private final List<FlyweightCell> cells = new ArrayList<FlyweightCell>();
 
@@ -115,9 +116,10 @@ class FlyweightRow implements Row {
         return escalator;
     }
 
-    void setup(final Element e, final int row) {
+    void setup(final Element e, final int row, int[] columnWidths) {
         element = e;
         this.row = row;
+        this.columnWidths = columnWidths;
     }
 
     /**
@@ -136,6 +138,7 @@ class FlyweightRow implements Row {
     boolean teardown() {
         element = null;
         row = BLANK;
+        columnWidths = null;
         for (final FlyweightCell cell : cells) {
             assert cell.teardown();
         }
@@ -200,10 +203,15 @@ class FlyweightRow implements Row {
      * access any of its data.
      */
     private void assertSetup() {
-        assert element != null && row != BLANK : "Flyweight row was not "
+        assert element != null && row != BLANK && columnWidths != null : "Flyweight row was not "
                 + "properly initialized. Make sure the setup-method is "
                 + "called before retrieving data. This is either a bug "
                 + "in Escalator, or the instance of the flyweight row "
                 + "has been stored and accessed.";
     }
+
+    int getColumnWidth(int column) {
+        assertSetup();
+        return columnWidths[column];
+    }
 }
index 2431fc815cf088ef0e34b1b82648074cf9f71146..fc9c217328d06a962988092c6e27c62c2d434232 100644 (file)
@@ -264,6 +264,31 @@ public class BasicEscalator extends AbstractTestUI {
                     }
                 }));
 
+        final Layout resizeColumnsLayout = new HorizontalLayout();
+        final TextField resizeColumnIndex = new TextField();
+        resizeColumnsLayout.addComponent(resizeColumnIndex);
+        final TextField resizeColumnPx = new TextField();
+        resizeColumnsLayout.addComponent(resizeColumnPx);
+        resizeColumnsLayout.addComponent(new Button("resize column",
+                new Button.ClickListener() {
+                    @Override
+                    public void buttonClick(final ClickEvent event) {
+                        final int index = Integer.parseInt(resizeColumnIndex
+                                .getValue());
+                        final int px = Integer.parseInt(resizeColumnPx
+                                .getValue());
+                        grid.setColumnWidth(index, px);
+                    }
+                }));
+        addComponent(resizeColumnsLayout);
+
+        addComponent(new Button("Autoresize columns",
+                new Button.ClickListener() {
+                    @Override
+                    public void buttonClick(ClickEvent event) {
+                        grid.calculateColumnWidths();
+                    }
+                }));
     }
 
     @Override
index 225cc34147b4d12c255702ccde927e763ff0be12..df2b8eb0751d7d6dee088a3ff21222f7d897d337 100644 (file)
@@ -39,4 +39,8 @@ public interface TestGridClientRpc extends ClientRpc {
     void insertFooters(int index, int amount);
 
     void removeFooters(int index, int amount);
+
+    void setColumnWidth(int index, int px);
+
+    void calculateColumnWidths();
 }
index b355c9e79ce741780e68436db04935c4d4f2bc8c..f9286091c00888bcf1e04a73867dd59bc784bd5f 100644 (file)
@@ -102,6 +102,16 @@ public class TestGridConnector extends AbstractComponentConnector {
             public void removeFooters(int index, int amount) {
                 getWidget().getFooter().removeRows(index, amount);
             }
+
+            @Override
+            public void setColumnWidth(int index, int px) {
+                getWidget().getColumnConfiguration().setColumnWidth(index, px);
+            }
+
+            @Override
+            public void calculateColumnWidths() {
+                getWidget().calculateColumnWidths();
+            }
         });
     }
 
index 18732549c0d3598aeb1342c22e9cf4fd081bd409..5c8dd4a6098588e0aeba4cbac9c27ff8ff9b4fdf 100644 (file)
@@ -217,4 +217,8 @@ public class VTestGrid extends Composite {
     public RowContainer getFooter() {
         return escalator.getFooter();
     }
+
+    public void calculateColumnWidths() {
+        escalator.calculateColumnWidths();
+    }
 }
index 2f14f21f0e44aee96f900ae8db4ec08d2ef70dbc..bdc5d46c1dd93b0dce736ada4f38730a7ee80e41 100644 (file)
@@ -81,4 +81,12 @@ public class TestGrid extends AbstractComponent {
     public void removeFooters(int index, int amount) {
         rpc().removeFooters(index, amount);
     }
+
+    public void setColumnWidth(int index, int px) {
+        rpc().setColumnWidth(index, px);
+    }
+
+    public void calculateColumnWidths() {
+        rpc().calculateColumnWidths();
+    }
 }