From: Johannes Dahlström Date: Fri, 8 Feb 2013 15:20:04 +0000 (+0200) Subject: Merge of second part of (#10513) to Vaadin 7. X-Git-Tag: 7.0.1~14 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=6949cd8109db726b92e80cffb3286c67ec7abb47;p=vaadin-framework.git Merge of second part of (#10513) to Vaadin 7. Fix for TreeTable's indent handling & width calculations. Conflicts: client/src/com/vaadin/client/ui/table/TableConnector.java Change-Id: I94f0d84ac978045d3b79be6b3726ea6973d30b35 --- diff --git a/client/src/com/vaadin/client/ui/VScrollTable.java b/client/src/com/vaadin/client/ui/VScrollTable.java index 75d67b82d6..c76dd38d8f 100644 --- a/client/src/com/vaadin/client/ui/VScrollTable.java +++ b/client/src/com/vaadin/client/ui/VScrollTable.java @@ -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, extend at your own risk. 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 true if this is hierarcyColumn's header cell, + * false 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 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.

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.

Calculates the maximum + * indent of the hierarcyColumn, if applicable. + */ + protected void calculateMaxIndent() { + // NOP + } + private void detectExtrawidth() { NodeList 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++; diff --git a/client/src/com/vaadin/client/ui/VTreeTable.java b/client/src/com/vaadin/client/ui/VTreeTable.java index f65198865c..097b9c7ab2 100644 --- a/client/src/com/vaadin/client/ui/VTreeTable.java +++ b/client/src/com/vaadin/client/ui/VTreeTable.java @@ -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 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; } diff --git a/client/src/com/vaadin/client/ui/table/TableConnector.java b/client/src/com/vaadin/client/ui/table/TableConnector.java index c967642059..fc31cdf8ea 100644 --- a/client/src/com/vaadin/client/ui/table/TableConnector.java +++ b/client/src/com/vaadin/client/ui/table/TableConnector.java @@ -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 index 0000000000..611e7a5994 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/treetable/TreeTableExtraScrollBarWithChildren.html @@ -0,0 +1,69 @@ + + + + + + +TreeTableExtraScrollbar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TreeTableExtraScrollbar
open/run/com.vaadin.tests.components.treetable.TreeTableExtraScrollbarWithChildren?restartApplication
mouseClickbutton
pause300
screenCaptureexpanded
mouseClickbutton
pause300
screenCapturecollapsed
open/run/com.vaadin.tests.components.treetable.TreeTableExtraScrollbarWithChildren
pause300
screenCapturerefreshed
+ + 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 index 0000000000..cad33e242f --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/treetable/TreeTableExtraScrollbarWithChildren.java @@ -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; + } + } + +}