summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJonatan Kronqvist <jonatan.kronqvist@itmill.com>2011-08-17 10:20:39 +0000
committerJonatan Kronqvist <jonatan.kronqvist@itmill.com>2011-08-17 10:20:39 +0000
commit63411e8a5478aef8316cd270f045d82c83caee68 (patch)
tree5daf8fde17cfcebfbdb535751dab687391bda533 /src
parentaade55ae17d4e40b0ad64ae8d3c2a2205897f6ff (diff)
downloadvaadin-framework-63411e8a5478aef8316cd270f045d82c83caee68.tar.gz
vaadin-framework-63411e8a5478aef8316cd270f045d82c83caee68.zip
Optional expand and collapse animations for TreeTable (#6723)
svn changeset:20445/svn branch:6.7
Diffstat (limited to 'src')
-rw-r--r--src/com/vaadin/terminal/gwt/client/ComputedStyle.java178
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java43
-rw-r--r--src/com/vaadin/terminal/gwt/client/ui/VTreeTable.java338
-rw-r--r--src/com/vaadin/ui/Table.java77
-rw-r--r--src/com/vaadin/ui/TreeTable.java22
5 files changed, 614 insertions, 44 deletions
diff --git a/src/com/vaadin/terminal/gwt/client/ComputedStyle.java b/src/com/vaadin/terminal/gwt/client/ComputedStyle.java
new file mode 100644
index 0000000000..5aac2caf47
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/ComputedStyle.java
@@ -0,0 +1,178 @@
+package com.vaadin.terminal.gwt.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.user.client.Element;
+
+public class ComputedStyle {
+
+ protected final JavaScriptObject computedStyle;
+ private final Element elem;
+
+ /**
+ * Gets this element's computed style object which can be used to gather
+ * information about the current state of the rendered node.
+ * <p>
+ * Note that this method is expensive. Wherever possible, reuse the returned
+ * object.
+ *
+ * @param elem
+ * the element
+ * @return the computed style
+ */
+ public ComputedStyle(Element elem) {
+ computedStyle = getComputedStyle(elem);
+ this.elem = elem;
+ }
+
+ private static native JavaScriptObject getComputedStyle(Element elem)
+ /*-{
+ if(elem.nodeType != 1) {
+ return {};
+ }
+
+ if($wnd.document.defaultView && $wnd.document.defaultView.getComputedStyle) {
+ return $wnd.document.defaultView.getComputedStyle(elem, null);
+ }
+
+ if(elem.currentStyle) {
+ return elem.currentStyle;
+ }
+ }-*/;
+
+ /**
+ *
+ * @param name
+ * name of the CSS property in camelCase
+ * @return the value of the property, normalized for across browsers (each
+ * browser returns pixel values whenever possible).
+ */
+ public final native String getProperty(String name)
+ /*-{
+ var cs = this.@com.vaadin.terminal.gwt.client.ComputedStyle::computedStyle;
+ var elem = this.@com.vaadin.terminal.gwt.client.ComputedStyle::elem;
+
+ // Border values need to be checked separately. The width might have a
+ // meaningful value even if the border style is "none". In that case the
+ // value should be 0.
+ if(name.indexOf("border") > -1 && name.indexOf("Width") > -1) {
+ var borderStyleProp = name.substring(0,name.length-5) + "Style";
+ if(cs.getPropertyValue)
+ var borderStyle = cs.getPropertyValue(borderStyleProp);
+ else // IE
+ var borderStyle = cs[borderStyleProp];
+ if(borderStyle == "none")
+ return "0px";
+ }
+
+ if(cs.getPropertyValue) {
+
+ // Convert name to dashed format
+ name = name.replace(/([A-Z])/g, "-$1").toLowerCase();
+ var ret = cs.getPropertyValue(name);
+
+ } else {
+
+ var ret = cs[name];
+ var style = elem.style;
+
+ // From the awesome hack by Dean Edwards
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+ // If we're not dealing with a regular pixel number
+ // but a number that has a weird ending, we need to convert it to pixels
+ if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
+ // Remember the original values
+ var left = style.left, rsLeft = elem.runtimeStyle.left;
+
+ // Put in the new values to get a computed value out
+ elem.runtimeStyle.left = this.left;
+ style.left = ret || 0;
+ ret = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ elem.runtimeStyle.left = rsLeft;
+ }
+
+ }
+
+ // Normalize margin values. This is not totally valid, but in most cases
+ // it is what the user wants to know.
+ if(name.indexOf("margin") > -1 && ret == "auto") {
+ return "0px";
+ }
+
+ // Some browsers return undefined width and height values as "auto", so
+ // we need to retrieve those ourselves.
+ if (name == "width" && ret == "auto") {
+ ret = elem.clientWidth + "px";
+ } else if (name == "height" && ret == "auto") {
+ ret = elem.clientHeight + "px";
+ }
+
+ return ret;
+
+ }-*/;
+
+ public final int getIntProperty(String name) {
+ return parseInt(getProperty(name)).intValue();
+ }
+
+ /**
+ * Get current margin values from the DOM. The array order is the default
+ * CSS order: top, right, bottom, left.
+ */
+ public final int[] getMargin() {
+ int[] margin = { 0, 0, 0, 0 };
+ margin[0] = getIntProperty("marginTop");
+ margin[1] = getIntProperty("marginRight");
+ margin[2] = getIntProperty("marginBottom");
+ margin[3] = getIntProperty("marginLeft");
+ return margin;
+ }
+
+ /**
+ * Get current padding values from the DOM. The array order is the default
+ * CSS order: top, right, bottom, left.
+ */
+ public final int[] getPadding() {
+ int[] padding = { 0, 0, 0, 0 };
+ padding[0] = getIntProperty("paddingTop");
+ padding[1] = getIntProperty("paddingRight");
+ padding[2] = getIntProperty("paddingBottom");
+ padding[3] = getIntProperty("paddingLeft");
+ return padding;
+ }
+
+ /**
+ * Get current border values from the DOM. The array order is the default
+ * CSS order: top, right, bottom, left.
+ */
+ public final int[] getBorder() {
+ int[] border = { 0, 0, 0, 0 };
+ border[0] = getIntProperty("borderTopWidth");
+ border[1] = getIntProperty("borderRightWidth");
+ border[2] = getIntProperty("borderBottomWidth");
+ border[3] = getIntProperty("borderLeftWidth");
+ return border;
+ }
+
+ /**
+ * Takes a String value e.g. "12px" and parses that to int 12.
+ *
+ * @param String
+ * a value starting with a number
+ * @return int the value from the string before any non-numeric characters.
+ * If the value cannot be parsed to a number, returns
+ * <code>null</code>.
+ */
+ public static native Integer parseInt(final String value)
+ /*-{
+ var number = parseInt(value, 10);
+ if (isNaN(number))
+ return null;
+ else
+ return @java.lang.Integer::valueOf(I)(number);
+ }-*/;
+
+}
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java b/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java
index ad69d8a751..fb51919b10 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java
@@ -876,7 +876,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
UIDL partialRowUpdates = uidl.getChildByTagName("urows");
if (partialRowUpdates != null || partialRowAdditions != null) {
updateRowsInBody(partialRowUpdates);
- addRowsToBody(partialRowAdditions);
+ addAndRemoveRows(partialRowAdditions);
} else {
UIDL rowData = uidl.getChildByTagName("rows");
if (rowData != null) {
@@ -1355,7 +1355,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
scrollBody.renderRows(uidl, firstRow, reqRows);
- updateCache();
+ discardRowsOutsideCacheWindow();
}
private void updateRowsInBody(UIDL partialRowUpdates) {
@@ -1367,10 +1367,14 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
scrollBody.unlinkRows(firstRowIx, count);
scrollBody.insertRows(partialRowUpdates, firstRowIx, count);
- updateCache();
+ discardRowsOutsideCacheWindow();
}
- private void updateCache() {
+ /**
+ * Updates the internal cache by unlinking rows that fall outside of the
+ * caching window.
+ */
+ protected void discardRowsOutsideCacheWindow() {
final int optimalFirstRow = (int) (firstRowInViewPort - pageLength
* cache_rate);
boolean cont = true;
@@ -1391,7 +1395,14 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
scrollBody.restoreRowVisibility();
}
- private void addRowsToBody(UIDL partialRowAdditions) {
+ /**
+ * Inserts rows in the table body or removes them from the table body based
+ * on the commands in the UIDL.
+ *
+ * @param partialRowAdditions
+ * the UIDL containing row updates.
+ */
+ protected void addAndRemoveRows(UIDL partialRowAdditions) {
if (partialRowAdditions == null) {
return;
}
@@ -1412,7 +1423,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
}
- updateCache();
+ discardRowsOutsideCacheWindow();
}
/**
@@ -3890,14 +3901,26 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
}
- protected void insertRows(UIDL rowData, int firstIndex, int rows) {
+ /**
+ * Inserts rows as provided in the rowData starting at firstIndex.
+ *
+ * @param rowData
+ * @param firstIndex
+ * @param rows
+ * the number of rows
+ * @return a list of the rows added.
+ */
+ protected List<VScrollTableRow> insertRows(UIDL rowData,
+ int firstIndex, int rows) {
aligns = tHead.getColumnAlignments();
final Iterator<?> it = rowData.getChildIterator();
+ List<VScrollTableRow> insertedRows = new ArrayList<VScrollTableRow>();
if (firstIndex == lastRendered + 1) {
while (it.hasNext()) {
final VScrollTableRow row = prepareRow((UIDL) it.next());
addRow(row);
+ insertedRows.add(row);
lastRendered++;
}
fixSpacers();
@@ -3910,18 +3933,22 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
for (i = 0; i < rows; i++) {
addRowBeforeFirstRendered(rowArray[i]);
+ insertedRows.add(rowArray[i]);
firstRendered--;
}
} else {
// insert in the middle
int realIx = firstIndex - firstRendered;
while (it.hasNext()) {
- insertRowAt(prepareRow((UIDL) it.next()), realIx);
+ VScrollTableRow row = prepareRow((UIDL) it.next());
+ insertRowAt(row, realIx);
+ insertedRows.add(row);
lastRendered++;
realIx++;
}
fixSpacers();
}
+ return insertedRows;
}
protected void insertRowsDeleteBelow(UIDL rowData, int firstIndex,
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTreeTable.java b/src/com/vaadin/terminal/gwt/client/ui/VTreeTable.java
index 15ebcde35e..a3b7008e77 100644
--- a/src/com/vaadin/terminal/gwt/client/ui/VTreeTable.java
+++ b/src/com/vaadin/terminal/gwt/client/ui/VTreeTable.java
@@ -4,17 +4,25 @@
package com.vaadin.terminal.gwt.client.ui;
+import java.util.ArrayList;
import java.util.Iterator;
+import java.util.List;
+import com.google.gwt.animation.client.Animation;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.dom.client.SpanElement;
+import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.dom.client.Style.Visibility;
import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.BrowserInfo;
+import com.vaadin.terminal.gwt.client.ComputedStyle;
import com.vaadin.terminal.gwt.client.RenderSpace;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.VConsole;
@@ -29,6 +37,7 @@ public class VTreeTable extends VScrollTable {
private int colIndexOfHierarchy;
private String collapsedRowKey;
private VTreeTableScrollBody scrollBody;
+ private boolean animationsEnabled;
@Override
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
@@ -38,6 +47,7 @@ public class VTreeTable extends VScrollTable {
widget = (FocusableScrollPanel) getWidget(1);
scrollPosition = widget.getScrollPosition();
}
+ animationsEnabled = uidl.getBooleanAttribute("animate");
colIndexOfHierarchy = uidl
.hasAttribute(ATTRIBUTE_HIERARCHY_COLUMN_INDEX) ? uidl
.getIntAttribute(ATTRIBUTE_HIERARCHY_COLUMN_INDEX) : 0;
@@ -73,6 +83,36 @@ public class VTreeTable extends VScrollTable {
return scrollBody;
}
+ /*
+ * Overridden to allow animation of expands and collapses of nodes.
+ */
+ @Override
+ protected void addAndRemoveRows(UIDL partialRowAdditions) {
+ if (partialRowAdditions == null) {
+ return;
+ }
+
+ if (animationsEnabled && browserSupportsAnimation()) {
+ if (partialRowAdditions.hasAttribute("hide")) {
+ scrollBody.unlinkRowsAnimatedAndUpdateCacheWhenFinished(
+ partialRowAdditions.getIntAttribute("firstprowix"),
+ partialRowAdditions.getIntAttribute("numprows"));
+ } else {
+ scrollBody.insertRowsAnimated(partialRowAdditions,
+ partialRowAdditions.getIntAttribute("firstprowix"),
+ partialRowAdditions.getIntAttribute("numprows"));
+ discardRowsOutsideCacheWindow();
+ }
+ } else {
+ super.addAndRemoveRows(partialRowAdditions);
+ }
+ }
+
+ private boolean browserSupportsAnimation() {
+ BrowserInfo bi = BrowserInfo.get();
+ return !(bi.isIE6() || bi.isIE7() || bi.isSafari4());
+ }
+
class VTreeTableScrollBody extends VScrollTable.VScrollTableBody {
private int identWidth = -1;
@@ -241,6 +281,304 @@ public class VTreeTable extends VScrollTable {
next.setIdent();
}
}
+
+ protected void unlinkRowsAnimatedAndUpdateCacheWhenFinished(
+ final int firstIndex, final int rows) {
+ List<VScrollTableRow> rowsToDelete = new ArrayList<VScrollTableRow>();
+ for (int ix = firstIndex; ix < firstIndex + rows; ix++) {
+ VScrollTableRow row = getRowByRowIndex(ix);
+ if (row != null) {
+ rowsToDelete.add(row);
+ }
+ }
+ RowCollapseAnimation anim = new RowCollapseAnimation(rowsToDelete) {
+ @Override
+ protected void onComplete() {
+ super.onComplete();
+ // Actually unlink the rows and update the cache after the
+ // animation is done.
+ unlinkRows(firstIndex, rows);
+ discardRowsOutsideCacheWindow();
+ ensureCacheFilled();
+ }
+ };
+ anim.run(150);
+ }
+
+ protected List<VScrollTableRow> insertRowsAnimated(UIDL rowData,
+ int firstIndex, int rows) {
+ List<VScrollTableRow> insertedRows = insertRows(rowData,
+ firstIndex, rows);
+ RowExpandAnimation anim = new RowExpandAnimation(insertedRows);
+ anim.run(150);
+ return insertedRows;
+ }
+
+ /**
+ * Prepares the table for animation by copying the background colors of
+ * all TR elements to their respective TD elements if the TD element is
+ * transparent. This is needed, since if TDs have transparent
+ * backgrounds, the rows sliding behind them are visible.
+ */
+ private class AnimationPreparator {
+ private final int lastItemIx;
+
+ public AnimationPreparator(int lastItemIx) {
+ this.lastItemIx = lastItemIx;
+ }
+
+ public void prepareTableForAnimation() {
+ int ix = lastItemIx;
+ VScrollTableRow row = null;
+ while ((row = getRowByRowIndex(ix)) != null) {
+ copyTRBackgroundsToTDs(row);
+ --ix;
+ }
+ }
+
+ private void copyTRBackgroundsToTDs(VScrollTableRow row) {
+ Element tr = row.getElement();
+ ComputedStyle cs = new ComputedStyle(tr);
+ String backgroundAttachment = cs
+ .getProperty("backgroundAttachment");
+ String backgroundClip = cs.getProperty("backgroundClip");
+ String backgroundColor = cs.getProperty("backgroundColor");
+ String backgroundImage = cs.getProperty("backgroundImage");
+ String backgroundOrigin = cs.getProperty("backgroundOrigin");
+ for (int ix = 0; ix < tr.getChildCount(); ix++) {
+ Element td = tr.getChild(ix).cast();
+ if (!elementHasBackground(td)) {
+ td.getStyle().setProperty("backgroundAttachment",
+ backgroundAttachment);
+ td.getStyle().setProperty("backgroundClip",
+ backgroundClip);
+ td.getStyle().setProperty("backgroundColor",
+ backgroundColor);
+ td.getStyle().setProperty("backgroundImage",
+ backgroundImage);
+ td.getStyle().setProperty("backgroundOrigin",
+ backgroundOrigin);
+ }
+ }
+ }
+
+ private boolean elementHasBackground(Element element) {
+ ComputedStyle cs = new ComputedStyle(element);
+ String clr = cs.getProperty("backgroundColor");
+ String img = cs.getProperty("backgroundImage");
+ return !("rgba(0, 0, 0, 0)".equals(clr.trim())
+ || "transparent".equals(clr.trim()) || img == null);
+ }
+
+ public void restoreTableAfterAnimation() {
+ int ix = lastItemIx;
+ VScrollTableRow row = null;
+ while ((row = getRowByRowIndex(ix)) != null) {
+ restoreStyleForTDsInRow(row);
+
+ --ix;
+ }
+ }
+
+ private void restoreStyleForTDsInRow(VScrollTableRow row) {
+ Element tr = row.getElement();
+ for (int ix = 0; ix < tr.getChildCount(); ix++) {
+ Element td = tr.getChild(ix).cast();
+ td.getStyle().clearProperty("backgroundAttachment");
+ td.getStyle().clearProperty("backgroundClip");
+ td.getStyle().clearProperty("backgroundColor");
+ td.getStyle().clearProperty("backgroundImage");
+ td.getStyle().clearProperty("backgroundOrigin");
+ }
+ }
+ }
+
+ /**
+ * Animates row expansion using the GWT animation framework.
+ *
+ * The idea is as follows:
+ *
+ * 1. Insert all rows normally
+ *
+ * 2. Insert a newly created DIV containing a new TABLE element below
+ * the DIV containing the actual scroll table body.
+ *
+ * 3. Clone the rows that were inserted in step 1 and attach the clones
+ * to the new TABLE element created in step 2.
+ *
+ * 4. The new DIV from step 2 is absolutely positioned so that the last
+ * inserted row is just behind the row that was expanded.
+ *
+ * 5. Hide the contents of the originally inserted rows by setting the
+ * DIV.v-table-cell-wrapper to display:none;.
+ *
+ * 6. Set the height of the originally inserted rows to 0.
+ *
+ * 7. The animation loop slides the DIV from step 2 downwards, while at
+ * the same pace growing the height of each of the inserted rows from 0
+ * to full height. The first inserted row grows from 0 to full and after
+ * this the second row grows from 0 to full, etc until all rows are full
+ * height.
+ *
+ * 8. Remove the DIV from step 2
+ *
+ * 9. Restore display:block; to the DIV.v-table-cell-wrapper elements.
+ *
+ * 10. DONE
+ */
+ private class RowExpandAnimation extends Animation {
+
+ private final List<VScrollTableRow> rows;
+ private Element cloneDiv;
+ private Element cloneTable;
+ private AnimationPreparator preparator;
+
+ public RowExpandAnimation(List<VScrollTableRow> rows) {
+ this.rows = rows;
+ buildAndInsertAnimatingDiv();
+ preparator = new AnimationPreparator(rows.get(0).getIndex() - 1);
+ preparator.prepareTableForAnimation();
+ for (VScrollTableRow row : rows) {
+ cloneAndAppendRow(row);
+ row.addStyleName("v-table-row-animating");
+ setCellWrapperDivsToDisplayNone(row);
+ row.setHeight(getInitialHeight());
+ }
+ }
+
+ protected String getInitialHeight() {
+ return "0px";
+ }
+
+ private void cloneAndAppendRow(VScrollTableRow row) {
+ Element clonedTR = null;
+ clonedTR = row.getElement().cloneNode(true).cast();
+ clonedTR.getStyle().setVisibility(Visibility.VISIBLE);
+ cloneTable.appendChild(clonedTR);
+ }
+
+ protected double getBaseOffset() {
+ return rows.get(0).getAbsoluteTop()
+ - rows.get(0).getParent().getAbsoluteTop()
+ - rows.size() * getRowHeight();
+ }
+
+ private void buildAndInsertAnimatingDiv() {
+ cloneDiv = DOM.createDiv();
+ cloneDiv.addClassName("v-treetable-animation-clone-wrapper");
+ cloneTable = DOM.createTable();
+ cloneTable.addClassName("v-treetable-animation-clone");
+ cloneDiv.appendChild(cloneTable);
+ insertAnimatingDiv();
+ }
+
+ private void insertAnimatingDiv() {
+ Element tableBody = getElement().cast();
+ Element tableBodyParent = tableBody.getParentElement().cast();
+ tableBodyParent.insertAfter(cloneDiv, tableBody);
+ }
+
+ @Override
+ protected void onUpdate(double progress) {
+ animateDiv(progress);
+ animateRowHeights(progress);
+ }
+
+ private void animateDiv(double progress) {
+ double offset = calculateDivOffset(progress, getRowHeight());
+
+ cloneDiv.getStyle().setTop(getBaseOffset() + offset, Unit.PX);
+ }
+
+ private void animateRowHeights(double progress) {
+ double rh = getRowHeight();
+ double vlh = calculateHeightOfAllVisibleLines(progress, rh);
+ int ix = 0;
+
+ while (ix < rows.size()) {
+ double height = vlh < rh ? vlh : rh;
+ rows.get(ix).setHeight(height + "px");
+ vlh -= height;
+ ix++;
+ }
+ }
+
+ protected double calculateHeightOfAllVisibleLines(double progress,
+ double rh) {
+ return rows.size() * rh * progress;
+ }
+
+ protected double calculateDivOffset(double progress, double rh) {
+ return progress * rows.size() * rh;
+ }
+
+ @Override
+ protected void onComplete() {
+ preparator.restoreTableAfterAnimation();
+ for (VScrollTableRow row : rows) {
+ resetCellWrapperDivsDisplayProperty(row);
+ row.removeStyleName("v-table-row-animating");
+ }
+ Element tableBodyParent = (Element) getElement()
+ .getParentElement();
+ tableBodyParent.removeChild(cloneDiv);
+ }
+
+ private void setCellWrapperDivsToDisplayNone(VScrollTableRow row) {
+ Element tr = row.getElement();
+ for (int ix = 0; ix < tr.getChildCount(); ix++) {
+ getWrapperDiv(tr, ix).getStyle().setDisplay(Display.NONE);
+ }
+ }
+
+ private Element getWrapperDiv(Element tr, int tdIx) {
+ Element td = tr.getChild(tdIx).cast();
+ return td.getChild(0).cast();
+ }
+
+ private void resetCellWrapperDivsDisplayProperty(VScrollTableRow row) {
+ Element tr = row.getElement();
+ for (int ix = 0; ix < tr.getChildCount(); ix++) {
+ getWrapperDiv(tr, ix).getStyle().clearProperty("display");
+ }
+ }
+
+ }
+
+ /**
+ * This is the inverse of the RowExpandAnimation and is implemented by
+ * extending it and overriding the calculation of offsets and heights.
+ */
+ private class RowCollapseAnimation extends RowExpandAnimation {
+
+ private final List<VScrollTableRow> rows;
+
+ public RowCollapseAnimation(List<VScrollTableRow> rows) {
+ super(rows);
+ this.rows = rows;
+ }
+
+ @Override
+ protected String getInitialHeight() {
+ return getRowHeight() + "px";
+ }
+
+ @Override
+ protected double getBaseOffset() {
+ return getRowHeight();
+ }
+
+ @Override
+ protected double calculateHeightOfAllVisibleLines(double progress,
+ double rh) {
+ return rows.size() * rh * (1 - progress);
+ }
+
+ @Override
+ protected double calculateDivOffset(double progress, double rh) {
+ return -super.calculateDivOffset(progress, rh);
+ }
+ }
}
/**
diff --git a/src/com/vaadin/ui/Table.java b/src/com/vaadin/ui/Table.java
index fd18bf0fd7..5f0fd07277 100644
--- a/src/com/vaadin/ui/Table.java
+++ b/src/com/vaadin/ui/Table.java
@@ -378,7 +378,7 @@ public class Table extends AbstractSelect implements Action.Container,
* Table cell specific style generator
*/
private CellStyleGenerator cellStyleGenerator = null;
-
+
/**
* Table cell specific tooltip generator
*/
@@ -2867,8 +2867,8 @@ public class Table extends AbstractSelect implements Action.Container,
target.addAttribute("style-" + columnIdMap.key(columnId),
cellStyle);
}
- }
-
+ }
+
if ((iscomponent[currentColumn] || iseditable)
&& Component.class.isInstance(cells[CELL_FIRSTCOL
+ currentColumn][indexInRowbuffer])) {
@@ -2881,29 +2881,34 @@ public class Table extends AbstractSelect implements Action.Container,
c.paint(target);
}
} else {
- target.addText((String) cells[CELL_FIRSTCOL + currentColumn][indexInRowbuffer]);
- paintCellTooltips(target, itemId, columnId);
+ target.addText((String) cells[CELL_FIRSTCOL + currentColumn][indexInRowbuffer]);
+ paintCellTooltips(target, itemId, columnId);
}
}
target.endTag("tr");
}
-
- private void paintCellTooltips(PaintTarget target, Object itemId, Object columnId) throws PaintException {
- if(itemDescriptionGenerator != null) {
- String itemDescription = itemDescriptionGenerator.generateDescription(this, itemId, columnId);
- if(itemDescription != null && !itemDescription.equals("")) {
- target.addAttribute("descr-" + columnIdMap.key(columnId), itemDescription);
- }
- }
+
+ private void paintCellTooltips(PaintTarget target, Object itemId,
+ Object columnId) throws PaintException {
+ if (itemDescriptionGenerator != null) {
+ String itemDescription = itemDescriptionGenerator
+ .generateDescription(this, itemId, columnId);
+ if (itemDescription != null && !itemDescription.equals("")) {
+ target.addAttribute("descr-" + columnIdMap.key(columnId),
+ itemDescription);
+ }
+ }
}
-
- private void paintRowTooltips(PaintTarget target, Object itemId ) throws PaintException {
- if(itemDescriptionGenerator != null) {
- String rowDescription = itemDescriptionGenerator.generateDescription(this, itemId, null);
- if(rowDescription != null && !rowDescription.equals("")){
- target.addAttribute("rowdescr", rowDescription);
- }
+
+ private void paintRowTooltips(PaintTarget target, Object itemId)
+ throws PaintException {
+ if (itemDescriptionGenerator != null) {
+ String rowDescription = itemDescriptionGenerator
+ .generateDescription(this, itemId, null);
+ if (rowDescription != null && !rowDescription.equals("")) {
+ target.addAttribute("rowdescr", rowDescription);
+ }
}
}
@@ -2947,11 +2952,11 @@ public class Table extends AbstractSelect implements Action.Container,
if (rowStyle != null && !rowStyle.equals("")) {
target.addAttribute("rowstyle", rowStyle);
}
- }
-
- paintRowTooltips(target, itemId);
-
- paintRowAttributes(target, itemId);
+ }
+
+ paintRowTooltips(target, itemId);
+
+ paintRowAttributes(target, itemId);
}
protected void paintRowHeader(PaintTarget target, Object[][] cells,
@@ -3895,7 +3900,7 @@ public class Table extends AbstractSelect implements Action.Container,
*/
public void setCellStyleGenerator(CellStyleGenerator cellStyleGenerator) {
this.cellStyleGenerator = cellStyleGenerator;
- requestRepaint();
+ refreshRenderedCells();
}
/**
@@ -4576,27 +4581,27 @@ public class Table extends AbstractSelect implements Action.Container,
removeListener(VScrollTable.COLUMN_REORDER_EVENT_ID,
ColumnReorderEvent.class, listener);
}
-
+
/**
- * Set the item description generator which generates tooltips
- * for cells and rows in the Table
+ * Set the item description generator which generates tooltips for cells and
+ * rows in the Table
*
* @param generator
- * The generator to use or null to disable
+ * The generator to use or null to disable
*/
- public void setItemDescriptionGenerator(ItemDescriptionGenerator generator){
+ public void setItemDescriptionGenerator(ItemDescriptionGenerator generator) {
if (generator != itemDescriptionGenerator) {
itemDescriptionGenerator = generator;
refreshRenderedCells();
}
}
-
+
/**
- * Get the item description generator which generates tooltips
- * for cells and rows in the Table.
+ * Get the item description generator which generates tooltips for cells and
+ * rows in the Table.
*/
- public ItemDescriptionGenerator getItemDescriptionGenerator(){
- return itemDescriptionGenerator;
+ public ItemDescriptionGenerator getItemDescriptionGenerator() {
+ return itemDescriptionGenerator;
}
}
diff --git a/src/com/vaadin/ui/TreeTable.java b/src/com/vaadin/ui/TreeTable.java
index 987e3a9bfd..f64c818313 100644
--- a/src/com/vaadin/ui/TreeTable.java
+++ b/src/com/vaadin/ui/TreeTable.java
@@ -309,6 +309,7 @@ public class TreeTable extends Table implements Hierarchical {
private Object focusedRowId = null;
private Object hierarchyColumnId;
private Object toggledItemId;
+ private boolean animationsEnabled;
private ContainerStrategy getContainerStrategy() {
if (cStrategy == null) {
@@ -408,6 +409,7 @@ public class TreeTable extends Table implements Hierarchical {
target.addAttribute("focusedRow", itemIdMapper.key(focusedRowId));
focusedRowId = null;
}
+ target.addAttribute("animate", animationsEnabled);
if (hierarchyColumnId != null) {
Object[] visibleColumns2 = getVisibleColumns();
for (int i = 0; i < visibleColumns2.length; i++) {
@@ -704,4 +706,24 @@ public class TreeTable extends Table implements Hierarchical {
fireEvent(new CollapseEvent(this, itemId));
}
+ /**
+ * @return true if animations are enabled
+ */
+ public boolean isAnimationsEnabled() {
+ return animationsEnabled;
+ }
+
+ /**
+ * Animations can be enabled by passing true to this method. Currently
+ * expanding rows slide in from the top and collapsing rows slide out the
+ * same way. NOTE! not supported in Internet Explorer 6 or 7.
+ *
+ * @param animationsEnabled
+ * true or false whether to enable animations or not.
+ */
+ public void setAnimationsEnabled(boolean animationsEnabled) {
+ this.animationsEnabled = animationsEnabled;
+ requestRepaint();
+ }
+
}