]> source.dussan.org Git - vaadin-framework.git/commitdiff
Merge of second part of (#10513) to Vaadin 7.
authorJohannes Dahlström <johannesd@vaadin.com>
Fri, 8 Feb 2013 15:20:04 +0000 (17:20 +0200)
committerJohannes Dahlström <johannesd@vaadin.com>
Mon, 18 Feb 2013 13:35:08 +0000 (15:35 +0200)
Fix for TreeTable's indent handling & width calculations.

Conflicts:
client/src/com/vaadin/client/ui/table/TableConnector.java

Change-Id: I94f0d84ac978045d3b79be6b3726ea6973d30b35

client/src/com/vaadin/client/ui/VScrollTable.java
client/src/com/vaadin/client/ui/VTreeTable.java
client/src/com/vaadin/client/ui/table/TableConnector.java
uitest/src/com/vaadin/tests/components/treetable/TreeTableExtraScrollBarWithChildren.html [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/treetable/TreeTableExtraScrollbarWithChildren.java [new file with mode: 0644]

index 75d67b82d66a562e7af57ed6327cb874fb28dd19..c76dd38d8fc01242ac63b20a45ec1f191471128a 100644 (file)
@@ -1191,6 +1191,39 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
         return totalRows;
     }
 
+    /**
+     * Returns the extra space that is given to the header column when column
+     * width is determined by header text.
+     * 
+     * @return extra space in pixels
+     */
+    private int getHeaderPadding() {
+        return scrollBody.getCellExtraWidth();
+    }
+
+    /**
+     * This method exists for the needs of {@link VTreeTable} only. Not part of
+     * the official API, <b>extend at your own risk</b>. May be removed or
+     * replaced in the future.
+     * 
+     * @return index of TreeTable's hierarchy column, or -1 if not applicable
+     */
+    protected int getHierarchyColumnIndex() {
+        return -1;
+    }
+
+    /**
+     * For internal use only. May be removed or replaced in the future.
+     */
+    public void updateMaxIndent() {
+        int oldIndent = scrollBody.getMaxIndent();
+        scrollBody.calculateMaxIndent();
+        if (oldIndent != scrollBody.getMaxIndent()) {
+            // indent updated, headers might need adjusting
+            triggerLazyColumnAdjustment(true);
+        }
+    }
+
     /** For internal use only. May be removed or replaced in the future. */
     public void focusRowFromBody() {
         if (selectedRowKeys.size() == 1) {
@@ -1382,6 +1415,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
      *            amount of rows in data set
      */
     public void updateBody(UIDL uidl, int firstRow, int reqRows) {
+        int oldIndent = scrollBody.getMaxIndent();
         if (uidl == null || reqRows < 1) {
             // container is empty, remove possibly existing rows
             if (firstRow <= 0) {
@@ -1399,6 +1433,11 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
         scrollBody.renderRows(uidl, firstRow, reqRows);
 
         discardRowsOutsideCacheWindow();
+        scrollBody.calculateMaxIndent();
+        if (oldIndent != scrollBody.getMaxIndent()) {
+            // indent updated, headers might need adjusting
+            headerChangedDuringUpdate = true;
+        }
     }
 
     /** For internal use only. May be removed or replaced in the future. */
@@ -1591,31 +1630,55 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
         return tHead.getHeaderCell(index).getColKey();
     }
 
-    private void setColWidth(int colIndex, int w, boolean isDefinedWidth) {
+    /**
+     * Note: not part of the official API, extend at your own risk. May be
+     * removed or replaced in the future.
+     * 
+     * Sets the indicated column's width for headers and scrollBody alike.
+     * 
+     * @param colIndex
+     *            index of the modified column
+     * @param w
+     *            new width (may be subject to modifications if doesn't meet
+     *            minimum requirements)
+     * @param isDefinedWidth
+     *            disables expand ratio if set true
+     */
+    protected void setColWidth(int colIndex, int w, boolean isDefinedWidth) {
         final HeaderCell hcell = tHead.getHeaderCell(colIndex);
 
         // Make sure that the column grows to accommodate the sort indicator if
         // necessary.
-        if (w < hcell.getMinWidth()) {
-            w = hcell.getMinWidth();
+        // get min width with no indent or padding
+        int minWidth = hcell.getMinWidth(false, false);
+        if (w < minWidth) {
+            w = minWidth;
         }
 
-        // Set header column width
+        // Set header column width WITHOUT INDENT
         hcell.setWidth(w, isDefinedWidth);
 
+        // Set footer column width likewise
+        FooterCell fcell = tFoot.getFooterCell(colIndex);
+        fcell.setWidth(w, isDefinedWidth);
+
         // Ensure indicators have been taken into account
         tHead.resizeCaptionContainer(hcell);
 
+        // Make sure that the body column grows to accommodate the indent if
+        // necessary.
+        // get min width with indent, no padding
+        minWidth = hcell.getMinWidth(true, false);
+        if (w < minWidth) {
+            w = minWidth;
+        }
+
         // Set body column width
         scrollBody.setColWidth(colIndex, w);
-
-        // Set footer column width
-        FooterCell fcell = tFoot.getFooterCell(colIndex);
-        fcell.setWidth(w, isDefinedWidth);
     }
 
     private int getColWidth(String colKey) {
-        return tHead.getHeaderCell(colKey).getWidth();
+        return tHead.getHeaderCell(colKey).getWidthWithIndent();
     }
 
     /**
@@ -1813,22 +1876,37 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
         tHead.enableBrowserIntelligence();
         tFoot.enableBrowserIntelligence();
 
+        int hierarchyColumnIndent = scrollBody != null ? scrollBody
+                .getMaxIndent() : 0;
+        HeaderCell hierarchyHeaderWithExpandRatio = null;
+
         // first loop: collect natural widths
         while (headCells.hasNext()) {
             final HeaderCell hCell = (HeaderCell) headCells.next();
             final FooterCell fCell = (FooterCell) footCells.next();
+            boolean needsIndent = hierarchyColumnIndent > 0
+                    && hCell.isHierarchyColumn();
             int w = hCell.getWidth();
             if (hCell.isDefinedWidth()) {
                 // server has defined column width explicitly
+                if (needsIndent && w < hierarchyColumnIndent) {
+                    // hierarchy indent overrides explicitly set width
+                    w = hierarchyColumnIndent;
+                }
                 totalExplicitColumnsWidths += w;
             } else {
                 if (hCell.getExpandRatio() > 0) {
                     expandRatioDivider += hCell.getExpandRatio();
                     w = 0;
+                    if (needsIndent && w < hierarchyColumnIndent) {
+                        hierarchyHeaderWithExpandRatio = hCell;
+                        // don't add to widths here, because will be included in
+                        // the expand ratio space if there's enough of it
+                    }
                 } else {
                     // get and store greater of header width and column width,
-                    // and
-                    // store it as a minimumn natural col width
+                    // and store it as a minimum natural column width (these
+                    // already contain the indent if any)
                     int headerWidth = hCell.getNaturalColumnWidth(i);
                     int footerWidth = fCell.getNaturalColumnWidth(i);
                     w = headerWidth > footerWidth ? headerWidth : footerWidth;
@@ -1840,6 +1918,9 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
             total += w;
             i++;
         }
+        if (hierarchyHeaderWithExpandRatio != null) {
+            total += hierarchyColumnIndent;
+        }
 
         tHead.disableBrowserIntelligence();
         tFoot.disableBrowserIntelligence();
@@ -1871,13 +1952,24 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
 
         if (availW > total) {
             // natural size is smaller than available space
-            final int extraSpace = availW - total;
+            int extraSpace = availW - total;
+            if (hierarchyHeaderWithExpandRatio != null) {
+                /*
+                 * add the indent's space back to ensure each column gets an
+                 * even share according to the expand ratios (note: if the
+                 * allocated space isn't enough for the hierarchy column it
+                 * shall be treated like a defined width column and the indent
+                 * space gets removed from the extra space again)
+                 */
+                extraSpace += hierarchyColumnIndent;
+            }
             final int totalWidthR = total - totalExplicitColumnsWidths;
             int checksum = 0;
 
             if (extraSpace == 1) {
                 // We cannot divide one single pixel so we give it the first
                 // undefined column
+                // no need to worry about indent here
                 headCells = tHead.iterator();
                 i = 0;
                 checksum = availW;
@@ -1891,6 +1983,22 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
                 }
 
             } else if (expandRatioDivider > 0) {
+                boolean setIndentToHierarchyHeader = false;
+                if (hierarchyHeaderWithExpandRatio != null) {
+                    // ensure first that the hierarchyColumn gets at least the
+                    // space allocated for indent
+                    final int newSpace = Math
+                            .round((extraSpace * (hierarchyHeaderWithExpandRatio
+                                    .getExpandRatio() / expandRatioDivider)));
+                    if (newSpace < hierarchyColumnIndent) {
+                        // not enough space for indent, remove indent from the
+                        // extraSpace again and handle hierarchy column's header
+                        // separately
+                        setIndentToHierarchyHeader = true;
+                        extraSpace -= hierarchyColumnIndent;
+                    }
+                }
+
                 // visible columns have some active expand ratios, excess
                 // space is divided according to them
                 headCells = tHead.iterator();
@@ -1899,9 +2007,17 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
                     HeaderCell hCell = (HeaderCell) headCells.next();
                     if (hCell.getExpandRatio() > 0) {
                         int w = widths[i];
-                        final int newSpace = Math.round((extraSpace * (hCell
-                                .getExpandRatio() / expandRatioDivider)));
-                        w += newSpace;
+                        if (setIndentToHierarchyHeader
+                                && hierarchyHeaderWithExpandRatio.equals(hCell)) {
+                            // hierarchy column's header is no longer part of
+                            // the expansion divide and only gets indent
+                            w += hierarchyColumnIndent;
+                        } else {
+                            final int newSpace = Math
+                                    .round((extraSpace * (hCell
+                                            .getExpandRatio() / expandRatioDivider)));
+                            w += newSpace;
+                        }
                         widths[i] = w;
                     }
                     checksum += widths[i];
@@ -1911,6 +2027,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
                 // no expand ratios defined, we will share extra space
                 // relatively to "natural widths" among those without
                 // explicit width
+                // no need to worry about indent here, it's already included
                 headCells = tHead.iterator();
                 i = 0;
                 while (headCells.hasNext()) {
@@ -1946,7 +2063,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
             }
 
         } else {
-            // bodys size will be more than available and scrollbar will appear
+            // body's size will be more than available and scrollbar will appear
         }
 
         // last loop: set possibly modified values or reset if new tBody
@@ -2046,8 +2163,8 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
     }
 
     /**
-     * Note, this method is not official api although declared as protected.
-     * Extend at you own risk.
+     * Note: this method is not part of official API although declared as
+     * protected. Extend at your own risk.
      * 
      * @return true if content area will have scrollbars visible.
      */
@@ -2430,6 +2547,16 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
             expandRatio = 0;
         }
 
+        /**
+         * Sets width to the header cell. This width should not include any
+         * possible indent modifications that are present in
+         * {@link VScrollTableBody#getMaxIndent()}.
+         * 
+         * @param w
+         *            required width of the cell sans indentations
+         * @param ensureDefinedWidth
+         *            disables expand ratio if required
+         */
         public void setWidth(int w, boolean ensureDefinedWidth) {
             if (ensureDefinedWidth) {
                 definedWidth = true;
@@ -2453,15 +2580,23 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
                  * unless TD width is not explicitly set.
                  */
                 if (scrollBody != null) {
-                    int tdWidth = width + scrollBody.getCellExtraWidth();
+                    int maxIndent = scrollBody.getMaxIndent();
+                    if (w < maxIndent && isHierarchyColumn()) {
+                        w = maxIndent;
+                    }
+                    int tdWidth = w + scrollBody.getCellExtraWidth();
                     setWidth(tdWidth + "px");
                 } else {
                     Scheduler.get().scheduleDeferred(new Command() {
 
                         @Override
                         public void execute() {
-                            int tdWidth = width
-                                    + scrollBody.getCellExtraWidth();
+                            int maxIndent = scrollBody.getMaxIndent();
+                            int tdWidth = width;
+                            if (tdWidth < maxIndent && isHierarchyColumn()) {
+                                tdWidth = maxIndent;
+                            }
+                            tdWidth += scrollBody.getCellExtraWidth();
                             setWidth(tdWidth + "px");
                         }
                     });
@@ -2484,10 +2619,45 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
             return definedWidth && width >= 0;
         }
 
+        /**
+         * This method exists for the needs of {@link VTreeTable} only.
+         * 
+         * Returns the pixels width of the header cell. This includes the
+         * indent, if applicable.
+         * 
+         * @return The width in pixels
+         */
+        protected int getWidthWithIndent() {
+            if (scrollBody != null && isHierarchyColumn()) {
+                int maxIndent = scrollBody.getMaxIndent();
+                if (maxIndent > width) {
+                    return maxIndent;
+                }
+            }
+            return width;
+        }
+
+        /**
+         * Returns the pixels width of the header cell.
+         * 
+         * @return The width in pixels
+         */
         public int getWidth() {
             return width;
         }
 
+        /**
+         * This method exists for the needs of {@link VTreeTable} only.
+         * 
+         * @return <code>true</code> if this is hierarcyColumn's header cell,
+         *         <code>false</code> otherwise
+         */
+        private boolean isHierarchyColumn() {
+            int hierarchyColumnIndex = getHierarchyColumnIndex();
+            return hierarchyColumnIndex >= 0
+                    && tHead.visibleCells.indexOf(this) == hierarchyColumnIndex;
+        }
+
         public void setText(String headerText) {
             DOM.setInnerHTML(captionContainer, headerText);
         }
@@ -2742,7 +2912,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
                 DOM.setCapture(getElement());
                 dragStartX = DOM.eventGetClientX(event);
                 colIndex = getColIndexByKey(cid);
-                originalWidth = getWidth();
+                originalWidth = getWidthWithIndent();
                 DOM.eventPreventDefault(event);
                 break;
             case Event.ONMOUSEUP:
@@ -2774,8 +2944,11 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
                     tHead.disableAutoColumnWidthCalculation(this);
 
                     int newWidth = originalWidth + deltaX;
-                    if (newWidth < getMinWidth()) {
-                        newWidth = getMinWidth();
+                    // get min width with indent, no padding
+                    int minWidth = getMinWidth(true, false);
+                    if (newWidth < minWidth) {
+                        // already includes indent if any
+                        newWidth = minWidth;
                     }
                     setColWidth(colIndex, newWidth, true);
                     triggerLazyColumnAdjustment(false);
@@ -2787,12 +2960,37 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
             }
         }
 
-        public int getMinWidth() {
-            int cellExtraWidth = 0;
+        /**
+         * Returns the smallest possible cell width in pixels.
+         * 
+         * @param includeIndent
+         *            - width should include hierarchy column indent if
+         *            applicable (VTreeTable only)
+         * @param includeCellExtraWidth
+         *            - width should include paddings etc.
+         * @return
+         */
+        private int getMinWidth(boolean includeIndent,
+                boolean includeCellExtraWidth) {
+            int minWidth = sortIndicator.getOffsetWidth();
             if (scrollBody != null) {
-                cellExtraWidth += scrollBody.getCellExtraWidth();
+                // check the need for indent before adding paddings etc.
+                if (includeIndent && isHierarchyColumn()) {
+                    int maxIndent = scrollBody.getMaxIndent();
+                    if (minWidth < maxIndent) {
+                        minWidth = maxIndent;
+                    }
+                }
+                if (includeCellExtraWidth) {
+                    minWidth += scrollBody.getCellExtraWidth();
+                }
             }
-            return cellExtraWidth + sortIndicator.getOffsetWidth();
+            return minWidth;
+        }
+
+        public int getMinWidth() {
+            // get min width with padding, no indent
+            return getMinWidth(false, true);
         }
 
         public String getCaption() {
@@ -2823,16 +3021,20 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
          * @return
          */
         public int getNaturalColumnWidth(int columnIndex) {
+            final int iw = columnIndex == getHierarchyColumnIndex() ? scrollBody
+                    .getMaxIndent() : 0;
             if (isDefinedWidth()) {
+                if (iw > width) {
+                    return iw;
+                }
                 return width;
             } else {
                 if (naturalWidth < 0) {
                     // This is recently revealed column. Try to detect a proper
-                    // value (greater of header and data
-                    // cols)
+                    // value (greater of header and data columns)
 
                     int hw = captionContainer.getOffsetWidth()
-                            + scrollBody.getCellExtraWidth();
+                            + getHeaderPadding();
                     if (BrowserInfo.get().isGecko()) {
                         hw += sortIndicator.getOffsetWidth();
                     }
@@ -2848,7 +3050,13 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
                     final int cw = scrollBody.getColWidth(columnIndex);
                     naturalWidth = (hw > cw ? hw : cw);
                 }
-                return naturalWidth;
+                if (iw > naturalWidth) {
+                    // indent is temporary value, naturalWidth shouldn't be
+                    // updated
+                    return iw;
+                } else {
+                    return naturalWidth;
+                }
             }
         }
 
@@ -2945,32 +3153,49 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
 
         public void resizeCaptionContainer(HeaderCell cell) {
             HeaderCell lastcell = getHeaderCell(visibleCells.size() - 1);
+            int columnSelectorOffset = columnSelector.getOffsetWidth();
 
-            // Measure column widths
-            int columnTotalWidth = 0;
-            for (Widget w : visibleCells) {
-                columnTotalWidth += w.getOffsetWidth();
-            }
-
-            if (cell == lastcell
-                    && columnSelector.getOffsetWidth() > 0
-                    && columnTotalWidth >= div.getOffsetWidth()
-                            - columnSelector.getOffsetWidth()
+            if (cell == lastcell && columnSelectorOffset > 0
                     && !hasVerticalScrollbar()) {
-                // Ensure column caption is visible when placed under the column
-                // selector widget by shifting and resizing the caption.
-                int offset = 0;
-                int diff = div.getOffsetWidth() - columnTotalWidth;
-                if (diff < columnSelector.getOffsetWidth() && diff > 0) {
-                    // If the difference is less than the column selectors width
-                    // then just offset by the
-                    // difference
-                    offset = columnSelector.getOffsetWidth() - diff;
+
+                // Measure column widths
+                int columnTotalWidth = 0;
+                for (Widget w : visibleCells) {
+                    int cellExtraWidth = w.getOffsetWidth();
+                    if (scrollBody != null
+                            && visibleCells.indexOf(w) == getHierarchyColumnIndex()
+                            && cellExtraWidth < scrollBody.getMaxIndent()) {
+                        // indent must be taken into consideration even if it
+                        // hasn't been applied yet
+                        columnTotalWidth += scrollBody.getMaxIndent();
+                    } else {
+                        columnTotalWidth += cellExtraWidth;
+                    }
+                }
+
+                int divOffset = div.getOffsetWidth();
+                if (columnTotalWidth >= divOffset - columnSelectorOffset) {
+                    /*
+                     * Ensure column caption is visible when placed under the
+                     * column selector widget by shifting and resizing the
+                     * caption.
+                     */
+                    int offset = 0;
+                    int diff = divOffset - columnTotalWidth;
+                    if (diff < columnSelectorOffset && diff > 0) {
+                        /*
+                         * If the difference is less than the column selectors
+                         * width then just offset by the difference
+                         */
+                        offset = columnSelectorOffset - diff;
+                    } else {
+                        // Else offset by the whole column selector
+                        offset = columnSelectorOffset;
+                    }
+                    lastcell.resizeCaptionContainer(offset);
                 } else {
-                    // Else offset by the whole column selector
-                    offset = columnSelector.getOffsetWidth();
+                    cell.resizeCaptionContainer(0);
                 }
-                lastcell.resizeCaptionContainer(offset);
             } else {
                 cell.resizeCaptionContainer(0);
             }
@@ -3033,10 +3258,14 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
                     // Make sure to accomodate for the sort indicator if
                     // necessary.
                     int width = Integer.parseInt(widthStr);
-                    if (width < c.getMinWidth()) {
-                        width = c.getMinWidth();
+                    int widthWithoutAddedIndent = width;
+
+                    // get min width with indent, no padding
+                    int minWidth = c.getMinWidth(true, false);
+                    if (width < minWidth) {
+                        width = minWidth;
                     }
-                    if (width != c.getWidth() && scrollBody != null) {
+                    if (scrollBody != null && width != c.getWidthWithIndent()) {
                         // Do a more thorough update if a column is resized from
                         // the server *after* the header has been properly
                         // initialized
@@ -3052,7 +3281,13 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
                                 });
                         refreshContentWidths = true;
                     } else {
-                        c.setWidth(width, true);
+                        // get min width with no indent or padding
+                        minWidth = c.getMinWidth(false, false);
+                        if (widthWithoutAddedIndent < minWidth) {
+                            widthWithoutAddedIndent = minWidth;
+                        }
+                        // save min width without indent
+                        c.setWidth(widthWithoutAddedIndent, true);
                     }
                 } else if (recalcWidths) {
                     c.setUndefinedWidth();
@@ -3507,12 +3742,14 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
         }
 
         /**
-         * Sets the width of the cell
+         * Sets the width of the cell. This width should not include any
+         * possible indent modifications that are present in
+         * {@link VScrollTableBody#getMaxIndent()}.
          * 
          * @param w
          *            The width of the cell
          * @param ensureDefinedWidth
-         *            Ensures the the given width is not recalculated
+         *            Ensures that the given width is not recalculated
          */
         public void setWidth(int w, boolean ensureDefinedWidth) {
 
@@ -3549,7 +3786,13 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
                  * unless TD width is not explicitly set.
                  */
                 if (scrollBody != null) {
-                    int tdWidth = width + scrollBody.getCellExtraWidth()
+                    int maxIndent = scrollBody.getMaxIndent();
+                    if (w < maxIndent
+                            && tFoot.visibleCells.indexOf(this) == getHierarchyColumnIndex()) {
+                        // ensure there's room for the indent
+                        w = maxIndent;
+                    }
+                    int tdWidth = w + scrollBody.getCellExtraWidth()
                             - borderWidths;
                     setWidth(Math.max(tdWidth, 0) + "px");
                 } else {
@@ -3557,8 +3800,14 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
 
                         @Override
                         public void execute() {
-                            int tdWidth = width
-                                    + scrollBody.getCellExtraWidth()
+                            int tdWidth = width;
+                            int maxIndent = scrollBody.getMaxIndent();
+                            if (tdWidth < maxIndent
+                                    && tFoot.visibleCells.indexOf(this) == getHierarchyColumnIndex()) {
+                                // ensure there's room for the indent
+                                tdWidth = maxIndent;
+                            }
+                            tdWidth += scrollBody.getCellExtraWidth()
                                     - borderWidths;
                             setWidth(Math.max(tdWidth, 0) + "px");
                         }
@@ -3571,6 +3820,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
          * Sets the width to undefined
          */
         public void setUndefinedWidth() {
+            definedWidth = false;
             setWidth(-1, false);
         }
 
@@ -3585,7 +3835,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
         }
 
         /**
-         * Returns the pixels width of the footer cell
+         * Returns the pixels width of the footer cell.
          * 
          * @return The width in pixels
          */
@@ -3699,7 +3949,12 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
          * @return
          */
         public int getNaturalColumnWidth(int columnIndex) {
+            final int iw = columnIndex == getHierarchyColumnIndex() ? scrollBody
+                    .getMaxIndent() : 0;
             if (isDefinedWidth()) {
+                if (iw > width) {
+                    return iw;
+                }
                 return width;
             } else {
                 if (naturalWidth < 0) {
@@ -3708,7 +3963,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
                     // cols)
 
                     final int hw = ((Element) getElement().getLastChild())
-                            .getOffsetWidth() + scrollBody.getCellExtraWidth();
+                            .getOffsetWidth() + getHeaderPadding();
                     if (columnIndex < 0) {
                         columnIndex = 0;
                         for (Iterator<Widget> it = tHead.iterator(); it
@@ -3721,7 +3976,11 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
                     final int cw = scrollBody.getColWidth(columnIndex);
                     naturalWidth = (hw > cw ? hw : cw);
                 }
-                return naturalWidth;
+                if (iw > naturalWidth) {
+                    return iw;
+                } else {
+                    return naturalWidth;
+                }
             }
         }
 
@@ -4627,6 +4886,28 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
             return cellExtraWidth;
         }
 
+        /**
+         * This method exists for the needs of {@link VTreeTable} only. May be
+         * removed or replaced in the future.</br> </br> Returns the maximum
+         * indent of the hierarcyColumn, if applicable.
+         * 
+         * @see {@link VScrollTable#getHierarchyColumnIndex()}
+         * 
+         * @return maximum indent in pixels
+         */
+        protected int getMaxIndent() {
+            return 0;
+        }
+
+        /**
+         * This method exists for the needs of {@link VTreeTable} only. May be
+         * removed or replaced in the future.</br> </br> Calculates the maximum
+         * indent of the hierarcyColumn, if applicable.
+         */
+        protected void calculateMaxIndent() {
+            // NOP
+        }
+
         private void detectExtrawidth() {
             NodeList<TableRowElement> rows = tBodyElement.getRows();
             if (rows.getLength() == 0) {
@@ -4806,8 +5087,23 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
 
             protected void setCellWidth(int cellIx, int width) {
                 final Element cell = DOM.getChild(getElement(), cellIx);
-                cell.getFirstChildElement().getStyle()
-                        .setPropertyPx("width", width);
+                Style wrapperStyle = cell.getFirstChildElement().getStyle();
+                int wrapperWidth = width;
+                if (BrowserInfo.get().isWebkit()
+                        || BrowserInfo.get().isOpera10()) {
+                    /*
+                     * Some versions of Webkit and Opera ignore the width
+                     * definition of zero width table cells. Instead, use 1px
+                     * and compensate with a negative margin.
+                     */
+                    if (width == 0) {
+                        wrapperWidth = 1;
+                        wrapperStyle.setMarginRight(-1, Unit.PX);
+                    } else {
+                        wrapperStyle.clearMarginRight();
+                    }
+                }
+                wrapperStyle.setPropertyPx("width", wrapperWidth);
                 cell.getStyle().setPropertyPx("width", width);
             }
 
@@ -5866,7 +6162,8 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
                     @Override
                     public void execute() {
                         if (showRowHeaders) {
-                            setCellWidth(0, tHead.getHeaderCell(0).getWidth());
+                            setCellWidth(0, tHead.getHeaderCell(0)
+                                    .getWidthWithIndent());
                             calcAndSetSpanWidthOnCell(1);
                         } else {
                             calcAndSetSpanWidthOnCell(0);
@@ -6100,14 +6397,35 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
             int totalExplicitColumnsWidths = 0;
             float expandRatioDivider = 0;
             int colIndex = 0;
+
+            int hierarchyColumnIndent = scrollBody.getMaxIndent();
+            int hierarchyColumnIndex = getHierarchyColumnIndex();
+            HeaderCell hierarchyHeaderInNeedOfFurtherHandling = null;
+
             while (headCells.hasNext()) {
                 final HeaderCell hCell = (HeaderCell) headCells.next();
+                boolean hasIndent = hierarchyColumnIndent > 0
+                        && hCell.isHierarchyColumn();
                 if (hCell.isDefinedWidth()) {
-                    totalExplicitColumnsWidths += hCell.getWidth();
-                    usedMinimumWidth += hCell.getWidth();
+                    // get width without indent to find out whether adjustments
+                    // are needed (requires special handling further ahead)
+                    int w = hCell.getWidth();
+                    if (hasIndent && w < hierarchyColumnIndent) {
+                        // enforce indent if necessary
+                        w = hierarchyColumnIndent;
+                        hierarchyHeaderInNeedOfFurtherHandling = hCell;
+                    }
+                    totalExplicitColumnsWidths += w;
+                    usedMinimumWidth += w;
                 } else {
-                    usedMinimumWidth += hCell.getNaturalColumnWidth(colIndex);
+                    // natural width already includes indent if any
+                    int naturalColumnWidth = hCell
+                            .getNaturalColumnWidth(colIndex);
+                    usedMinimumWidth += naturalColumnWidth;
                     expandRatioDivider += hCell.getExpandRatio();
+                    if (hasIndent) {
+                        hierarchyHeaderInNeedOfFurtherHandling = hCell;
+                    }
                 }
                 colIndex++;
             }
@@ -6152,6 +6470,28 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
             int totalUndefinedNaturalWidths = usedMinimumWidth
                     - totalExplicitColumnsWidths;
 
+            if (hierarchyHeaderInNeedOfFurtherHandling != null
+                    && !hierarchyHeaderInNeedOfFurtherHandling.isDefinedWidth()) {
+                // ensure the cell gets enough space for the indent
+                int w = hierarchyHeaderInNeedOfFurtherHandling
+                        .getNaturalColumnWidth(hierarchyColumnIndex);
+                int newSpace = Math.round(w + (float) extraSpace * (float) w
+                        / totalUndefinedNaturalWidths);
+                if (newSpace >= hierarchyColumnIndent) {
+                    // no special handling required
+                    hierarchyHeaderInNeedOfFurtherHandling = null;
+                } else {
+                    // treat as a defined width column of indent's width
+                    totalExplicitColumnsWidths += hierarchyColumnIndent;
+                    usedMinimumWidth -= w - hierarchyColumnIndent;
+                    totalUndefinedNaturalWidths = usedMinimumWidth
+                            - totalExplicitColumnsWidths;
+                    expandRatioDivider += hierarchyHeaderInNeedOfFurtherHandling
+                            .getExpandRatio();
+                    extraSpace = Math.max(availW - usedMinimumWidth, 0);
+                }
+            }
+
             // we have some space that can be divided optimally
             HeaderCell hCell;
             colIndex = 0;
@@ -6167,7 +6507,10 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
                         newSpace = Math.round((w + extraSpace
                                 * hCell.getExpandRatio() / expandRatioDivider));
                     } else {
-                        if (totalUndefinedNaturalWidths != 0) {
+                        if (hierarchyHeaderInNeedOfFurtherHandling == hCell) {
+                            // still exists, so needs exactly the indent's width
+                            newSpace = hierarchyColumnIndent;
+                        } else if (totalUndefinedNaturalWidths != 0) {
                             // divide relatively to natural column widths
                             newSpace = Math.round(w + (float) extraSpace
                                     * (float) w / totalUndefinedNaturalWidths);
@@ -6177,8 +6520,21 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
                     }
                     checksum += newSpace;
                     setColWidth(colIndex, newSpace, false);
+
                 } else {
-                    checksum += hCell.getWidth();
+                    if (hierarchyHeaderInNeedOfFurtherHandling == hCell) {
+                        // defined with enforced into indent width
+                        checksum += hierarchyColumnIndent;
+                        setColWidth(colIndex, hierarchyColumnIndent, false);
+                    } else {
+                        int cellWidth = hCell.getWidthWithIndent();
+                        checksum += cellWidth;
+                        if (hCell.isHierarchyColumn()) {
+                            // update in case the indent has changed
+                            // (not detectable earlier)
+                            setColWidth(colIndex, cellWidth, true);
+                        }
+                    }
                 }
                 colIndex++;
             }
@@ -6194,8 +6550,8 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
                 while (headCells.hasNext()) {
                     HeaderCell hc = (HeaderCell) headCells.next();
                     if (!hc.isDefinedWidth()) {
-                        setColWidth(colIndex,
-                                hc.getWidth() + availW - checksum, false);
+                        setColWidth(colIndex, hc.getWidthWithIndent() + availW
+                                - checksum, false);
                         break;
                     }
                     colIndex++;
index f65198865ccf37808b41bd31694e03257832f638..097b9c7ab2f208f87fcbffbedf89b8ef2e4871c8 100644 (file)
@@ -122,8 +122,14 @@ public class VTreeTable extends VScrollTable {
         }
     }
 
+    @Override
+    protected int getHierarchyColumnIndex() {
+        return colIndexOfHierarchy + (showRowHeaders ? 1 : 0);
+    }
+
     public class VTreeTableScrollBody extends VScrollTable.VScrollTableBody {
         private int indentWidth = -1;
+        private int maxIndent = 0;
 
         VTreeTableScrollBody() {
             super();
@@ -232,6 +238,11 @@ public class VTreeTable extends VScrollTable {
                     treeSpacer.getParentElement().getStyle()
                             .setPaddingLeft(getIndent(), Unit.PX);
                     treeSpacer.getStyle().setWidth(getIndent(), Unit.PX);
+                    int colWidth = getColWidth(getHierarchyColumnIndex());
+                    if (colWidth > 0 && getIndent() > colWidth) {
+                        VTreeTable.this.setColWidth(getHierarchyColumnIndex(),
+                                getIndent(), false);
+                    }
                 }
             }
 
@@ -277,16 +288,12 @@ public class VTreeTable extends VScrollTable {
                     // hierarchy column
                     int indent = getIndent();
                     if (indent != -1) {
-                        width = Math.max(width - getIndent(), 0);
+                        width = Math.max(width - indent, 0);
                     }
                 }
                 super.setCellWidth(cellIx, width);
             }
 
-            private int getHierarchyColumnIndex() {
-                return colIndexOfHierarchy + (showRowHeaders ? 1 : 0);
-            }
-
             private int getIndent() {
                 return (depth + 1) * getIndentWidth();
             }
@@ -323,7 +330,8 @@ public class VTreeTable extends VScrollTable {
                     @Override
                     public void execute() {
                         if (showRowHeaders) {
-                            setCellWidth(0, tHead.getHeaderCell(0).getWidth());
+                            setCellWidth(0, tHead.getHeaderCell(0)
+                                    .getWidthWithIndent());
                             calcAndSetSpanWidthOnCell(1);
                         } else {
                             calcAndSetSpanWidthOnCell(0);
@@ -421,6 +429,22 @@ public class VTreeTable extends VScrollTable {
             return indentWidth;
         }
 
+        @Override
+        protected int getMaxIndent() {
+            return maxIndent;
+        }
+
+        @Override
+        protected void calculateMaxIndent() {
+            int maxIndent = 0;
+            Iterator<Widget> iterator = iterator();
+            while (iterator.hasNext()) {
+                VTreeTableRow next = (VTreeTableRow) iterator.next();
+                maxIndent = Math.max(maxIndent, next.getIndent());
+            }
+            this.maxIndent = maxIndent;
+        }
+
         private void detectIndent(VTreeTableRow vTreeTableRow) {
             indentWidth = vTreeTableRow.treeSpacer.getOffsetWidth();
             if (indentWidth == 0) {
@@ -432,6 +456,7 @@ public class VTreeTable extends VScrollTable {
                 VTreeTableRow next = (VTreeTableRow) iterator.next();
                 next.setIndent();
             }
+            calculateMaxIndent();
         }
 
         protected void unlinkRowsAnimatedAndUpdateCacheWhenFinished(
@@ -471,6 +496,7 @@ public class VTreeTable extends VScrollTable {
                 RowExpandAnimation anim = new RowExpandAnimation(insertedRows);
                 anim.run(150);
             }
+            scrollBody.calculateMaxIndent();
             return insertedRows;
         }
 
index c967642059805303428413e119adc39ce30d7a3d..fc31cdf8eae26521ba39f4c68d36950e3f5733c6 100644 (file)
@@ -176,6 +176,7 @@ public class TableConnector extends AbstractHasComponentsConnector implements
             // amount of rows)
             getWidget().scrollBody.setLastRendered(getWidget().scrollBody
                     .getLastRendered());
+            getWidget().updateMaxIndent();
         } else {
             getWidget().postponeSanityCheckForLastRendered = false;
             UIDL rowData = uidl.getChildByTagName("rows");
diff --git a/uitest/src/com/vaadin/tests/components/treetable/TreeTableExtraScrollBarWithChildren.html b/uitest/src/com/vaadin/tests/components/treetable/TreeTableExtraScrollBarWithChildren.html
new file mode 100644 (file)
index 0000000..611e7a5
--- /dev/null
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="" />
+<title>TreeTableExtraScrollbar</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">TreeTableExtraScrollbar</td></tr>
+</thead><tbody>
+<tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.components.treetable.TreeTableExtraScrollbarWithChildren?restartApplication</td>
+       <td></td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>button</td>
+       <td></td>
+</tr>
+<tr>
+    <td>pause</td>
+    <td>300</td>
+    <td></td>
+</tr>
+<!-- screenCapture should not have a horizontal scrollbar, and the columns should be the same width in body and headers -->
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td>expanded</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>button</td>
+       <td></td>
+</tr>
+<tr>
+    <td>pause</td>
+    <td>300</td>
+    <td></td>
+</tr>
+<!-- screenCapture should not have a horizontal scrollbar, the columns should be back to original width in body and headers, and vertical scrollbar shouldn't have left an empty space -->
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td>collapsed</td>
+</tr>
+<tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.components.treetable.TreeTableExtraScrollbarWithChildren</td>
+       <td></td>
+</tr>
+<tr>
+    <td>pause</td>
+    <td>300</td>
+    <td></td>
+</tr>
+<!-- screenCapture should not have changed -->
+<tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td>refreshed</td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/treetable/TreeTableExtraScrollbarWithChildren.java b/uitest/src/com/vaadin/tests/components/treetable/TreeTableExtraScrollbarWithChildren.java
new file mode 100644 (file)
index 0000000..cad33e2
--- /dev/null
@@ -0,0 +1,92 @@
+package com.vaadin.tests.components.treetable;
+
+import com.vaadin.tests.components.TestBase;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Button.ClickListener;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.Notification;
+import com.vaadin.ui.Table;
+import com.vaadin.ui.TreeTable;
+
+public class TreeTableExtraScrollbarWithChildren extends TestBase {
+
+    @Override
+    protected String getDescription() {
+        return "Arrow calculation should not cause a horizontal scrollbar"
+                + " if there is enough space for the final contents.";
+    }
+
+    @Override
+    protected Integer getTicketNumber() {
+        return 10513;
+    }
+
+    @Override
+    protected void setup() {
+        HorizontalLayout layout = new HorizontalLayout();
+        layout.setWidth("300px");
+        layout.setHeight("300px");
+
+        final TreeTable table = new TreeTable();
+        table.setSizeFull();
+
+        table.addGeneratedColumn("parameterId", new HierarchyColumnGenerator());
+        table.addGeneratedColumn("wordingTextId", new TypeColumnGenerator());
+        table.addGeneratedColumn("parameterTypeId", new TypeColumnGenerator());
+
+        table.setColumnWidth("parameterId", 26);
+
+        table.addItem(new TestObject("name 1", "value 1"));
+        table.addItem(new TestObject("name 2", "value 2"));
+        table.addItem(new TestObject("name 3", "value 3"));
+        table.addItem(new TestObject("name 4", "value 4"));
+        table.addItem(new TestObject("name 5", "value 5"));
+        final TestObject parent = new TestObject("name 6", "value 6");
+        table.addItem(parent);
+        table.setFooterVisible(true);
+        for (int i = 1; i <= 10; ++i) {
+            TestObject child = new TestObject("name 6-" + i, "value 6-" + i);
+            table.addItem(child);
+            table.setParent(child, parent);
+        }
+        table.setVisibleColumns(new Object[] { "wordingTextId", "parameterId",
+                "parameterTypeId" });
+        table.setColumnHeaders(new String[] { "", "", "" });
+        table.setHierarchyColumn("parameterId");
+
+        layout.addComponent(table);
+
+        Button button = new Button("Toggle");
+        button.setId("button");
+        button.addClickListener(new ClickListener() {
+
+            public void buttonClick(ClickEvent event) {
+                table.setCollapsed(parent, !table.isCollapsed(parent));
+                Notification.show("collapsed: " + table.isCollapsed(parent));
+            }
+        });
+
+        addComponent(layout);
+        addComponent(button);
+    }
+
+    private class HierarchyColumnGenerator implements Table.ColumnGenerator {
+        public Object generateCell(Table table, Object itemId, Object columnId) {
+            Label label = new Label("this should be mostly hidden");
+            label.setSizeUndefined();
+            return label;
+        }
+    }
+
+    private class TypeColumnGenerator implements Table.ColumnGenerator {
+        public Object generateCell(Table table, Object itemId, Object columnId) {
+            if (itemId instanceof TestObject) {
+                return new Label(((TestObject) itemId).getValue());
+            }
+            return null;
+        }
+    }
+
+}