]> source.dussan.org Git - vaadin-framework.git/commitdiff
Notify EscalatorUpdater when inserting columns (#13334)
authorJohannes Dahlström <johannesd@vaadin.com>
Mon, 2 Jun 2014 16:02:09 +0000 (19:02 +0300)
committerJohannes Dahlström <johannesd@vaadin.com>
Fri, 6 Jun 2014 12:50:56 +0000 (15:50 +0300)
preAttach and postAttach are invoked for each DOM row, passing
the cells corresponding to the inserted columns.

Change-Id: I666bb7b5e690145a3911154d298e703bac0df1cd

client/src/com/vaadin/client/ui/grid/Escalator.java
client/src/com/vaadin/client/ui/grid/EscalatorUpdater.java
client/src/com/vaadin/client/ui/grid/FlyweightCell.java
client/src/com/vaadin/client/ui/grid/FlyweightRow.java

index 3b015fbc8e182d6ba91ad0a87d62d990682c3c3f..618e576aa6812ef67005ffdd0fb52380e3e1f270 100644 (file)
@@ -1550,24 +1550,8 @@ public class Escalator extends Widget {
             final NodeList<Node> childNodes = root.getChildNodes();
 
             for (int row = 0; row < childNodes.getLength(); row++) {
-                final int rowHeight = getDefaultRowHeight();
                 final Element tr = getTrByVisualIndex(row);
-
-                Node referenceCell;
-                if (offset != 0) {
-                    referenceCell = tr.getChild(offset - 1);
-                } else {
-                    referenceCell = null;
-                }
-
-                for (int col = offset; col < offset + numberOfColumns; col++) {
-                    final int colWidth = columnConfiguration
-                            .getColumnWidthActual(col);
-                    final Element cellElem = createCellElement(rowHeight,
-                            colWidth);
-                    referenceCell = insertAfterReferenceAndUpdateIt(tr,
-                            cellElem, referenceCell);
-                }
+                paintInsertCells(tr, row, offset, numberOfColumns);
             }
             reapplyRowWidths();
 
@@ -1604,6 +1588,63 @@ public class Escalator extends Widget {
             }
         }
 
+        /**
+         * Inserts new cell elements into a single row element, invoking
+         * {@link #getEscalatorUpdater()}
+         * {@link EscalatorUpdater#preAttach(Row, Iterable) preAttach} and
+         * {@link EscalatorUpdater#postAttach(Row, Iterable) postAttach} before
+         * and after inserting the cells, respectively.
+         * <p>
+         * Precondition: The row must be already attached to the DOM and the
+         * FlyweightCell instances corresponding to the new columns added to
+         * {@code flyweightRow}.
+         *
+         * @param tr
+         *            the row in which to insert the cells
+         * @param logicalRowIndex
+         *            the index of the row
+         * @param offset
+         *            the index of the first cell
+         * @param numberOfCells
+         *            the number of cells to insert
+         */
+        private void paintInsertCells(final Element tr, int logicalRowIndex,
+                final int offset, final int numberOfCells) {
+
+            assert Document.get().isOrHasChild(tr) : "The row must be attached to the document";
+
+            flyweightRow.setup(tr, logicalRowIndex,
+                    columnConfiguration.getCalculatedColumnWidths());
+
+            Iterable<FlyweightCell> cells = flyweightRow.getUninitializedCells(
+                    offset, numberOfCells);
+
+            final int rowHeight = getDefaultRowHeight();
+            for (FlyweightCell cell : cells) {
+                final int colWidth = columnConfiguration
+                        .getColumnWidthActual(cell.getColumn());
+                final Element cellElem = createCellElement(rowHeight, colWidth);
+                cell.setElement(cellElem);
+            }
+
+            getEscalatorUpdater().preAttach(flyweightRow, cells);
+
+            Node referenceCell;
+            if (offset != 0) {
+                referenceCell = tr.getChild(offset - 1);
+            } else {
+                referenceCell = null;
+            }
+            for (FlyweightCell cell : cells) {
+                referenceCell = insertAfterReferenceAndUpdateIt(tr,
+                        cell.getElement(), referenceCell);
+            }
+
+            getEscalatorUpdater().postAttach(flyweightRow, cells);
+
+            assert flyweightRow.teardown();
+        }
+
         public void setColumnFrozen(int column, boolean frozen) {
             final NodeList<Node> childNodes = root.getChildNodes();
 
index 22cf55cf796fcd17fccfb6204ea0cd0b7523a52d..bfe0ba8bccf5f0351e02db6850ffc18dbb99b716 100644 (file)
@@ -17,9 +17,9 @@
 package com.vaadin.client.ui.grid;
 
 /**
- * A functional interface that allows client code to define how a certain row in
- * Escalator will be displayed. The contents of an escalator's header, body and
- * footer are rendered by their respective updaters.
+ * An interface that allows client code to define how a certain row in Escalator
+ * will be displayed. The contents of an escalator's header, body and footer are
+ * rendered by their respective updaters.
  * <p>
  * The updater is responsible for internally handling all remote communication,
  * should the displayed data need to be fetched remotely.
index 950b3e167aede1d7acb540173f3746e7e9128553..90a9ef4296a09a3889cd98d4aeef98e00e369914 100644 (file)
@@ -43,6 +43,7 @@ public class FlyweightCell {
     private final int column;
     private final FlyweightRow row;
 
+    private Element element = null;
     private CellIterator currentIterator = null;
 
     private final Escalator escalator;
@@ -75,16 +76,36 @@ public class FlyweightCell {
      * or a <code>TH</code> element.
      */
     public Element getElement() {
-        return (Element) row.getElement().getChild(column);
+        return element;
     }
 
-    void setup(final CellIterator cellIterator) {
-        currentIterator = cellIterator;
+    /**
+     * Sets the DOM element for this FlyweightCell, either a <code>TD</code> or
+     * a <code>TH</code>. This method should only be called when
+     * {@code getElement() == null}. It is the caller's responsibility to
+     * actually insert the given element to the document when needed.
+     * 
+     * @param element
+     *            the element corresponding to this FlyweightCell
+     */
+    void setElement(Element element) {
+        assert element != null;
+        // When asserts are enabled, teardown() resets the element to null
+        // so this won't fire simply due to cell reuse
+        assert this.element == null : "Cell element can only be set once";
+        this.element = element;
+    }
+
+    void setup(final CellIterator iterator) {
+        currentIterator = iterator;
 
-        final Element e = getElement();
-        e.setPropertyInt(COLSPAN_ATTR, 1);
-        e.getStyle().setWidth(row.getColumnWidth(column), Unit.PX);
-        e.getStyle().clearDisplay();
+        if (iterator.areCellsInitialized()) {
+            final Element e = (Element) row.getElement().getChild(column);
+            e.setPropertyInt(COLSPAN_ATTR, 1);
+            e.getStyle().setWidth(row.getColumnWidth(column), Unit.PX);
+            e.getStyle().clearDisplay();
+            setElement(e);
+        }
     }
 
     /**
@@ -103,6 +124,7 @@ public class FlyweightCell {
      */
     boolean teardown() {
         currentIterator = null;
+        element = null;
         return true;
     }
 
index 3505b317d1230dc9c60a0e265ca6e680c7b7e654..76013087f7eb98a03f7d23465f7662157bd7e0a6 100644 (file)
@@ -38,11 +38,42 @@ class FlyweightRow implements Row {
     static class CellIterator implements Iterator<FlyweightCell> {
         /** A defensive copy of the cells in the current row. */
         private final ArrayList<FlyweightCell> cells;
+        private final boolean initialized;
         private int cursor = 0;
         private int skipNext = 0;
 
-        public CellIterator(final Collection<FlyweightCell> cells) {
+        /**
+         * Creates a new iterator of initialized flyweight cells. A cell is
+         * initialized if it has a corresponding
+         * {@link FlyweightCell#getElement() DOM element} attached to the row
+         * element.
+         * 
+         * @param cells
+         *            the collection of cells to iterate
+         */
+        public static CellIterator initialized(
+                final Collection<FlyweightCell> cells) {
+            return new CellIterator(cells, true);
+        }
+
+        /**
+         * Creates a new iterator of uninitialized flyweight cells. A cell is
+         * uninitialized if it does not have a corresponding
+         * {@link FlyweightCell#getElement() DOM element} attached to the row
+         * element.
+         * 
+         * @param cells
+         *            the collection of cells to iterate
+         */
+        public static CellIterator uninitialized(
+                final Collection<FlyweightCell> cells) {
+            return new CellIterator(cells, false);
+        }
+
+        private CellIterator(final Collection<FlyweightCell> cells,
+                final boolean initialized) {
             this.cells = new ArrayList<FlyweightCell>(cells);
+            this.initialized = initialized;
         }
 
         @Override
@@ -97,6 +128,10 @@ class FlyweightRow implements Row {
             final int to = Math.min(cursor + n, cells.size());
             return cells.subList(from, to);
         }
+
+        public boolean areCellsInitialized() {
+            return initialized;
+        }
     }
 
     private static final int BLANK = Integer.MIN_VALUE;
@@ -180,11 +215,10 @@ class FlyweightRow implements Row {
     }
 
     /**
-     * Get flyweight cells for the client code to render.
+     * Returns flyweight cells for the client code to render.
+     * 
+     * @return an iterable of flyweight cells
      * 
-     * @return a list of {@link FlyweightCell FlyweightCells}. They are
-     *         generified into {@link Cell Cells}, because Java's generics
-     *         system isn't expressive enough.
      * @see #setup(Element, int, int[])
      * @see #teardown()
      */
@@ -193,7 +227,35 @@ class FlyweightRow implements Row {
         return new Iterable<FlyweightCell>() {
             @Override
             public Iterator<FlyweightCell> iterator() {
-                return new CellIterator(cells);
+                return CellIterator.initialized(cells);
+            }
+        };
+    }
+
+    /**
+     * Returns a subsequence of uninitialized flyweight cells. Uninitialized
+     * cells do not have {@link FlyweightCell#getElement() elements} associated.
+     * Note that FlyweightRow does not keep track of whether cells in actuality
+     * have corresponding DOM elements or not; it is the caller's responsibility
+     * to invoke this method with correct parameters.
+     * <p>
+     * Precondition: the range [offset, offset + numberOfCells) must be valid
+     * 
+     * @param offset
+     *            the index of the first cell to return
+     * @param numberOfCells
+     *            the number of cells to return
+     * @return an iterable of flyweight cells
+     */
+    Iterable<FlyweightCell> getUninitializedCells(final int offset,
+            final int numberOfCells) {
+        assertSetup();
+        assert offset >= 0 && offset + numberOfCells <= cells.size() : "Invalid range of cells";
+        return new Iterable<FlyweightCell>() {
+            @Override
+            public Iterator<FlyweightCell> iterator() {
+                return CellIterator.uninitialized(cells.subList(offset, offset
+                        + numberOfCells));
             }
         };
     }