diff options
6 files changed, 332 insertions, 2 deletions
diff --git a/WebContent/VAADIN/themes/base/formlayout/formlayout.scss b/WebContent/VAADIN/themes/base/formlayout/formlayout.scss index a8006fdfe3..2e2a3213f9 100644 --- a/WebContent/VAADIN/themes/base/formlayout/formlayout.scss +++ b/WebContent/VAADIN/themes/base/formlayout/formlayout.scss @@ -19,6 +19,7 @@ } .#{$primaryStyleName}-errorcell, .#{$primaryStyleName}-captioncell { width: 1px; /* Don't use any extra space */ + min-width: 1px; } .#{$primaryStyleName}-captioncell .v-caption { overflow: visible; diff --git a/WebContent/VAADIN/themes/reindeer/formlayout/formlayout.scss b/WebContent/VAADIN/themes/reindeer/formlayout/formlayout.scss index 51b8a96b60..7f8871fdac 100644 --- a/WebContent/VAADIN/themes/reindeer/formlayout/formlayout.scss +++ b/WebContent/VAADIN/themes/reindeer/formlayout/formlayout.scss @@ -2,6 +2,7 @@ .#{$primaryStyleName}-errorcell { width: 13px; + min-width: 13px; } .#{$primaryStyleName}-cell .v-errorindicator { width: 13px; diff --git a/client/src/com/vaadin/client/ui/VFormLayout.java b/client/src/com/vaadin/client/ui/VFormLayout.java index bcbb3ebf7b..3719688f2b 100644 --- a/client/src/com/vaadin/client/ui/VFormLayout.java +++ b/client/src/com/vaadin/client/ui/VFormLayout.java @@ -86,7 +86,7 @@ public class VFormLayout extends SimplePanel { private static final int COLUMN_CAPTION = 0; private static final int COLUMN_ERRORFLAG = 1; - private static final int COLUMN_WIDGET = 2; + public static final int COLUMN_WIDGET = 2; private HashMap<Widget, Caption> widgetToCaption = new HashMap<Widget, Caption>(); private HashMap<Widget, ErrorFlag> widgetToError = new HashMap<Widget, ErrorFlag>(); diff --git a/client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java b/client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java index 9517619cf3..febbb5d6a2 100644 --- a/client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java +++ b/client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java @@ -15,26 +15,124 @@ */ package com.vaadin.client.ui.formlayout; +import java.util.HashMap; +import java.util.Map; + import com.google.gwt.dom.client.Element; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ComponentConnector; import com.vaadin.client.ConnectorHierarchyChangeEvent; +import com.vaadin.client.LayoutManager; import com.vaadin.client.TooltipInfo; import com.vaadin.client.WidgetUtil; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractFieldConnector; import com.vaadin.client.ui.AbstractLayoutConnector; +import com.vaadin.client.ui.PostLayoutListener; import com.vaadin.client.ui.VFormLayout; import com.vaadin.client.ui.VFormLayout.Caption; import com.vaadin.client.ui.VFormLayout.ErrorFlag; import com.vaadin.client.ui.VFormLayout.VFormLayoutTable; +import com.vaadin.client.ui.layout.ElementResizeEvent; +import com.vaadin.client.ui.layout.ElementResizeListener; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.MarginInfo; import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutState; import com.vaadin.ui.FormLayout; @Connect(FormLayout.class) -public class FormLayoutConnector extends AbstractLayoutConnector { +public class FormLayoutConnector extends AbstractLayoutConnector implements + PostLayoutListener { + + private Map<ComponentConnector, String> oldMaxWidths = null; + + private static final ElementResizeListener dummyFirstCellResizeListener = new ElementResizeListener() { + @Override + public void onElementResize(ElementResizeEvent e) { + // Ignore event, listener added just to make measurements available + } + }; + + // Detects situations when there's something inside the FormLayout that + // prevents it from shrinking + private ElementResizeListener resizeListener = new ElementResizeListener() { + @Override + public void onElementResize(ElementResizeEvent e) { + LayoutManager layoutManager = getLayoutManager(); + double tableWidth = layoutManager + .getOuterWidthDouble(getWidget().table.getElement()); + double ownWidth = layoutManager.getInnerWidthDouble(getWidget() + .getElement()); + if (ownWidth < tableWidth) { + // Something inside the table prevents it from shrinking, + // temporarily force column widths + double excessWidth = tableWidth - ownWidth; + + // All td elements in the component column have the same width, + // so we only need to check the width of the first one to know + // how wide the column is. + Element firstComponentTd = findFirstComponentTd(); + if (firstComponentTd == null) { + // Can't do anything if there are no rows + return; + } + + double componentColWidth = layoutManager + .getOuterWidthDouble(firstComponentTd); + + if (componentColWidth == -1) { + // Didn't get a proper width reading, best to not touch + // anything + return; + } + + // Restrict content td width + // Round down to prevent interactions with fractional sizes of + // other columns + int targetWidth = (int) Math.floor(componentColWidth + - excessWidth); + + // Target might be negative if captions are wider than the total + // available width + targetWidth = Math.max(0, targetWidth); + + if (oldMaxWidths == null) { + oldMaxWidths = new HashMap<ComponentConnector, String>(); + } + + for (ComponentConnector child : getChildComponents()) { + Element childElement = child.getWidget().getElement(); + if (!oldMaxWidths.containsKey(child)) { + oldMaxWidths.put(child, + childElement.getPropertyString("maxWidth")); + } + childElement.getStyle().setPropertyPx("maxWidth", + targetWidth); + layoutManager.reportOuterWidth(child, targetWidth); + } + } + } + }; + + @Override + protected void init() { + super.init(); + getLayoutManager().addElementResizeListener( + getWidget().table.getElement(), resizeListener); + getLayoutManager().addElementResizeListener(getWidget().getElement(), + resizeListener); + addComponentCellListener(); + } + + @Override + public void onUnregister() { + getLayoutManager().removeElementResizeListener( + getWidget().table.getElement(), resizeListener); + getLayoutManager().removeElementResizeListener( + getWidget().getElement(), resizeListener); + removeComponentCellListener(); + super.onUnregister(); + } @Override public AbstractOrderedLayoutState getState() { @@ -57,6 +155,8 @@ public class FormLayoutConnector extends AbstractLayoutConnector { VFormLayout formLayout = getWidget(); VFormLayoutTable formLayoutTable = getWidget().table; + removeComponentCellListener(); + int childId = 0; formLayoutTable.setRowCount(getChildComponents().size()); @@ -87,6 +187,33 @@ public class FormLayoutConnector extends AbstractLayoutConnector { formLayoutTable.cleanReferences(oldChild.getWidget()); } + addComponentCellListener(); + } + + private void addComponentCellListener() { + Element td = findFirstComponentTd(); + if (td != null) { + getLayoutManager().addElementResizeListener(td, + dummyFirstCellResizeListener); + } + } + + private void removeComponentCellListener() { + Element td = findFirstComponentTd(); + if (td != null) { + getLayoutManager().removeElementResizeListener(td, + dummyFirstCellResizeListener); + } + } + + private Element findFirstComponentTd() { + VFormLayoutTable table = getWidget().table; + if (table.getRowCount() == 0) { + return null; + } else { + return table.getCellFormatter().getElement(0, + VFormLayoutTable.COLUMN_WIDGET); + } } @Override @@ -148,4 +275,20 @@ public class FormLayoutConnector extends AbstractLayoutConnector { return true; } + @Override + public void postLayout() { + if (oldMaxWidths != null) { + for (ComponentConnector child : getChildComponents()) { + Element childNode = child.getWidget().getElement(); + String oldValue = oldMaxWidths.get(child); + if (oldValue == null) { + childNode.getStyle().clearProperty("maxWidth"); + } else { + childNode.getStyle().setProperty("maxWidth", oldValue); + } + } + oldMaxWidths = null; + } + } + } diff --git a/uitest/src/com/vaadin/tests/components/formlayout/FormLayoutResizing.java b/uitest/src/com/vaadin/tests/components/formlayout/FormLayoutResizing.java new file mode 100644 index 0000000000..7ed91979e2 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/formlayout/FormLayoutResizing.java @@ -0,0 +1,85 @@ +/* + * 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.formlayout; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Component; +import com.vaadin.ui.CssLayout; +import com.vaadin.ui.FormLayout; +import com.vaadin.ui.Table; +import com.vaadin.ui.TextField; +import com.vaadin.ui.VerticalLayout; + +public class FormLayoutResizing extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + FormLayout form1 = createForm("Table", createTable()); + + CssLayout cssLayout = new CssLayout(createTable()); + cssLayout.setWidth("100%"); + FormLayout form2 = createForm("Wrap", cssLayout); + + final VerticalLayout view = new VerticalLayout(form1, form2); + view.setWidth("400px"); + + addComponent(view); + + addComponent(new Button("Toggle width", new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + if ((int) view.getWidth() == 400) { + view.setWidth("600px"); + } else { + view.setWidth("400px"); + } + } + })); + } + + private static FormLayout createForm(String caption, Component table) { + table.setCaption(caption); + + TextField tf = new TextField("Text field"); + tf.setWidth("100%"); + + FormLayout form = new FormLayout(); + form.setWidth("100%"); + + form.addComponent(tf); + form.addComponent(table); + return form; + } + + private static Table createTable() { + Table table = new Table(); + table.setHeight("100px"); + + table.addContainerProperty("Column 1", String.class, ""); + table.addContainerProperty("Column 2", String.class, ""); + table.setWidth("100%"); + return table; + } + + @Override + protected String getTestDescription() { + return "100% wide Table inside FormLayout should resize when the layout width changes"; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/formlayout/FormLayoutResizingTest.java b/uitest/src/com/vaadin/tests/components/formlayout/FormLayoutResizingTest.java new file mode 100644 index 0000000000..3285503aeb --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/formlayout/FormLayoutResizingTest.java @@ -0,0 +1,100 @@ +/* + * 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.formlayout; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; + +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.elements.FormLayoutElement; +import com.vaadin.testbench.elements.TableElement; +import com.vaadin.testbench.parallel.BrowserUtil; +import com.vaadin.tests.tb3.MultiBrowserThemeTest; + +public class FormLayoutResizingTest extends MultiBrowserThemeTest { + @Test + public void testTableResizing() { + openTestURL(); + + List<TableElement> tables = $(TableElement.class).all(); + Assert.assertEquals("Sanity check", 2, tables.size()); + + List<FormLayoutElement> layouts = $(FormLayoutElement.class).all(); + Assert.assertEquals("Sanity check", 2, layouts.size()); + + ButtonElement toggleButton = $(ButtonElement.class).first(); + + int[] originalWidths = getWidths(tables); + + // In some browser and theme combinations, the original rendering is + // slightly too wide. Find out this overshoot and adjust the expected + // table width accordingly. + for (int i = 0; i < 2; i++) { + FormLayoutElement formLayout = layouts.get(i); + WebElement table = formLayout.findElement(By.tagName("table")); + int overshoot = table.getSize().width - formLayout.getSize().width; + originalWidths[i] -= overshoot; + } + + // Toggle size from 400 px to 600 px + toggleButton.click(); + + int[] expandedWidths = getWidths(tables); + + Assert.assertEquals("Table should have grown ", + originalWidths[0] + 200, expandedWidths[0]); + Assert.assertEquals("Wrapped table should have grown ", + originalWidths[1] + 200, expandedWidths[1]); + + // Toggle size from 600 px to 400 px + toggleButton.click(); + + int[] collapsedWidths = getWidths(tables); + + Assert.assertEquals("Table should return to original width ", + originalWidths[0], collapsedWidths[0]); + Assert.assertEquals("Wrapped table should return to original width ", + originalWidths[1], collapsedWidths[1]); + + // Verify that growing is not restricted after triggering the fix + // Toggle size from 400 px to 600 px + toggleButton.click(); + + expandedWidths = getWidths(tables); + + Assert.assertEquals("Table should have grown ", + originalWidths[0] + 200, expandedWidths[0]); + Assert.assertEquals("Wrapped table should have grown ", + originalWidths[1] + 200, expandedWidths[1]); + } + + @Override + protected boolean useNativeEventsForIE() { + return !BrowserUtil.isIE(getDesiredCapabilities(), 11); + } + + private static int[] getWidths(List<TableElement> tables) { + int[] widths = new int[tables.size()]; + for (int i = 0; i < widths.length; i++) { + widths[i] = tables.get(i).getSize().width; + } + return widths; + } +} |