Parcourir la source

Client-side Grid header/footer rewrite: add simple footer support (#13334)

Currently supported:
* A single-row hard-coded header
* A zero-row hard-coded footer
* Text captions

TODO:
* 0..n headers and footers
* Column spanning
* HTML content
* Widget content
* Component content
* Sorting/Indicators
* Server side API
* Shared state handling

Change-Id: Ic85051b16ef77791f1fdae78fca47a0729e1c43d
tags/7.4.0.beta1
Johannes Dahlström il y a 10 ans
Parent
révision
f49e1227ae

+ 83
- 58
client/src/com/vaadin/client/ui/grid/Grid.java Voir le fichier

@@ -45,6 +45,7 @@ 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.GridStaticSection.StaticRow;
import com.vaadin.client.ui.grid.renderers.ComplexRenderer;
import com.vaadin.client.ui.grid.renderers.TextRenderer;
import com.vaadin.client.ui.grid.renderers.WidgetRenderer;
@@ -409,7 +410,9 @@ public class Grid<T> extends Composite implements
*/
private Escalator escalator = GWT.create(Escalator.class);

private GridHeader header = GWT.create(GridHeader.class);
private final GridHeader header = GWT.create(GridHeader.class);

private final GridFooter footer = GWT.create(GridFooter.class);

/**
* List of columns in the grid. Order defines the visible order.
@@ -1292,6 +1295,57 @@ public class Grid<T> extends Composite implements
}
}

protected class StaticSectionUpdater implements EscalatorUpdater {

private GridStaticSection<?> section;

public StaticSectionUpdater(GridStaticSection<?> section) {
super();
this.section = section;
}

@Override
public void update(Row row, Iterable<FlyweightCell> cellsToUpdate) {
StaticRow<?> gridRow = section.getRow(row.getRow());

final List<Integer> columnIndices = getVisibleColumnIndices();

for (FlyweightCell cell : cellsToUpdate) {
int index = columnIndices.get(cell.getColumn());
gridRow.getRenderer().render(cell,
gridRow.getCell(index).getText());

activeCellHandler.updateActiveCellStyle(cell);
}
}

@Override
public void preAttach(Row row, Iterable<FlyweightCell> cellsToAttach) {
}

@Override
public void postAttach(Row row, Iterable<FlyweightCell> attachedCells) {
}

@Override
public void preDetach(Row row, Iterable<FlyweightCell> cellsToDetach) {
}

@Override
public void postDetach(Row row, Iterable<FlyweightCell> detachedCells) {
}

private List<Integer> getVisibleColumnIndices() {
List<Integer> indices = new ArrayList<Integer>(getColumnCount());
for (int i = 0; i < getColumnCount(); i++) {
if (getColumn(i).isVisible()) {
indices.add(i);
}
}
return indices;
}
};

/**
* Creates a new instance.
*/
@@ -1353,48 +1407,7 @@ public class Grid<T> extends Composite implements
* @return the updater that updates the data in the escalator.
*/
private EscalatorUpdater createHeaderUpdater() {
return new EscalatorUpdater() {

@Override
public void update(Row row, Iterable<FlyweightCell> cellsToUpdate) {
GridHeader.HeaderRow headerRow = header.getRow(row.getRow());

int colIndex = -1;
for (FlyweightCell cell : cellsToUpdate) {
activeCellHandler.updateActiveCellStyle(cell);

if (colIndex == -1) {
colIndex = cell.getColumn();
}
while (!columns.get(colIndex).isVisible()) {
colIndex++;
}

headerRow.getRenderer().render(cell,
headerRow.getCell(colIndex).getText());

colIndex++;
}
}

@Override
public void preAttach(Row row, Iterable<FlyweightCell> cellsToAttach) {
}

@Override
public void postAttach(Row row,
Iterable<FlyweightCell> attachedCells) {
}

@Override
public void preDetach(Row row, Iterable<FlyweightCell> cellsToDetach) {
}

@Override
public void postDetach(Row row,
Iterable<FlyweightCell> detachedCells) {
}
};
return new StaticSectionUpdater(header);
}

private EscalatorUpdater createBodyUpdater() {
@@ -1537,7 +1550,7 @@ public class Grid<T> extends Composite implements
* @return the updater that updates the data in the escalator.
*/
private EscalatorUpdater createFooterUpdater() {
return EscalatorUpdater.NULL;
return new StaticSectionUpdater(footer);
}

/**
@@ -1552,18 +1565,10 @@ public class Grid<T> extends Composite implements
* the footer
*/
private void refreshRowContainer(RowContainer rows,
boolean firstRowIsVisible, boolean isHeader) {

// Count needed rows
int totalRows = firstRowIsVisible ? 1 : 0;
for (ColumnGroupRow<T> row : columnGroupRows) {
if (isHeader ? row.isHeaderVisible() : row.isFooterVisible()) {
totalRows++;
}
}
GridStaticSection<?> section) {

// Add or Remove rows on demand
int rowDiff = totalRows - rows.getRowCount();
int rowDiff = section.getRows().size() - rows.getRowCount();
if (rowDiff > 0) {
rows.insertRows(0, rowDiff);
} else if (rowDiff < 0) {
@@ -1580,8 +1585,7 @@ public class Grid<T> extends Composite implements
* Refreshes all header rows
*/
void refreshHeader() {
refreshRowContainer(escalator.getHeader(), isColumnHeadersVisible(),
true);
refreshRowContainer(escalator.getHeader(), header);
}

/**
@@ -1595,8 +1599,7 @@ public class Grid<T> extends Composite implements
* Refreshes all footer rows
*/
void refreshFooter() {
refreshRowContainer(escalator.getFooter(), isColumnFootersVisible(),
false);
refreshRowContainer(escalator.getFooter(), footer);
}

/**
@@ -1638,6 +1641,7 @@ public class Grid<T> extends Composite implements
columns.add(index, column);

header.addColumn(column, index);
footer.addColumn(column, index);

// Register this grid instance with the column
((AbstractGridColumn<?, T>) column).setGrid(this);
@@ -1739,7 +1743,9 @@ public class Grid<T> extends Composite implements
int columnIndex = columns.indexOf(column);
int visibleIndex = findVisibleColumnIndex(column);
columns.remove(columnIndex);

header.removeColumn(columnIndex);
footer.removeColumn(columnIndex);

// de-register column with grid
((AbstractGridColumn<?, T>) column).setGrid(null);
@@ -1991,6 +1997,25 @@ public class Grid<T> extends Composite implements
return null;
}

/**
* Returns the header section of this grid. The default header contains a
* single row displaying the column captions.
*
* @return the header
*/
public GridHeader getHeader() {
return header;
}

/**
* Returns the footer section of this grid. The default footer is empty.
*
* @return the footer
*/
public GridFooter getFooter() {
return footer;
}

/**
* {@inheritDoc}
* <p>

+ 66
- 0
client/src/com/vaadin/client/ui/grid/GridFooter.java Voir le fichier

@@ -0,0 +1,66 @@
/*
* 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;

import java.util.Collections;
import java.util.List;

/**
* Represents the footer section of a Grid. The footer is always empty.
*
* TODO Arbitrary number of footer rows (zero by default)
*
* TODO Merging footer cells
*
* TODO Widgets in cells
*
* TODO HTML in cells
*
* @since
* @author Vaadin Ltd
*/
public class GridFooter extends GridStaticSection<GridFooter.FooterRow> {

/**
* A single row in a grid Footer section.
*
*/
public static class FooterRow extends
GridStaticSection.StaticRow<FooterCell> {

@Override
protected FooterCell createCell() {
return new FooterCell();
}
}

/**
* A single cell in a grid Footer row. Has a textual caption.
*
*/
public static class FooterCell extends GridStaticSection.StaticCell {
}

@Override
protected FooterRow createRow() {
return new FooterRow();
}

@Override
protected List<FooterRow> createRowList() {
return Collections.emptyList();
}
}

+ 14
- 49
client/src/com/vaadin/client/ui/grid/GridHeader.java Voir le fichier

@@ -15,18 +15,15 @@
*/
package com.vaadin.client.ui.grid;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.vaadin.client.ui.grid.renderers.TextRenderer;

/**
* Represents the header section of a Grid. A header consists of a single header
* row containing a header cell for each column. Each cell has a simple textual
* caption.
*
* TODO Arbitrary number of header rows (zero included)
* TODO Arbitrary number of header rows (zero included, one by default)
*
* TODO Merging header cells
*
@@ -39,67 +36,35 @@ import com.vaadin.client.ui.grid.renderers.TextRenderer;
* @since
* @author Vaadin Ltd
*/
public class GridHeader {
public class GridHeader extends GridStaticSection<GridHeader.HeaderRow> {

/**
* A single row in a grid header section.
*
* @since
* @author Vaadin Ltd
*/
public static class HeaderRow {

private List<HeaderCell> cells = new ArrayList<HeaderCell>();

private Renderer<String> renderer = new TextRenderer();

public HeaderCell getCell(int index) {
return cells.get(index);
}

protected void addCell(int index) {
cells.add(index, new HeaderCell());
}

protected void removeCell(int index) {
cells.remove(index);
}
public static class HeaderRow extends
GridStaticSection.StaticRow<HeaderCell> {

protected Renderer<String> getRenderer() {
return renderer;
@Override
protected HeaderCell createCell() {
return new HeaderCell();
}
}

/**
* A single cell in a grid header row. Has a textual caption.
*
* @since
* @author Vaadin Ltd
*/
public static class HeaderCell {

private String text = "";

public void setText(String text) {
this.text = text;
}

public String getText() {
return text;
}
}

private List<HeaderRow> rows = Arrays.asList(new HeaderRow());

public HeaderRow getRow(int index) {
return rows.get(index);
public static class HeaderCell extends GridStaticSection.StaticCell {
}

protected void addColumn(GridColumn<?, ?> column, int index) {
getRow(0).addCell(index);
@Override
protected HeaderRow createRow() {
return new HeaderRow();
}

protected void removeColumn(int index) {
getRow(0).removeCell(index);
@Override
protected List<HeaderRow> createRowList() {
return Arrays.asList(createRow());
}
}

+ 141
- 0
client/src/com/vaadin/client/ui/grid/GridStaticSection.java Voir le fichier

@@ -0,0 +1,141 @@
/*
* 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;

import java.util.ArrayList;
import java.util.List;

import com.vaadin.client.ui.grid.GridStaticSection.StaticRow;
import com.vaadin.client.ui.grid.renderers.TextRenderer;

/**
* Abstract base class for Grid header and footer sections.
*
* @since
* @author Vaadin Ltd
* @param <ROWTYPE>
* the type of the rows in the section
*/
abstract class GridStaticSection<ROWTYPE extends StaticRow<?>> {

/**
* A header or footer cell. Has a simple textual caption.
*
* TODO HTML content
*
* TODO Widget content
*/
static class StaticCell {

private String text = "";

/**
* Sets the text displayed in this cell.
*
* @param text
* a plain text caption
*/
public void setText(String text) {
this.text = text;
}

/**
* Returns the text displayed in this cell.
*
* @return the plain text caption
*/
public String getText() {
return text;
}
}

/**
* Abstract base class for Grid header and footer rows.
*
* @param <CELLTYPE>
* the type of the cells in the row
*/
abstract static class StaticRow<CELLTYPE extends StaticCell> {

private List<CELLTYPE> cells = new ArrayList<CELLTYPE>();

private Renderer<String> renderer = new TextRenderer();

/**
* Returns the cell at the given position in this row.
*
* @param index
* the position of the cell
* @return the cell at the index
* @throws IndexOutOfBoundsException
* if the index is out of bounds
*/
public CELLTYPE getCell(int index) {
return cells.get(index);
}

protected void addCell(int index) {
cells.add(index, createCell());
}

protected void removeCell(int index) {
cells.remove(index);
}

protected Renderer<String> getRenderer() {
return renderer;
}

protected abstract CELLTYPE createCell();
}

private List<ROWTYPE> rows = createRowList();

/**
* Returns the row at the given position in this section.
*
* @param index
* the position of the row
* @return the row at the index
* @throws IndexOutOfBoundsException
* if the index is out of bounds
*/
public ROWTYPE getRow(int index) {
return rows.get(index);
}

protected List<ROWTYPE> getRows() {
return rows;
}

protected void addColumn(GridColumn<?, ?> column, int index) {
for (ROWTYPE row : getRows()) {
row.addCell(index);
}
}

protected void removeColumn(int index) {
for (ROWTYPE row : getRows()) {
row.removeCell(index);
}
}

protected List<ROWTYPE> createRowList() {
return new ArrayList<ROWTYPE>();
}

protected abstract ROWTYPE createRow();
}

+ 8
- 0
uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeaturesTest.java Voir le fichier

@@ -71,6 +71,14 @@ public abstract class GridBasicFeaturesTest extends MultiBrowserTest {
return headerCells;
}

protected List<TestBenchElement> getGridFooterRowCells() {
List<TestBenchElement> footerCells = new ArrayList<TestBenchElement>();
for (int i = 0; i < getGridElement().getFooterCount(); ++i) {
footerCells.addAll(getGridElement().getFooterCells(i));
}
return footerCells;
}

private Object executeScript(String script, WebElement element) {
final WebDriver driver = getDriver();
if (driver instanceof JavascriptExecutor) {

+ 36
- 0
uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridFooterTest.java Voir le fichier

@@ -0,0 +1,36 @@
/*
* 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.basicfeatures;

import static org.junit.Assert.assertEquals;

import java.util.List;

import org.junit.Test;

import com.vaadin.testbench.TestBenchElement;

public class GridFooterTest extends GridBasicClientFeaturesTest {

@Test
public void testFooterVisibility() throws Exception {
openTestURL();

// Footer should have zero rows by default
List<TestBenchElement> cells = getGridFooterRowCells();
assertEquals(0, cells.size());
}
}

+ 45
- 2
uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridHeaderTest.java Voir le fichier

@@ -26,11 +26,54 @@ import com.vaadin.testbench.TestBenchElement;
public class GridHeaderTest extends GridBasicClientFeaturesTest {

@Test
public void testHeaderVisible() throws Exception {
public void testHeaderVisibility() throws Exception {
openTestURL();

// Column headers should be visible
// Column headers should be visible by default
List<TestBenchElement> cells = getGridHeaderRowCells();
assertEquals(GridBasicFeatures.COLUMNS, cells.size());
}

@Test
public void testHeaderCaptions() throws Exception {
openTestURL();

List<TestBenchElement> cells = getGridHeaderRowCells();

int i = 0;
for (TestBenchElement cell : cells) {
assertText("Column " + i, cell);
i++;
}
}

@Test
public void testHeadersWithInvisibleColumns() throws Exception {
openTestURL();

selectMenuPath("Component", "Columns", "Column 1", "Visible");
selectMenuPath("Component", "Columns", "Column 3", "Visible");

List<TestBenchElement> cells = getGridHeaderRowCells();
assertEquals(GridBasicFeatures.COLUMNS - 2, cells.size());

assertText("Column 0", cells.get(0));
assertText("Column 2", cells.get(1));
assertText("Column 4", cells.get(2));

selectMenuPath("Component", "Columns", "Column 3", "Visible");

cells = getGridHeaderRowCells();
assertEquals(GridBasicFeatures.COLUMNS - 1, cells.size());

assertText("Column 0", cells.get(0));
assertText("Column 2", cells.get(1));
assertText("Column 3", cells.get(2));
assertText("Column 4", cells.get(3));
}

private static void assertText(String text, TestBenchElement e) {
// TBE.getText returns "" if the element is scrolled out of view
assertEquals(text, e.getAttribute("innerHTML"));
}
}

+ 34
- 7
uitest/src/com/vaadin/tests/widgetset/client/grid/GridBasicClientFeatures.java Voir le fichier

@@ -25,6 +25,7 @@ import com.vaadin.client.ui.grid.FlyweightCell;
import com.vaadin.client.ui.grid.Grid;
import com.vaadin.client.ui.grid.Grid.SelectionMode;
import com.vaadin.client.ui.grid.GridColumn;
import com.vaadin.client.ui.grid.GridHeader.HeaderCell;
import com.vaadin.client.ui.grid.Renderer;
import com.vaadin.client.ui.grid.datasources.ListDataSource;
import com.vaadin.client.ui.grid.renderers.DateRenderer;
@@ -192,35 +193,61 @@ public class GridBasicClientFeatures extends
});
}

// Set captions to column headers

for (int i = 0; i < COLUMNS; ++i) {
HeaderCell cell = grid.getHeader().getRow(0).getCell(i);
cell.setText("Column " + i);
}

//
// Populate the menu
//

createStateMenu();
createColumnsMenu();

grid.getElement().getStyle().setZIndex(0);
add(grid);
}

private void createStateMenu() {
String[] selectionModePath = { "Component", "State", "Selection mode" };

addMenuCommand("multi", new ScheduledCommand() {
@Override
public void execute() {
grid.setSelectionMode(SelectionMode.MULTI);
}
}, "Component", "State", "Selection mode");
}, selectionModePath);

addMenuCommand("single", new ScheduledCommand() {

@Override
public void execute() {
grid.setSelectionMode(SelectionMode.SINGLE);
}
}, "Component", "State", "Selection mode");
}, selectionModePath);

addMenuCommand("none", new ScheduledCommand() {

@Override
public void execute() {
grid.setSelectionMode(SelectionMode.NONE);
}
}, "Component", "State", "Selection mode");
}, selectionModePath);
}

grid.getElement().getStyle().setZIndex(0);
add(grid);
private void createColumnsMenu() {

for (int i = 0; i < COLUMNS; i++) {
final int index = i;
addMenuCommand("Visible", new ScheduledCommand() {
@Override
public void execute() {
grid.getColumn(index).setVisible(
!grid.getColumn(index).isVisible());
}
}, "Component", "Columns", "Column " + i);
}
}

/**

Chargement…
Annuler
Enregistrer