getEscalatorUpdater().preDetach(flyweightRow,
flyweightRow.getCells());
- for (int c = 0; c < tr.getChildCount(); c++) {
- // TODO this should be WidgetRenderer's responsibility
- detachPossibleWidgetFromCell((Element) tr.getChild(c).cast());
- }
tr.removeFromParent();
getEscalatorUpdater().postDetach(flyweightRow,
for (FlyweightCell cell : cells) {
Element cellElement = cell.getElement();
- detachPossibleWidgetFromCell(cellElement);
cellElement.removeFromParent();
}
}
}
- 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();
* 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
.listIterator(visualRowOrder.size());
for (int i = 0; i < -neededEscalatorRowsDiff; i++) {
final Element last = iter.previous();
- for (int c = 0; c < last.getChildCount(); c++) {
- detachPossibleWidgetFromCell((Element) last.getChild(c)
- .cast());
- }
last.removeFromParent();
iter.remove();
}
body.getLogicalRowIndex(body.visualRowOrder.getLast()) + 1);
}
- /**
- * 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
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.Unit;
-import com.google.gwt.user.client.ui.IsWidget;
-import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ui.grid.FlyweightRow.CellIterator;
/**
}
}
}
-
- /**
- * @deprecated Will be removed in further refactorings
- */
- @Deprecated
- public Widget getWidget() {
- return Escalator.getWidgetFromCell(getElement());
- }
-
- /**
- * @deprecated Will be removed in further refactorings
- */
- @Deprecated
- 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.
- getElement().appendChild(widget.getElement());
-
- Escalator.setParent(widget, escalator);
- }
- }
-
- /**
- * @deprecated Will be removed in further refactorings
- */
- @Deprecated
- public void setWidget(IsWidget w) {
- setWidget(Widget.asWidgetOrNull(w));
- }
-
}
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HasVisibility;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.Util;
import com.vaadin.client.data.DataChangeHandler;
import com.vaadin.client.data.DataSource;
import com.vaadin.client.ui.SubPartAware;
import com.vaadin.client.ui.grid.renderers.ComplexRenderer;
import com.vaadin.client.ui.grid.renderers.TextRenderer;
+import com.vaadin.client.ui.grid.renderers.WidgetRenderer;
import com.vaadin.client.ui.grid.selection.MultiSelectionRenderer;
import com.vaadin.shared.ui.grid.GridConstants;
import com.vaadin.shared.ui.grid.HeightMode;
return new EscalatorUpdater() {
@Override
- public void update(Row row, Iterable<FlyweightCell> cellsToUpdate) {
- int rowIndex = row.getRow();
- if (dataSource == null) {
- setCellsLoading(cellsToUpdate);
- return;
+ public void preAttach(Row row, Iterable<FlyweightCell> cellsToAttach) {
+ // NOP
+ }
+
+ @Override
+ public void postAttach(Row row,
+ Iterable<FlyweightCell> attachedCells) {
+ for (FlyweightCell cell : attachedCells) {
+ Renderer renderer = findRenderer(cell);
+ if (renderer instanceof WidgetRenderer) {
+ WidgetRenderer widgetRenderer = (WidgetRenderer) renderer;
+
+ Widget widget = widgetRenderer.createWidget();
+ assert widget != null : "WidgetRenderer.createWidget() returned null. It should return a widget.";
+ assert widget.getParent() == null : "WidgetRenderer.createWidget() returned a widget which already is attached.";
+ assert cell.getElement().getChildCount() == 0 : "Cell content should be empty when adding Widget";
+
+ // Physical attach
+ cell.getElement().appendChild(widget.getElement());
+
+ // Logical attach
+ setParent(widget, Grid.this);
+ }
}
+ }
+ @Override
+ public void update(Row row, Iterable<FlyweightCell> cellsToUpdate) {
+ int rowIndex = row.getRow();
T rowData = dataSource.getRow(rowIndex);
if (rowData == null) {
- setCellsLoading(cellsToUpdate);
return;
}
for (FlyweightCell cell : cellsToUpdate) {
GridColumn column = getColumnFromVisibleIndex(cell
.getColumn());
- if (column != null) {
- Object value = column.getValue(rowData);
- column.getRenderer().render(cell, value);
- }
- }
- }
-
- private void setCellsLoading(Iterable<FlyweightCell> cellsToUpdate) {
- for (FlyweightCell cell : cellsToUpdate) {
- cell.getElement().setInnerText("...");
+ assert column != null : "Column was not found from cell ("
+ + cell.getColumn() + "," + cell.getRow() + ")";
+ Object value = column.getValue(rowData);
+ Renderer renderer = findRenderer(cell);
+ renderer.render(cell, value);
}
}
- @Override
- public void preAttach(Row row, Iterable<FlyweightCell> cellsToAttach) {
- // NOOP for now
- }
-
- @Override
- public void postAttach(Row row,
- Iterable<FlyweightCell> attachedCells) {
- // NOOP for now
- }
-
@Override
public void preDetach(Row row, Iterable<FlyweightCell> cellsToDetach) {
- // NOOP for now
+ for (FlyweightCell cell : cellsToDetach) {
+ Renderer renderer = findRenderer(cell);
+ if (renderer instanceof WidgetRenderer) {
+ Widget w = Util.findWidget(cell.getElement()
+ .getFirstChildElement(), Widget.class);
+ if (w != null) {
+
+ // Logical detach
+ setParent(w, null);
+
+ // Physical detach
+ cell.getElement().removeChild(w.getElement());
+ }
+ }
+ }
}
@Override
return null;
}
+ private Renderer findRenderer(FlyweightCell cell) {
+ GridColumn column = getColumnFromVisibleIndex(cell.getColumn());
+ assert column != null : "Could not find column at index:"
+ + cell.getColumn();
+ return column.getRenderer();
+ }
+
/**
* Removes a column from the grid.
*
private boolean isSelected(T row) {
return false;
}
+
+ /**
+ * Accesses the package private method Widget#setParent()
+ *
+ * @param widget
+ * The widget to access
+ * @param parent
+ * The parent to set
+ */
+ private 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);
+ }-*/;
}
--- /dev/null
+/*
+ * Copyright 2000-2014 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.renderers;
+
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.Util;
+import com.vaadin.client.ui.grid.FlyweightCell;
+
+/**
+ * A renderer for rendering widgets into cells.
+ *
+ * @since 7.4
+ * @author Vaadin Ltd
+ * @param <T>
+ * the row data type
+ * @param <W>
+ * the Widget type
+ */
+public abstract class WidgetRenderer<T, W extends Widget> extends
+ ComplexRenderer<T> {
+
+ /**
+ * Creates a widget to attach to a cell. The widgets will be attached to the
+ * cell after the cell element has been attached to DOM.
+ *
+ * @return widget to attach to a cell. All returned instances should be new
+ * widget instances without a parent.
+ */
+ public abstract W createWidget();
+
+ @Override
+ public void render(FlyweightCell cell, T data) {
+ W w = Util.findWidget(cell.getElement().getFirstChildElement(), null);
+ assert w != null : "Widget not found in cell (" + cell.getColumn()
+ + "," + cell.getRow() + ")";
+ render(cell, data, w);
+ }
+
+ /**
+ * Renders a cell with a widget. This provides a way to update any
+ * information in the widget that is cell specific. Do not detach the Widget
+ * here, it will be done automatically by the Grid when the widget is no
+ * longer needed.
+ *
+ * @param cell
+ * the cell to render
+ * @param data
+ * the data of the cell
+ * @param widget
+ * the widget embedded in the cell
+ */
+ public abstract void render(FlyweightCell cell, T data, W widget);
+
+}
--- /dev/null
+/*
+ * Copyright 2000-2014 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.tests.components.grid;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.openqa.selenium.Alert;
+import org.openqa.selenium.WebElement;
+
+import com.vaadin.testbench.By;
+import com.vaadin.testbench.TestBenchElement;
+import com.vaadin.testbench.elements.NativeButtonElement;
+import com.vaadin.testbench.elements.NativeSelectElement;
+import com.vaadin.testbench.elements.ServerClass;
+import com.vaadin.tests.tb3.MultiBrowserTest;
+import com.vaadin.tests.widgetset.client.grid.GridClientColumnRendererConnector.Renderers;
+import com.vaadin.tests.widgetset.server.grid.GridClientColumnRenderers;
+
+/**
+ * Tests Grid client side renderers
+ *
+ * @since 7.4
+ * @author Vaadin Ltd
+ */
+public class GridClientRenderers extends MultiBrowserTest {
+
+ @Override
+ protected Class<?> getUIClass() {
+ return GridClientColumnRenderers.class;
+ }
+
+ @ServerClass("com.vaadin.tests.widgetset.server.grid.GridClientColumnRenderers.GridController")
+ public static class MyClientGridElement extends GridElement {
+ }
+
+ @Test
+ public void addWidgetRenderer() throws Exception {
+ openTestURL();
+
+ // Add widget renderer column
+ $(NativeSelectElement.class).first().selectByText(
+ Renderers.WIDGET_RENDERER.toString());
+ $(NativeButtonElement.class).caption("Add").first().click();
+
+ // Click the button in cell 1,1
+ TestBenchElement cell = getGrid().getCell(1, 1);
+ WebElement gwtButton = cell.findElement(By.tagName("button"));
+ gwtButton.click();
+
+ // Should be an alert visible
+ Alert alert = driver.switchTo().alert();
+ assertEquals(alert.getText(), "Click");
+ }
+
+ @Test
+ public void detachAndAttachGrid() {
+ openTestURL();
+
+ // Add widget renderer column
+ $(NativeSelectElement.class).first().selectByText(
+ Renderers.WIDGET_RENDERER.toString());
+ $(NativeButtonElement.class).caption("Add").first().click();
+
+ // Detach and re-attach the Grid
+ $(NativeButtonElement.class).caption("DetachAttach").first().click();
+
+ // Click the button in cell 1,1
+ TestBenchElement cell = getGrid().getCell(1, 1);
+ WebElement gwtButton = cell.findElement(By.tagName("button"));
+ gwtButton.click();
+
+ // Should be an alert visible
+ Alert alert = driver.switchTo().alert();
+ assertEquals(alert.getText(), "Click");
+ }
+
+ private GridElement getGrid() {
+ return $(MyClientGridElement.class).first();
+ }
+}
--- /dev/null
+/*
+ * Copyright 2000-2014 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.tests.widgetset.client.grid;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.HasWidgets;
+import com.vaadin.client.ui.AbstractComponentConnector;
+import com.vaadin.client.ui.grid.FlyweightCell;
+import com.vaadin.client.ui.grid.Grid;
+import com.vaadin.client.ui.grid.GridColumn;
+import com.vaadin.client.ui.grid.Renderer;
+import com.vaadin.client.ui.grid.datasources.ListDataSource;
+import com.vaadin.client.ui.grid.renderers.DateRenderer;
+import com.vaadin.client.ui.grid.renderers.HtmlRenderer;
+import com.vaadin.client.ui.grid.renderers.NumberRenderer;
+import com.vaadin.client.ui.grid.renderers.TextRenderer;
+import com.vaadin.client.ui.grid.renderers.WidgetRenderer;
+import com.vaadin.shared.ui.Connect;
+import com.vaadin.tests.widgetset.server.grid.GridClientColumnRenderers;
+
+@Connect(GridClientColumnRenderers.GridController.class)
+public class GridClientColumnRendererConnector extends
+ AbstractComponentConnector {
+
+ public static enum Renderers {
+ TEXT_RENDERER, WIDGET_RENDERER, HTML_RENDERER, NUMBER_RENDERER, DATE_RENDERER;
+ }
+
+ @Override
+ protected void init() {
+ Grid<String> grid = getWidget();
+ grid.setColumnHeadersVisible(false);
+
+ // Generated some column data
+ List<String> columnData = new ArrayList<String>();
+ for (int i = 0; i < 100; i++) {
+ columnData.add(String.valueOf(i));
+ }
+
+ // Provide data as data source
+ grid.setDataSource(new ListDataSource<String>(columnData));
+
+ // Add a column to display the data in
+ grid.addColumn(createColumnWithRenderer(Renderers.TEXT_RENDERER));
+
+ // Handle RPC calls
+ registerRpc(GridClientColumnRendererRpc.class,
+ new GridClientColumnRendererRpc() {
+
+ @Override
+ public void addColumn(Renderers renderer) {
+
+ if (renderer == Renderers.NUMBER_RENDERER) {
+ getWidget().addColumn(
+ createNumberColumnWithRenderer(renderer));
+ } else if (renderer == Renderers.DATE_RENDERER) {
+ getWidget().addColumn(
+ createDateColumnWithRenderer(renderer));
+
+ } else {
+ getWidget().addColumn(
+ createColumnWithRenderer(renderer));
+ }
+ }
+
+ @Override
+ public void detachAttach() {
+
+ // Detach
+ HasWidgets parent = (HasWidgets) getWidget()
+ .getParent();
+ parent.remove(getWidget());
+
+ // Re-attach
+ parent.add(getWidget());
+ }
+ });
+ }
+
+ /**
+ * Creates a a renderer for a {@link Renderers}
+ */
+ private Renderer createRenderer(Renderers renderer) {
+ switch (renderer) {
+ case TEXT_RENDERER:
+ return new TextRenderer();
+
+ case WIDGET_RENDERER:
+ return new WidgetRenderer<String, Button>() {
+
+ @Override
+ public Button createWidget() {
+ return new Button("", new ClickHandler() {
+
+ @Override
+ public void onClick(ClickEvent event) {
+ Window.alert("Click");
+ }
+ });
+ }
+
+ @Override
+ public void render(FlyweightCell cell, String data,
+ Button button) {
+ button.setHTML(data);
+ }
+ };
+
+ case HTML_RENDERER:
+ return new HtmlRenderer() {
+
+ @Override
+ public void render(FlyweightCell cell, String htmlString) {
+ super.render(cell, "<b>" + htmlString + "</b>");
+ }
+ };
+
+ case NUMBER_RENDERER:
+ return new NumberRenderer<Long>();
+
+ case DATE_RENDERER:
+ return new DateRenderer();
+
+ default:
+ return new TextRenderer();
+ }
+ }
+
+ private GridColumn<String, String> createColumnWithRenderer(
+ Renderers renderer) {
+ return new GridColumn<String, String>(createRenderer(renderer)) {
+
+ @Override
+ public String getValue(String row) {
+ return row;
+ }
+ };
+ }
+
+ private GridColumn<Number, String> createNumberColumnWithRenderer(
+ Renderers renderer) {
+ return new GridColumn<Number, String>(createRenderer(renderer)) {
+
+ @Override
+ public Number getValue(String row) {
+ return Long.parseLong(row);
+ }
+ };
+ }
+
+ private GridColumn<Date, String> createDateColumnWithRenderer(
+ Renderers renderer) {
+ return new GridColumn<Date, String>(createRenderer(renderer)) {
+
+ @Override
+ public Date getValue(String row) {
+ return new Date();
+ }
+ };
+ }
+
+ @Override
+ public Grid<String> getWidget() {
+ return (Grid<String>) super.getWidget();
+ }
+}
--- /dev/null
+/*
+ * Copyright 2000-2014 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.tests.widgetset.client.grid;
+
+import com.vaadin.shared.communication.ClientRpc;
+import com.vaadin.tests.widgetset.client.grid.GridClientColumnRendererConnector.Renderers;
+
+public interface GridClientColumnRendererRpc extends ClientRpc {
+
+ /**
+ * Adds a new column with a specific renderer to the grid
+ *
+ */
+ void addColumn(Renderers renderer);
+
+ /**
+ * Detaches and attaches the client side Grid
+ */
+ void detachAttach();
+}
--- /dev/null
+/*
+ * Copyright 2000-2014 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.tests.widgetset.server.grid;
+
+import java.util.Arrays;
+
+import com.vaadin.annotations.Widgetset;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.widgetset.TestingWidgetSet;
+import com.vaadin.tests.widgetset.client.grid.GridClientColumnRendererConnector.Renderers;
+import com.vaadin.tests.widgetset.client.grid.GridClientColumnRendererRpc;
+import com.vaadin.ui.AbstractComponent;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Button.ClickListener;
+import com.vaadin.ui.CssLayout;
+import com.vaadin.ui.NativeButton;
+import com.vaadin.ui.NativeSelect;
+import com.vaadin.ui.UI;
+import com.vaadin.ui.VerticalLayout;
+
+@Widgetset(TestingWidgetSet.NAME)
+public class GridClientColumnRenderers extends UI {
+
+ /**
+ * Controls the grid on the client side
+ */
+ public static class GridController extends AbstractComponent {
+
+ private GridClientColumnRendererRpc rpc() {
+ return getRpcProxy(GridClientColumnRendererRpc.class);
+ }
+
+ /**
+ * Adds a new column with a renderer to the grid.
+ */
+ public void addColumn(Renderers renderer) {
+ rpc().addColumn(renderer);
+ }
+
+ /**
+ * Tests detaching and attaching grid
+ */
+ public void detachAttach() {
+ rpc().detachAttach();
+ }
+ }
+
+ @Override
+ protected void init(VaadinRequest request) {
+ final GridController controller = new GridController();
+ final CssLayout controls = new CssLayout();
+ final VerticalLayout content = new VerticalLayout();
+
+ content.addComponent(controller);
+ content.addComponent(controls);
+ setContent(content);
+
+ final NativeSelect select = new NativeSelect(
+ "Add Column with Renderer", Arrays.asList(Renderers.values()));
+ select.setValue(Renderers.TEXT_RENDERER);
+ select.setNullSelectionAllowed(false);
+ controls.addComponent(select);
+
+ NativeButton addColumnBtn = new NativeButton("Add");
+ addColumnBtn.addClickListener(new ClickListener() {
+
+ @Override
+ public void buttonClick(ClickEvent event) {
+ Renderers renderer = (Renderers) select.getValue();
+ controller.addColumn(renderer);
+ }
+ });
+ controls.addComponent(addColumnBtn);
+
+ NativeButton detachAttachBtn = new NativeButton("DetachAttach");
+ detachAttachBtn.addClickListener(new ClickListener() {
+
+ @Override
+ public void buttonClick(ClickEvent event) {
+ controller.detachAttach();
+ }
+ });
+ controls.addComponent(detachAttachBtn);
+ }
+}