package com.vaadin.client.ui.grid;
import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.ui.HasOneWidget;
/**
* A representation of a single cell.
* @since 7.2
* @author Vaadin Ltd
*/
-public interface Cell {
+public interface Cell extends HasOneWidget {
+
/**
* Gets the index of the row this cell is in.
*
* @since 7.2
* @author Vaadin Ltd
*/
-public class ColumnGroup {
+public class ColumnGroup<T> {
/**
* The text shown in the header
* The columns included in the group when also accounting for subgroup
* columns
*/
- private final List<GridColumn> columns;
+ private final List<GridColumn<?, T>> columns;
/**
* The grid associated with the column group
/**
* Constructs a new column group
*/
- ColumnGroup(Grid grid, Collection<GridColumn> columns) {
+ ColumnGroup(Grid grid, Collection<GridColumn<?, T>> columns) {
if (columns == null) {
throw new IllegalArgumentException(
"columns cannot be null. Pass an empty list instead.");
}
this.grid = grid;
- this.columns = Collections.unmodifiableList(new ArrayList<GridColumn>(
- columns));
+ this.columns = Collections
+ .unmodifiableList(new ArrayList<GridColumn<?, T>>(columns));
}
/**
*
* @return unmodifiable list of columns
*/
- public List<GridColumn> getColumns() {
+ public List<GridColumn<?, T>> getColumns() {
return columns;
}
}
* A column group row represents an auxiliary header or footer row added to the
* grid. A column group row includes column groups that group columns together.
*
+ * @param <T>
+ * Row type
* @since 7.2
* @author Vaadin Ltd
*/
-public class ColumnGroupRow {
+public class ColumnGroupRow<T> {
/**
* The column groups in this row
*/
- private List<ColumnGroup> groups = new ArrayList<ColumnGroup>();
+ private List<ColumnGroup<T>> groups = new ArrayList<ColumnGroup<T>>();
/**
* The grid associated with the column row
*/
- private final Grid grid;
+ private final Grid<T> grid;
/**
* Is the header shown
* Grid associated with this column
*
*/
- ColumnGroupRow(Grid grid) {
+ ColumnGroupRow(Grid<T> grid) {
this.grid = grid;
}
* @return a column group representing the collection of columns added to
* the group.
*/
- public ColumnGroup addGroup(GridColumn... columns) {
+ public ColumnGroup<T> addGroup(GridColumn<?, T>... columns) {
- for (GridColumn column : columns) {
+ for (GridColumn<?, T> column : columns) {
if (isColumnGrouped(column)) {
throw new IllegalArgumentException("Column "
+ String.valueOf(column.getHeaderCaption())
}
}
- ColumnGroup group = new ColumnGroup(grid, Arrays.asList(columns));
+ ColumnGroup<T> group = new ColumnGroup<T>(grid, Arrays.asList(columns));
groups.add(group);
grid.refreshHeader();
grid.refreshFooter();
* the group.
*
*/
- public ColumnGroup addGroup(ColumnGroup... groups) {
+ public ColumnGroup<T> addGroup(ColumnGroup<T>... groups) {
assert groups != null : "groups cannot be null";
- Set<GridColumn> columns = new HashSet<GridColumn>();
- for (ColumnGroup group : groups) {
+ Set<GridColumn<?, T>> columns = new HashSet<GridColumn<?, T>>();
+ for (ColumnGroup<T> group : groups) {
columns.addAll(group.getColumns());
}
- ColumnGroup group = new ColumnGroup(grid, columns);
+ ColumnGroup<T> group = new ColumnGroup<T>(grid, columns);
this.groups.add(group);
grid.refreshHeader();
grid.refreshFooter();
* @param group
* The group to remove
*/
- public void removeGroup(ColumnGroup group) {
+ public void removeGroup(ColumnGroup<T> group) {
groups.remove(group);
grid.refreshHeader();
grid.refreshFooter();
*
* @return unmodifiable list of groups in this row
*/
- public List<ColumnGroup> getGroups() {
+ public List<ColumnGroup<T>> getGroups() {
return Collections.unmodifiableList(groups);
}
* Iterates all the column groups and checks if the columns alread has been
* added to a group.
*/
- private boolean isColumnGrouped(GridColumn column) {
- for (ColumnGroup group : groups) {
+ private boolean isColumnGrouped(GridColumn<?, T> column) {
+ for (ColumnGroup<T> group : groups) {
if (group.getColumns().contains(column)) {
return true;
}
* escalator DOM). NOTE: these bits can most often also be identified by
* searching for code that call scrollElem.getScrollTop();.
*/
- /*
- * [[widgets]]: This needs to be re-inspected once GWT/Vaadin widgets are
- * being supported.
- */
/**
* A utility class that contains utility methods that are usually called
final Node tr = childNodes.getItem(visualRowIndex);
for (int column = 0; column < numberOfColumns; column++) {
- // TODO [[widgets]]
- tr.getChild(offset).removeFromParent();
+ Element cellElement = tr.getChild(offset).cast();
+ detachPossibleWidgetFromCell(cellElement);
+ cellElement.removeFromParent();
}
recalculateRowWidth((Element) tr);
}
}
}
+ void detachPossibleWidgetFromCell(Node cellNode) {
+ // Detach possible widget
+ Widget widget = getWidgetFromCell(cellNode);
+ if (widget != null) {
+ // Orphan.
+ setParent(widget, null);
+
+ // Physical detach.
+ cellNode.removeChild(widget.getElement());
+ }
+ }
+
protected void paintInsertColumns(final int offset,
final int numberOfColumns, boolean frozen) {
final NodeList<Node> childNodes = root.getChildNodes();
protected void paintRemoveRows(final int index, final int numberOfRows) {
for (int i = index; i < index + numberOfRows; i++) {
final Element tr = (Element) root.getChild(index);
- // TODO [[widgets]]
+ for (int c = 0; c < tr.getChildCount(); c++) {
+ detachPossibleWidgetFromCell((Element) tr.getChild(c)
+ .cast());
+ }
tr.removeFromParent();
}
recalculateSectionHeight();
for (int i = 0; i < escalatorRowsToRemove; i++) {
final Element tr = visualRowOrder
.remove(removedVisualInside.getStart());
- // TODO [[widgets]]
+ for (int c = 0; c < tr.getChildCount(); c++) {
+ detachPossibleWidgetFromCell((Element) tr.getChild(
+ c).cast());
+ }
tr.removeFromParent();
rowTopPosMap.remove(tr);
}
final ListIterator<Element> iter = visualRowOrder
.listIterator(visualRowOrder.size());
for (int i = 0; i < -neededEscalatorRowsDiff; i++) {
- // TODO [[widgets]]
final Element last = iter.previous();
+ for (int c = 0; c < last.getChildCount(); c++) {
+ detachPossibleWidgetFromCell((Element) last.getChild(c)
+ .cast());
+ }
last.removeFromParent();
iter.remove();
}
fireEvent(new RowVisibilityChangeEvent(visibleRangeStart,
visibleRowCount));
}
+
+ /**
+ * Accesses the package private method Widget#setParent()
+ *
+ * @param widget
+ * The widget to access
+ * @param parent
+ * The parent to set
+ */
+ static native final void setParent(Widget widget, Widget parent)
+ /*-{
+ widget.@com.google.gwt.user.client.ui.Widget::setParent(Lcom/google/gwt/user/client/ui/Widget;)(parent);
+ }-*/;
+
+ /**
+ * Returns the widget from a cell node or <code>null</code> if there is no
+ * widget in the cell
+ *
+ * @param cellNode
+ * The cell node
+ */
+ static Widget getWidgetFromCell(Node cellNode) {
+ Node possibleWidgetNode = cellNode.getFirstChild();
+ if (possibleWidgetNode != null
+ && possibleWidgetNode.getNodeType() == Node.ELEMENT_NODE) {
+ return Util.findWidget((Element) possibleWidgetNode, null);
+ }
+ return null;
+ }
}
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.ui.IsWidget;
+import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ui.grid.FlyweightRow.CellIterator;
/**
private final int column;
private final FlyweightRow row;
+
private CellIterator currentIterator = null;
- public FlyweightCell(final FlyweightRow row, final int column) {
+ private final Escalator escalator;
+
+ public FlyweightCell(final FlyweightRow row, final int column,
+ Escalator escalator) {
this.row = row;
this.column = column;
+ this.escalator = escalator;
}
@Override
}
}
}
+
+ @Override
+ public Widget getWidget() {
+ return Escalator.getWidgetFromCell(getElement());
+ }
+
+ @Override
+ public void setWidget(Widget widget) {
+
+ Widget oldWidget = getWidget();
+
+ // Validate
+ if (oldWidget == widget) {
+ return;
+ }
+
+ // Detach old child.
+ if (oldWidget != null) {
+ // Orphan.
+ Escalator.setParent(oldWidget, null);
+
+ // Physical detach.
+ getElement().removeChild(oldWidget.getElement());
+ }
+
+ // Remove any previous text nodes from previous
+ // setInnerText/setInnerHTML
+ getElement().removeAllChildren();
+
+ // Attach new child.
+ if (widget != null) {
+ // Detach new child from old parent.
+ widget.removeFromParent();
+
+ // Physical attach.
+ DOM.appendChild(getElement(), widget.getElement());
+
+ Escalator.setParent(widget, escalator);
+ }
+ }
+
+ @Override
+ public void setWidget(IsWidget w) {
+ setWidget(Widget.asWidgetOrNull(w));
+ }
+
}
void addCells(final int index, final int numberOfColumns) {
for (int i = 0; i < numberOfColumns; i++) {
final int col = index + i;
- cells.add(col, new FlyweightCell(this, col));
+ cells.add(col, new FlyweightCell(this, col, escalator));
}
updateRestOfCells(index + numberOfColumns);
}
private void updateRestOfCells(final int startPos) {
// update the column number for the cells to the right
for (int col = startPos; col < cells.size(); col++) {
- cells.set(col, new FlyweightCell(this, col));
+ cells.set(col, new FlyweightCell(this, col, escalator));
}
}
import com.google.gwt.core.shared.GWT;
import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.data.DataChangeHandler;
import com.vaadin.client.data.DataSource;
import com.vaadin.shared.util.SharedUtil;
/**
* List of columns in the grid. Order defines the visible order.
*/
- private final List<GridColumn<T>> columns = new ArrayList<GridColumn<T>>();
+ private final List<GridColumn<?, T>> columns = new ArrayList<GridColumn<?, T>>();
private DataSource<T> dataSource;
/**
* The column groups rows added to the grid
*/
- private final List<ColumnGroupRow> columnGroupRows = new ArrayList<ColumnGroupRow>();
+ private final List<ColumnGroupRow<T>> columnGroupRows = new ArrayList<ColumnGroupRow<T>>();
/**
* Are the headers for the columns visible
*/
private boolean columnFootersVisible = false;
- private GridColumn<T> lastFrozenColumn;
+ private GridColumn<?, T> lastFrozenColumn;
/**
* Base class for grid columns internally used by the Grid. The user should
* use {@link GridColumn} when creating new columns.
*
+ * @param <C>
+ * the column type
+ *
* @param <T>
* the row type
*/
- public static abstract class AbstractGridColumn<T> {
+ public static abstract class AbstractGridColumn<C, T> {
/**
* The grid the column is associated with
*/
private String footer;
+ /**
+ * Renderer for rendering a value into the cell
+ */
+ private Renderer<C> renderer = new Renderer<C>() {
+
+ @Override
+ public void renderCell(Cell cell, C value) {
+ if (value instanceof Widget) {
+ cell.setWidget((Widget) value);
+ } else if (value instanceof String) {
+ cell.getElement().setInnerText(value.toString());
+ } else {
+ throw new IllegalArgumentException(
+ "Cell value cannot be converted into a String. Please use a custom renderer to convert the value.");
+ }
+ }
+ };
+
+ /**
+ * Constructs a new column.
+ */
+ public AbstractGridColumn() {
+
+ }
+
+ /**
+ * Constructs a new column with a custom renderer.
+ *
+ * @param renderer
+ * The renderer to use for rendering the cells
+ */
+ public AbstractGridColumn(Renderer<C> renderer) {
+ if (renderer == null) {
+ throw new IllegalArgumentException("Renderer cannot be null.");
+ }
+ this.renderer = renderer;
+ }
+
/**
* Internally used by the grid to set itself
*
} else {
conf.removeColumns(index, 1);
}
-
- // TODO should update body as well
}
this.visible = visible;
}
/**
- * Returns the text that should be displayed in the cell.
+ * Returns the data that should be rendered into the cell. By default
+ * returning Strings and Widgets are supported. If the return type is a
+ * String then it will be treated as preformatted text.
+ * <p>
+ * To support other types you will need to pass a custom renderer to the
+ * column via the column constructor.
*
* @param row
* The row object that provides the cell content.
*
* @return The cell content
*/
- public abstract String getValue(T row);
+ public abstract C getValue(T row);
+
+ /**
+ * The renderer to render the cell width. By default renders the data as
+ * a String or adds the widget into the cell if the column type is of
+ * widget type.
+ *
+ * @return The renderer to render the cell content with
+ */
+ public Renderer<C> getRenderer() {
+ return renderer;
+ }
/**
* Finds the index of this column instance
/**
* Gets the header/footer caption value
*
+ * @param column
+ * The column to get the value for.
+ *
* @return The value that should be rendered for the column caption
*/
- public abstract String getColumnValue(GridColumn column);
+ public abstract String getColumnValue(GridColumn<?, T> column);
/**
* Gets the group caption value
* The group for with the caption value should be returned
* @return The value that should be rendered for the column caption
*/
- public abstract String getGroupValue(ColumnGroup group);
+ public abstract String getGroupValue(ColumnGroup<T> group);
/**
* Is the row visible in the header/footer
*
+ * @param row
+ * the row to check
+ *
* @return <code>true</code> if the row should be visible
*/
- public abstract boolean isRowVisible(ColumnGroupRow row);
+ public abstract boolean isRowVisible(ColumnGroupRow<T> row);
/**
* Should the first row be visible
// column headers
for (Cell cell : cellsToUpdate) {
int columnIndex = cell.getColumn();
- GridColumn column = columns.get(columnIndex);
+ GridColumn<?, T> column = columns.get(columnIndex);
cell.getElement().setInnerText(getColumnValue(column));
}
}
// Adjust for previous invisible header rows
- ColumnGroupRow groupRow = null;
+ ColumnGroupRow<T> groupRow = null;
for (int i = 0, realIndex = 0; i < columnGroupRows.size(); i++) {
groupRow = columnGroupRows.get(i);
if (isRowVisible(groupRow)) {
for (Cell cell : cellsToUpdate) {
int columnIndex = cell.getColumn();
- GridColumn column = columns.get(columnIndex);
- ColumnGroup group = getGroupForColumn(groupRow, column);
+ GridColumn<?, T> column = columns.get(columnIndex);
+ ColumnGroup<T> group = getGroupForColumn(groupRow, column);
if (group != null) {
// FIXME Should merge the group cells when escalator
return new HeaderFooterEscalatorUpdater(escalator.getHeader(), true) {
@Override
- public boolean isRowVisible(ColumnGroupRow row) {
+ public boolean isRowVisible(ColumnGroupRow<T> row) {
return row.isHeaderVisible();
}
@Override
- public String getGroupValue(ColumnGroup group) {
+ public String getGroupValue(ColumnGroup<T> group) {
return group.getHeaderCaption();
}
@Override
- public String getColumnValue(GridColumn column) {
+ public String getColumnValue(GridColumn<?, T> column) {
return column.getHeaderCaption();
}
}
for (Cell cell : cellsToUpdate) {
- String value = getColumn(cell.getColumn())
- .getValue(rowData);
- cell.getElement().setInnerText(value);
+ GridColumn column = getColumn(cell.getColumn());
+ Object value = column.getValue(rowData);
+ column.getRenderer().renderCell(cell, value);
}
}
return new HeaderFooterEscalatorUpdater(escalator.getFooter(), false) {
@Override
- public boolean isRowVisible(ColumnGroupRow row) {
+ public boolean isRowVisible(ColumnGroupRow<T> row) {
return row.isFooterVisible();
}
@Override
- public String getGroupValue(ColumnGroup group) {
+ public String getGroupValue(ColumnGroup<T> group) {
return group.getFooterCaption();
}
@Override
- public String getColumnValue(GridColumn column) {
+ public String getColumnValue(GridColumn<?, T> column) {
return column.getFooterCaption();
}
// Count needed rows
int totalRows = firstRowIsVisible ? 1 : 0;
- for (ColumnGroupRow row : columnGroupRows) {
+ for (ColumnGroupRow<T> row : columnGroupRows) {
if (isHeader ? row.isHeaderVisible() : row.isFooterVisible()) {
totalRows++;
}
* @param column
* the column to add
*/
- public void addColumn(GridColumn<T> column) {
+ public void addColumn(GridColumn<?, T> column) {
ColumnConfiguration conf = escalator.getColumnConfiguration();
addColumn(column, conf.getColumnCount());
}
* @param column
* the column to add
*/
- public void addColumn(GridColumn<T> column, int index) {
+ public void addColumn(GridColumn<?, T> column, int index) {
// Register this grid instance with the column
- ((AbstractGridColumn<T>) column).setGrid(this);
+ ((AbstractGridColumn<?, T>) column).setGrid(this);
columns.add(index, column);
conf.insertColumns(index, 1);
if (lastFrozenColumn != null
- && ((AbstractGridColumn<T>) lastFrozenColumn)
+ && ((AbstractGridColumn<?, T>) lastFrozenColumn)
.findIndexOfColumn() < index) {
refreshFrozenColumns();
}
* @param column
* the column to remove
*/
- public void removeColumn(GridColumn<T> column) {
+ public void removeColumn(GridColumn<?, T> column) {
int columnIndex = columns.indexOf(column);
columns.remove(columnIndex);
// de-register column with grid
- ((AbstractGridColumn<T>) column).setGrid(null);
+ ((AbstractGridColumn<?, T>) column).setGrid(null);
ColumnConfiguration conf = escalator.getColumnConfiguration();
conf.removeColumns(columnIndex, 1);
*
* @return A unmodifiable list of the columns in the grid
*/
- public List<GridColumn<T>> getColumns() {
- return Collections.unmodifiableList(new ArrayList<GridColumn<T>>(
+ public List<GridColumn<?, T>> getColumns() {
+ return Collections.unmodifiableList(new ArrayList<GridColumn<?, T>>(
columns));
}
* @throws IllegalArgumentException
* if the column index does not exist in the grid
*/
- public GridColumn<T> getColumn(int index) throws IllegalArgumentException {
+ public GridColumn<?, T> getColumn(int index)
+ throws IllegalArgumentException {
if (index < 0 || index >= columns.size()) {
throw new IllegalStateException("Column not found.");
}
*
* @return a column group row instance you can use to add column groups
*/
- public ColumnGroupRow addColumnGroupRow() {
- ColumnGroupRow row = new ColumnGroupRow(this);
+ public ColumnGroupRow<T> addColumnGroupRow() {
+ ColumnGroupRow<T> row = new ColumnGroupRow<T>(this);
columnGroupRows.add(row);
refreshHeader();
refreshFooter();
* the index where the column group row should be added
* @return a column group row instance you can use to add column groups
*/
- public ColumnGroupRow addColumnGroupRow(int rowIndex) {
- ColumnGroupRow row = new ColumnGroupRow(this);
+ public ColumnGroupRow<T> addColumnGroupRow(int rowIndex) {
+ ColumnGroupRow<T> row = new ColumnGroupRow<T>(this);
columnGroupRows.add(rowIndex, row);
refreshHeader();
refreshFooter();
* @param row
* The row to remove
*/
- public void removeColumnGroupRow(ColumnGroupRow row) {
+ public void removeColumnGroupRow(ColumnGroupRow<T> row) {
columnGroupRows.remove(row);
refreshHeader();
refreshFooter();
* @return a unmodifiable list of column group rows
*
*/
- public List<ColumnGroupRow> getColumnGroupRows() {
- return Collections.unmodifiableList(new ArrayList<ColumnGroupRow>(
+ public List<ColumnGroupRow<T>> getColumnGroupRows() {
+ return Collections.unmodifiableList(new ArrayList<ColumnGroupRow<T>>(
columnGroupRows));
}
* @return A column group for the row and column or <code>null</code> if not
* found.
*/
- private static ColumnGroup getGroupForColumn(ColumnGroupRow row,
- GridColumn column) {
- for (ColumnGroup group : row.getGroups()) {
- List<GridColumn> columns = group.getColumns();
+ private ColumnGroup<T> getGroupForColumn(ColumnGroupRow<T> row,
+ GridColumn<?, T> column) {
+ for (ColumnGroup<T> group : row.getGroups()) {
+ List<GridColumn<?, T>> columns = group.getColumns();
if (columns.contains(column)) {
return group;
}
* @throws IllegalArgumentException
* if {@code lastFrozenColumn} is not a column from this grid
*/
- public void setLastFrozenColumn(GridColumn<T> lastFrozenColumn) {
+ public void setLastFrozenColumn(GridColumn<?, T> lastFrozenColumn) {
this.lastFrozenColumn = lastFrozenColumn;
refreshFrozenColumns();
}
* @return the rightmost frozen column in the grid, or <code>null</code> if
* no columns are frozen.
*/
- public GridColumn<T> getLastFrozenColumn() {
+ public GridColumn<?, T> getLastFrozenColumn() {
return lastFrozenColumn;
}
}
/**
* Represents a column in the {@link Grid}.
*
+ * @param <C>
+ * The column type
+ *
* @param <T>
* The row type
*
* @since 7.2
* @author Vaadin Ltd
*/
-public abstract class GridColumn<T> extends Grid.AbstractGridColumn<T> {
+public abstract class GridColumn<C, T> extends Grid.AbstractGridColumn<C, T> {
/*
* This class is a convenience class so you do not have to reference
/**
* Custom implementation of the custom grid column using a String[] to
- * represent the cell value
+ * represent the cell value and String as a column type.
*/
- private class CustomGridColumn extends GridColumn<String[]> {
+ private class CustomGridColumn extends GridColumn<String, String[]> {
private final int columnIndex;
* The index of the column to update
*/
private void updateColumnFromStateChangeEvent(int columnIndex) {
- GridColumn<String[]> column = getWidget().getColumn(columnIndex);
+ GridColumn<?, String[]> column = getWidget().getColumn(columnIndex);
GridColumnState columnState = getState().columns.get(columnIndex);
updateColumnFromState(column, columnState);
}
* @param state
* The state to get the data from
*/
- private static void updateColumnFromState(GridColumn<String[]> column,
+ private static void updateColumnFromState(GridColumn<?, String[]> column,
GridColumnState state) {
column.setVisible(state.visible);
column.setHeaderCaption(state.header);
// FIXME When something changes the header/footer rows will be
// re-created. At some point we should optimize this so partial updates
// can be made on the header/footer.
- for (ColumnGroupRow row : getWidget().getColumnGroupRows()) {
+ for (ColumnGroupRow<String[]> row : getWidget().getColumnGroupRows()) {
getWidget().removeColumnGroupRow(row);
}
for (ColumnGroupRowState rowState : getState().columnGroupRows) {
- ColumnGroupRow row = getWidget().addColumnGroupRow();
+ ColumnGroupRow<String[]> row = getWidget().addColumnGroupRow();
row.setFooterVisible(rowState.footerVisible);
row.setHeaderVisible(rowState.headerVisible);
for (ColumnGroupState groupState : rowState.groups) {
- List<GridColumn> columns = new ArrayList<GridColumn>();
+ List<GridColumn<String, String[]>> columns = new ArrayList<GridColumn<String, String[]>>();
for (String columnId : groupState.columns) {
CustomGridColumn column = columnIdToColumn.get(columnId);
columns.add(column);
}
- ColumnGroup group = row.addGroup(columns
+ ColumnGroup<String[]> group = row.addGroup(columns
.toArray(new GridColumn[columns.size()]));
group.setFooterCaption(groupState.footer);
group.setHeaderCaption(groupState.header);
--- /dev/null
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client.ui.grid;
+
+/**
+ * Renderer for rending a value <T> into cell.
+ * <p>
+ * You can add a renderer to any column by overring the
+ * {@link GridColumn#getRenderer()} method and returning your own renderer. You
+ * can retrieve the cell element using {@link Cell#getElement()}.
+ *
+ * @param <T>
+ * The row type
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public interface Renderer<T> {
+
+ /**
+ * Called whenever the {@link Grid} updates a cell
+ *
+ * @param cell
+ * The cell that gets updated
+ *
+ * @param data
+ * The row data object
+ */
+ public void renderCell(Cell cell, T data);
+}