diff options
author | Teemu Suo-Anttila <teemusa@vaadin.com> | 2015-04-08 13:41:37 +0300 |
---|---|---|
committer | Vaadin Code Review <review@vaadin.com> | 2015-04-10 12:40:00 +0000 |
commit | 3cb1cfec55f568838798a5afb5bf27b13ba82b42 (patch) | |
tree | a7792ed6ad3ecd81784d1e2b2227895ea15c177f /server/src/com/vaadin/ui/GridLayout.java | |
parent | 38e35c5db6621b20929d924707be149e22032997 (diff) | |
download | vaadin-framework-3cb1cfec55f568838798a5afb5bf27b13ba82b42.tar.gz vaadin-framework-3cb1cfec55f568838798a5afb5bf27b13ba82b42.zip |
Fix GridLayout declarative support (#16594)
Change-Id: I25e52a9246c9ccbc406d1f162b4e9ff69bb411ff
Diffstat (limited to 'server/src/com/vaadin/ui/GridLayout.java')
-rw-r--r-- | server/src/com/vaadin/ui/GridLayout.java | 325 |
1 files changed, 324 insertions, 1 deletions
diff --git a/server/src/com/vaadin/ui/GridLayout.java b/server/src/com/vaadin/ui/GridLayout.java index 96854c5b1b..35110b39ab 100644 --- a/server/src/com/vaadin/ui/GridLayout.java +++ b/server/src/com/vaadin/ui/GridLayout.java @@ -17,12 +17,21 @@ package com.vaadin.ui; import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; + +import org.jsoup.nodes.Attributes; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; import com.vaadin.event.LayoutEvents.LayoutClickEvent; import com.vaadin.event.LayoutEvents.LayoutClickListener; @@ -36,6 +45,8 @@ import com.vaadin.shared.ui.MarginInfo; import com.vaadin.shared.ui.gridlayout.GridLayoutServerRpc; import com.vaadin.shared.ui.gridlayout.GridLayoutState; import com.vaadin.shared.ui.gridlayout.GridLayoutState.ChildComponentData; +import com.vaadin.ui.declarative.DesignAttributeHandler; +import com.vaadin.ui.declarative.DesignContext; /** * A layout where the components are laid out on a grid using cell coordinates. @@ -1282,4 +1293,316 @@ public class GridLayout extends AbstractLayout implements public boolean isHideEmptyRowsAndColumns() { return getState(false).hideEmptyRowsAndColumns; } -} + + /** + * {@inheritDoc} + * <p> + * After reading the design, cursorY is set to point to a row outside of the + * GridLayout area. CursorX is reset to 0. + */ + @Override + public void readDesign(Element design, DesignContext designContext) { + super.readDesign(design, designContext); + + // Prepare a 2D map for reading column contents + Elements rowElements = design.getElementsByTag("row"); + List<Map<Integer, Component>> rows = new ArrayList<Map<Integer, Component>>(); + for (int i = 0; i < rowElements.size(); ++i) { + rows.add(new HashMap<Integer, Component>()); + } + setRows(Math.max(rows.size(), 1)); + + List<Integer> columnExpandRatios = new ArrayList<Integer>(); + for (int row = 0; row < rowElements.size(); ++row) { + Element rowElement = rowElements.get(row); + + // Row Expand + if (rowElement.hasAttr("expand")) { + int expand = DesignAttributeHandler.readAttribute("expand", + rowElement.attributes(), int.class); + setRowExpandRatio(row, expand); + } + + Elements cols = rowElement.children(); + + // Amount of skipped columns due to spanned components + int skippedColumns = 0; + + for (int column = 0; column < cols.size(); ++column) { + while (rows.get(row).containsKey(column + skippedColumns)) { + // Skip any spanned components + skippedColumns++; + } + + Element col = cols.get(column); + Component child = null; + + if (col.children().size() > 0) { + child = designContext.readDesign(col.child(0)); + // TODO: Currently ignoring any extra children. + // Needs Error handling? + } // Else: Empty placeholder. No child component. + + // Handle rowspan and colspan for this child component + Attributes attr = col.attributes(); + int colspan = DesignAttributeHandler.readAttribute("colspan", + attr, 1, int.class); + int rowspan = DesignAttributeHandler.readAttribute("rowspan", + attr, 1, int.class); + + for (int rowIndex = row; rowIndex < row + rowspan; ++rowIndex) { + for (int colIndex = column; colIndex < column + colspan; ++colIndex) { + if (rowIndex == rows.size()) { + // Rowspan with not enough rows. Fix by adding rows. + rows.add(new HashMap<Integer, Component>()); + } + rows.get(rowIndex) + .put(colIndex + skippedColumns, child); + } + } + + // Read column expand ratios if handling the first row. + if (row == 0) { + if (col.hasAttr("expand")) { + for (String expand : col.attr("expand").split(",")) { + columnExpandRatios.add(Integer.parseInt(expand)); + } + } else { + for (int c = 0; c < colspan; ++c) { + columnExpandRatios.add(0); + } + } + } + + skippedColumns += (colspan - 1); + } + } + + // Calculate highest column count and set columns + int colMax = 0; + for (Map<Integer, Component> cols : rows) { + if (colMax < cols.size()) { + colMax = cols.size(); + } + } + setColumns(Math.max(colMax, 1)); + + for (int i = 0; i < columnExpandRatios.size(); ++i) { + setColumnExpandRatio(i, columnExpandRatios.get(i)); + } + + // Reiterate through the 2D map and add components to GridLayout + Set<Component> visited = new HashSet<Component>(); + + // Ignore any missing components + visited.add(null); + + for (int i = 0; i < rows.size(); ++i) { + Map<Integer, Component> row = rows.get(i); + for (int j = 0; j < colMax; ++j) { + Component child = row.get(j); + if (visited.contains(child)) { + // Empty location or already handled child + continue; + } + visited.add(child); + + // Figure out col and rowspan from 2D map + int colspan = 0; + while (j + colspan + 1 < row.size() + && row.get(j + colspan + 1) == child) { + ++colspan; + } + + int rowspan = 0; + while (i + rowspan + 1 < rows.size() + && rows.get(i + rowspan + 1).get(j) == child) { + ++rowspan; + } + + // Add component with area + addComponent(child, j, i, j + colspan, i + rowspan); + } + } + // Set cursor position explicitly + setCursorY(getRows()); + setCursorX(0); + } + + @Override + public void writeDesign(Element design, DesignContext designContext) { + super.writeDesign(design, designContext); + + GridLayout def = designContext.getDefaultInstance(this); + if (components.isEmpty() + || !designContext.shouldWriteChildren(this, def)) { + return; + } + + final Map<Connector, ChildComponentData> childData = getState().childData; + + // Make a 2D map of component areas. + Component[][] componentMap = new Component[getState().rows][getState().columns]; + final Component dummyComponent = new Label(""); + + for (Component component : components) { + ChildComponentData coords = childData.get(component); + for (int row = coords.row1; row <= coords.row2; ++row) { + for (int col = coords.column1; col <= coords.column2; ++col) { + componentMap[row][col] = component; + } + } + } + + // Go through the map and write only needed column tags + Set<Connector> visited = new HashSet<Connector>(); + + // Skip the dummy placeholder + visited.add(dummyComponent); + + for (int i = 0; i < componentMap.length; ++i) { + Element row = design.appendElement("row"); + + // Row Expand + DesignAttributeHandler.writeAttribute("expand", row.attributes(), + (int) getRowExpandRatio(i), 0, int.class); + + int colspan = 1; + Element col; + for (int j = 0; j < componentMap[i].length; ++j) { + Component child = componentMap[i][j]; + if (child != null) { + if (visited.contains(child)) { + // Child has already been written in the design + continue; + } + visited.add(child); + + Element childElement = designContext.createElement(child); + col = row.appendElement("column"); + + // Write child data into design + ChildComponentData coords = childData.get(child); + + Alignment alignment = getComponentAlignment(child); + if (alignment.isMiddle()) { + childElement.attr(":middle", ""); + } else if (alignment.isBottom()) { + childElement.attr(":bottom", ""); + } + if (alignment.isCenter()) { + childElement.attr(":center", ""); + } else if (alignment.isRight()) { + childElement.attr(":right", ""); + } + + col.appendChild(childElement); + if (coords.row1 != coords.row2) { + col.attr("rowspan", "" + + (1 + coords.row2 - coords.row1)); + } + + colspan = 1 + coords.column2 - coords.column1; + if (colspan > 1) { + col.attr("colspan", "" + colspan); + } + + } else { + boolean hasExpands = false; + if (i == 0 + && lastComponentOnRow(componentMap[i], j, visited)) { + // A column with expand and no content in the end of + // first row needs to be present. + for (int c = j; c < componentMap[i].length; ++c) { + if ((int) getColumnExpandRatio(c) > 0) { + hasExpands = true; + } + } + } + + if (lastComponentOnRow(componentMap[i], j, visited) + && !hasExpands) { + continue; + } + + // Empty placeholder tag. + col = row.appendElement("column"); + + // Use colspan to make placeholders more pleasant + while (j + colspan < componentMap[i].length + && componentMap[i][j + colspan] == child) { + ++colspan; + } + + int rowspan = getRowSpan(componentMap, i, j, colspan, child); + if (colspan > 1) { + col.attr("colspan", "" + colspan); + } + if (rowspan > 1) { + col.attr("rowspan", "" + rowspan); + } + for (int x = 0; x < rowspan; ++x) { + for (int y = 0; y < colspan; ++y) { + // Mark handled columns + componentMap[i + x][j + y] = dummyComponent; + } + } + } + + // Column expands + if (i == 0) { + // Only do expands on first row + String expands = ""; + boolean expandRatios = false; + for (int c = 0; c < colspan; ++c) { + int colExpand = (int) getColumnExpandRatio(j + c); + if (colExpand > 0) { + expandRatios = true; + } + expands += (c > 0 ? "," : "") + colExpand; + } + if (expandRatios) { + col.attr("expand", expands); + } + } + + j += colspan - 1; + } + } + } + + private int getRowSpan(Component[][] compMap, int i, int j, int colspan, + Component child) { + int rowspan = 1; + while (i + rowspan < compMap.length && compMap[i + rowspan][j] == child) { + for (int k = 0; k < colspan; ++k) { + if (compMap[i + rowspan][j + k] != child) { + return rowspan; + } + } + rowspan++; + } + return rowspan; + } + + private boolean lastComponentOnRow(Component[] componentArray, int j, + Set<Connector> visited) { + while ((++j) < componentArray.length) { + Component child = componentArray[j]; + if (child != null && !visited.contains(child)) { + return false; + } + } + return true; + } + + @Override + protected Collection<String> getCustomAttributes() { + Collection<String> result = super.getCustomAttributes(); + result.add("cursor-x"); + result.add("cursor-y"); + result.add("rows"); + result.add("columns"); + return result; + } +}
\ No newline at end of file |