]> source.dussan.org Git - vaadin-framework.git/commitdiff
Ensure GridLayout rounds available space down instead of up (#15451)
authorArtur Signell <artur@vaadin.com>
Sun, 7 Jun 2015 20:06:45 +0000 (23:06 +0300)
committerVaadin Code Review <review@vaadin.com>
Fri, 12 Jun 2015 11:51:16 +0000 (11:51 +0000)
Store measured widths and heights as doubles to be able to round later

Change-Id: Id0e91702dd62ba362f53317e8520f85b46f19769

client/src/com/vaadin/client/LayoutManager.java
client/src/com/vaadin/client/MeasuredSize.java
client/src/com/vaadin/client/WidgetUtil.java
client/src/com/vaadin/client/ui/VGridLayout.java
uitest/src/com/vaadin/tests/components/gridlayout/GridLayoutFractionalSizeAndAlignment.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/gridlayout/GridLayoutFractionalSizeAndAlignmentTest.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/widgetset/client/ScrollableGridLayoutConnector.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/widgetset/server/ScrollableGridLayout.java [new file with mode: 0644]

index 3f189bcea8f9885593dd16909645a1cee1b7490f..102e618f5e346492d2aec1013186e7620b4a0fb0 100644 (file)
@@ -946,7 +946,7 @@ public class LayoutManager {
      * given element, provided that it has been measured. These elements are
      * guaranteed to be measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -954,6 +954,9 @@ public class LayoutManager {
      * 
      * -1 is returned if the element has not been measured. If 0 is returned, it
      * might indicate that the element is not attached to the DOM.
+     * <p>
+     * The value returned by this method is always rounded up. To get the exact
+     * outer width, use {@link #getOuterHeightDouble(Element)}
      * 
      * @param element
      *            the element to get the measured size for
@@ -961,6 +964,31 @@ public class LayoutManager {
      *         borders) of the element in pixels.
      */
     public final int getOuterHeight(Element element) {
+        assert needsMeasure(element) : "Getting measurement for element that is not measured";
+        return (int) Math.ceil(getMeasuredSize(element, nullSize)
+                .getOuterHeight());
+    }
+
+    /**
+     * Gets the outer height (including margins, paddings and borders) of the
+     * given element, provided that it has been measured. These elements are
+     * guaranteed to be measured:
+     * <ul>
+     * <li>ManagedLayouts and their child Connectors
+     * <li>Elements for which there is at least one ElementResizeListener
+     * <li>Elements for which at least one ManagedLayout has registered a
+     * dependency
+     * </ul>
+     * 
+     * -1 is returned if the element has not been measured. If 0 is returned, it
+     * might indicate that the element is not attached to the DOM.
+     * 
+     * @param element
+     *            the element to get the measured size for
+     * @return the measured outer height (including margins, paddings and
+     *         borders) of the element in pixels.
+     */
+    public final double getOuterHeightDouble(Element element) {
         assert needsMeasure(element) : "Getting measurement for element that is not measured";
         return getMeasuredSize(element, nullSize).getOuterHeight();
     }
@@ -970,7 +998,7 @@ public class LayoutManager {
      * given element, provided that it has been measured. These elements are
      * guaranteed to be measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -978,6 +1006,9 @@ public class LayoutManager {
      * 
      * -1 is returned if the element has not been measured. If 0 is returned, it
      * might indicate that the element is not attached to the DOM.
+     * <p>
+     * The value returned by this method is always rounded up. To get the exact
+     * outer width, use {@link #getOuterWidthDouble(Element)}
      * 
      * @param element
      *            the element to get the measured size for
@@ -985,6 +1016,31 @@ public class LayoutManager {
      *         borders) of the element in pixels.
      */
     public final int getOuterWidth(Element element) {
+        assert needsMeasure(element) : "Getting measurement for element that is not measured";
+        return (int) Math.ceil(getMeasuredSize(element, nullSize)
+                .getOuterWidth());
+    }
+
+    /**
+     * Gets the outer width (including margins, paddings and borders) of the
+     * given element, provided that it has been measured. These elements are
+     * guaranteed to be measured:
+     * <ul>
+     * <li>ManagedLayouts and their child Connectors
+     * <li>Elements for which there is at least one ElementResizeListener
+     * <li>Elements for which at least one ManagedLayout has registered a
+     * dependency
+     * </ul>
+     * 
+     * -1 is returned if the element has not been measured. If 0 is returned, it
+     * might indicate that the element is not attached to the DOM.
+     * 
+     * @param element
+     *            the element to get the measured size for
+     * @return the measured outer width (including margins, paddings and
+     *         borders) of the element in pixels.
+     */
+    public final double getOuterWidthDouble(Element element) {
         assert needsMeasure(element) : "Getting measurement for element that is not measured";
         return getMeasuredSize(element, nullSize).getOuterWidth();
     }
@@ -994,7 +1050,7 @@ public class LayoutManager {
      * given element, provided that it has been measured. These elements are
      * guaranteed to be measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -1002,6 +1058,9 @@ public class LayoutManager {
      * 
      * -1 is returned if the element has not been measured. If 0 is returned, it
      * might indicate that the element is not attached to the DOM.
+     * <p>
+     * The value returned by this method is always rounded up. To get the exact
+     * outer width, use {@link #getInnerHeightDouble(Element)}
      * 
      * @param element
      *            the element to get the measured size for
@@ -1009,6 +1068,31 @@ public class LayoutManager {
      *         borders) of the element in pixels.
      */
     public final int getInnerHeight(Element element) {
+        assert needsMeasure(element) : "Getting measurement for element that is not measured";
+        return (int) Math.ceil(getMeasuredSize(element, nullSize)
+                .getInnerHeight());
+    }
+
+    /**
+     * Gets the inner height (excluding margins, paddings and borders) of the
+     * given element, provided that it has been measured. These elements are
+     * guaranteed to be measured:
+     * <ul>
+     * <li>ManagedLayouts and their child Connectors
+     * <li>Elements for which there is at least one ElementResizeListener
+     * <li>Elements for which at least one ManagedLayout has registered a
+     * dependency
+     * </ul>
+     * 
+     * -1 is returned if the element has not been measured. If 0 is returned, it
+     * might indicate that the element is not attached to the DOM.
+     * 
+     * @param element
+     *            the element to get the measured size for
+     * @return the measured inner height (excluding margins, paddings and
+     *         borders) of the element in pixels.
+     */
+    public final double getInnerHeightDouble(Element element) {
         assert needsMeasure(element) : "Getting measurement for element that is not measured";
         return getMeasuredSize(element, nullSize).getInnerHeight();
     }
@@ -1018,7 +1102,7 @@ public class LayoutManager {
      * given element, provided that it has been measured. These elements are
      * guaranteed to be measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -1026,6 +1110,9 @@ public class LayoutManager {
      * 
      * -1 is returned if the element has not been measured. If 0 is returned, it
      * might indicate that the element is not attached to the DOM.
+     * <p>
+     * The value returned by this method is always rounded up. To get the exact
+     * outer width, use {@link #getOuterHeightDouble(Element)}
      * 
      * @param element
      *            the element to get the measured size for
@@ -1033,6 +1120,31 @@ public class LayoutManager {
      *         borders) of the element in pixels.
      */
     public final int getInnerWidth(Element element) {
+        assert needsMeasure(element) : "Getting measurement for element that is not measured";
+        return (int) Math.ceil(getMeasuredSize(element, nullSize)
+                .getInnerWidth());
+    }
+
+    /**
+     * Gets the inner width (excluding margins, paddings and borders) of the
+     * given element, provided that it has been measured. These elements are
+     * guaranteed to be measured:
+     * <ul>
+     * <li>ManagedLayouts and their child Connectors
+     * <li>Elements for which there is at least one ElementResizeListener
+     * <li>Elements for which at least one ManagedLayout has registered a
+     * dependency
+     * </ul>
+     * 
+     * -1 is returned if the element has not been measured. If 0 is returned, it
+     * might indicate that the element is not attached to the DOM.
+     * 
+     * @param element
+     *            the element to get the measured size for
+     * @return the measured inner width (excluding margins, paddings and
+     *         borders) of the element in pixels.
+     */
+    public final double getInnerWidthDouble(Element element) {
         assert needsMeasure(element) : "Getting measurement for element that is not measured";
         return getMeasuredSize(element, nullSize).getInnerWidth();
     }
@@ -1042,7 +1154,7 @@ public class LayoutManager {
      * provided that it has been measured. These elements are guaranteed to be
      * measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -1067,7 +1179,7 @@ public class LayoutManager {
      * element, provided that it has been measured. These elements are
      * guaranteed to be measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -1092,7 +1204,7 @@ public class LayoutManager {
      * provided that it has been measured. These elements are guaranteed to be
      * measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -1116,7 +1228,7 @@ public class LayoutManager {
      * Gets the top border of the given element, provided that it has been
      * measured. These elements are guaranteed to be measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -1139,7 +1251,7 @@ public class LayoutManager {
      * Gets the left border of the given element, provided that it has been
      * measured. These elements are guaranteed to be measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -1162,7 +1274,7 @@ public class LayoutManager {
      * Gets the bottom border of the given element, provided that it has been
      * measured. These elements are guaranteed to be measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -1185,7 +1297,7 @@ public class LayoutManager {
      * Gets the right border of the given element, provided that it has been
      * measured. These elements are guaranteed to be measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -1209,7 +1321,7 @@ public class LayoutManager {
      * element, provided that it has been measured. These elements are
      * guaranteed to be measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -1233,7 +1345,7 @@ public class LayoutManager {
      * Gets the top padding of the given element, provided that it has been
      * measured. These elements are guaranteed to be measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -1256,7 +1368,7 @@ public class LayoutManager {
      * Gets the left padding of the given element, provided that it has been
      * measured. These elements are guaranteed to be measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -1279,7 +1391,7 @@ public class LayoutManager {
      * Gets the bottom padding of the given element, provided that it has been
      * measured. These elements are guaranteed to be measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -1302,7 +1414,7 @@ public class LayoutManager {
      * Gets the right padding of the given element, provided that it has been
      * measured. These elements are guaranteed to be measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -1325,7 +1437,7 @@ public class LayoutManager {
      * Gets the top margin of the given element, provided that it has been
      * measured. These elements are guaranteed to be measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -1348,7 +1460,7 @@ public class LayoutManager {
      * Gets the right margin of the given element, provided that it has been
      * measured. These elements are guaranteed to be measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -1371,7 +1483,7 @@ public class LayoutManager {
      * Gets the bottom margin of the given element, provided that it has been
      * measured. These elements are guaranteed to be measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -1394,7 +1506,7 @@ public class LayoutManager {
      * Gets the left margin of the given element, provided that it has been
      * measured. These elements are guaranteed to be measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -1417,7 +1529,7 @@ public class LayoutManager {
      * Gets the combined top & bottom margin of the given element, provided that
      * they have been measured. These elements are guaranteed to be measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
@@ -1439,7 +1551,7 @@ public class LayoutManager {
      * Gets the combined left & right margin of the given element, provided that
      * they have been measured. These elements are guaranteed to be measured:
      * <ul>
-     * <li>ManagedLayotus and their child Connectors
+     * <li>ManagedLayouts and their child Connectors
      * <li>Elements for which there is at least one ElementResizeListener
      * <li>Elements for which at least one ManagedLayout has registered a
      * dependency
index ba8fca417b63d5af5f18729d6b6445404e39b89d..20990083503d6ef3451b28b286284566f877f188 100644 (file)
@@ -45,8 +45,8 @@ public class MeasuredSize {
         }
     }
 
-    private int width = -1;
-    private int height = -1;
+    private double width = -1;
+    private double height = -1;
 
     private int[] paddings = new int[4];
     private int[] borders = new int[4];
@@ -54,11 +54,11 @@ public class MeasuredSize {
 
     private FastStringSet dependents = FastStringSet.create();
 
-    public int getOuterHeight() {
+    public double getOuterHeight() {
         return height;
     }
 
-    public int getOuterWidth() {
+    public double getOuterWidth() {
         return width;
     }
 
@@ -86,17 +86,17 @@ public class MeasuredSize {
         return sizes[0] + sizes[2];
     }
 
-    public int getInnerHeight() {
+    public double getInnerHeight() {
         return height - sumHeights(margins) - sumHeights(borders)
                 - sumHeights(paddings);
     }
 
-    public int getInnerWidth() {
+    public double getInnerWidth() {
         return width - sumWidths(margins) - sumWidths(borders)
                 - sumWidths(paddings);
     }
 
-    public boolean setOuterHeight(int height) {
+    public boolean setOuterHeight(double height) {
         if (this.height != height) {
             this.height = height;
             return true;
@@ -105,7 +105,7 @@ public class MeasuredSize {
         }
     }
 
-    public boolean setOuterWidth(int width) {
+    public boolean setOuterWidth(double width) {
         if (this.width != width) {
             this.width = width;
             return true;
@@ -238,20 +238,20 @@ public class MeasuredSize {
         Profiler.leave("Measure borders");
 
         Profiler.enter("Measure height");
-        int requiredHeight = WidgetUtil.getRequiredHeight(element);
-        int marginHeight = sumHeights(margins);
-        int oldHeight = height;
-        int oldWidth = width;
-        if (setOuterHeight(requiredHeight + marginHeight)) {
+        double requiredHeight = WidgetUtil.getRequiredHeightDouble(element);
+        double outerHeight = requiredHeight + sumHeights(margins);
+        double oldHeight = height;
+        if (setOuterHeight(outerHeight)) {
             debugSizeChange(element, "Height (outer)", oldHeight, height);
             heightChanged = true;
         }
         Profiler.leave("Measure height");
 
         Profiler.enter("Measure width");
-        int requiredWidth = WidgetUtil.getRequiredWidth(element);
-        int marginWidth = sumWidths(margins);
-        if (setOuterWidth(requiredWidth + marginWidth)) {
+        double requiredWidth = WidgetUtil.getRequiredWidthDouble(element);
+        double outerWidth = requiredWidth + sumWidths(margins);
+        double oldWidth = width;
+        if (setOuterWidth(outerWidth)) {
             debugSizeChange(element, "Width (outer)", oldWidth, width);
             widthChanged = true;
         }
@@ -270,7 +270,7 @@ public class MeasuredSize {
     }
 
     private void debugSizeChange(Element element, String sizeChangeType,
-            int changedFrom, int changedTo) {
+            double changedFrom, double changedTo) {
         debugSizeChange(element, sizeChangeType, String.valueOf(changedFrom),
                 String.valueOf(changedTo));
     }
index e89e87595347a78eb8acbf94470ca9be31cd2077..fca6fbccbc7da20a1c0d902ff9cc926f58ff2e85 100644 (file)
@@ -540,6 +540,29 @@ public class WidgetUtil {
         return reqWidth;
     }
 
+    /**
+     * Gets the border-box width for the given element, i.e. element width +
+     * border + padding.
+     * 
+     * @param element
+     *            The element to check
+     * @return The border-box width for the element
+     */
+    public static double getRequiredWidthDouble(
+            com.google.gwt.dom.client.Element element) {
+        double reqWidth = getRequiredWidthBoundingClientRectDouble(element);
+        if (BrowserInfo.get().isIE() && !BrowserInfo.get().isIE8()) {
+            double csWidth = getRequiredWidthComputedStyleDouble(element);
+            if (csWidth > reqWidth && csWidth < (reqWidth + 1)) {
+                // IE9 rounds reqHeight to integers BUT sometimes reports wrong
+                // csHeight it seems, so we only use csHeight if it is within a
+                // rounding error
+                return csWidth;
+            }
+        }
+        return reqWidth;
+    }
+
     /**
      * Gets the border-box height for the given element, i.e. element height +
      * border + padding. Always rounds up to nearest integer.
@@ -566,6 +589,29 @@ public class WidgetUtil {
         return reqHeight;
     }
 
+    /**
+     * Gets the border-box height for the given element, i.e. element height +
+     * border + padding.
+     * 
+     * @param element
+     *            The element to check
+     * @return The border-box height for the element
+     */
+    public static double getRequiredHeightDouble(
+            com.google.gwt.dom.client.Element element) {
+        double reqHeight = getRequiredHeightBoundingClientRectDouble(element);
+        if (BrowserInfo.get().isIE() && !BrowserInfo.get().isIE8()) {
+            double csHeight = getRequiredHeightComputedStyleDouble(element);
+            if (csHeight > reqHeight && csHeight < (reqHeight + 1)) {
+                // IE9 rounds reqHeight to integers BUT sometimes reports wrong
+                // csHeight it seems, so we only use csHeight if it is within a
+                // rounding error
+                return csHeight;
+            }
+        }
+        return reqHeight;
+    }
+
     /**
      * Calculates the width of the element's bounding rectangle.
      * <p>
@@ -605,34 +651,44 @@ public class WidgetUtil {
         }
     }-*/;
 
-    public static native int getRequiredHeightComputedStyle(
+    public static int getRequiredHeightComputedStyle(
+            com.google.gwt.dom.client.Element element) {
+        return (int) Math.ceil(getRequiredHeightComputedStyleDouble(element));
+    }
+
+    public static native double getRequiredHeightComputedStyleDouble(
             com.google.gwt.dom.client.Element element)
     /*-{
          var cs = element.ownerDocument.defaultView.getComputedStyle(element);
          var heightPx = cs.height;
          if(heightPx == 'auto'){
              // Fallback for inline elements
-             return @com.vaadin.client.WidgetUtil::getRequiredHeightBoundingClientRect(Lcom/google/gwt/dom/client/Element;)(element);
+             return @com.vaadin.client.WidgetUtil::getRequiredHeightBoundingClientRectDouble(Lcom/google/gwt/dom/client/Element;)(element);
          }
          var height = parseFloat(heightPx); // Will automatically skip "px" suffix
          var border = parseFloat(cs.borderTopWidth) + parseFloat(cs.borderBottomWidth); // Will automatically skip "px" suffix 
          var padding = parseFloat(cs.paddingTop) + parseFloat(cs.paddingBottom); // Will automatically skip "px" suffix
-         return Math.ceil(height+border+padding);
+         return height+border+padding;
      }-*/;
 
-    public static native int getRequiredWidthComputedStyle(
+    public static int getRequiredWidthComputedStyle(
+            com.google.gwt.dom.client.Element element) {
+        return (int) Math.ceil(getRequiredWidthComputedStyleDouble(element));
+    }
+
+    public static native int getRequiredWidthComputedStyleDouble(
             com.google.gwt.dom.client.Element element)
     /*-{
          var cs = element.ownerDocument.defaultView.getComputedStyle(element);
          var widthPx = cs.width;
          if(widthPx == 'auto'){
              // Fallback for inline elements
-             return @com.vaadin.client.WidgetUtil::getRequiredWidthBoundingClientRect(Lcom/google/gwt/dom/client/Element;)(element);
+             return @com.vaadin.client.WidgetUtil::getRequiredWidthBoundingClientRectDouble(Lcom/google/gwt/dom/client/Element;)(element);
          }
          var width = parseFloat(widthPx); // Will automatically skip "px" suffix
          var border = parseFloat(cs.borderLeftWidth) + parseFloat(cs.borderRightWidth); // Will automatically skip "px" suffix
          var padding = parseFloat(cs.paddingLeft) + parseFloat(cs.paddingRight); // Will automatically skip "px" suffix
-         return Math.ceil(width+border+padding);
+         return width+border+padding;
      }-*/;
 
     /**
index d1e055eb5da2d9ca5fa02b8d617ce9a164c24443..0c1d4cec90738f987c1d4b66b153767f3083afce 100644 (file)
@@ -143,8 +143,10 @@ public class VGridLayout extends ComplexPanel {
         if (!isUndefinedHeight()) {
             int usedSpace = calcRowUsedSpace();
             int[] actualExpandRatio = calcRowExpandRatio();
-            int availableSpace = LayoutManager.get(client).getInnerHeight(
-                    getElement());
+            // Round down to avoid problems with fractions (100.1px available ->
+            // can use 100, not 101)
+            int availableSpace = (int) LayoutManager.get(client)
+                    .getInnerHeightDouble(getElement());
             int excessSpace = availableSpace - usedSpace;
             int distributed = 0;
             if (excessSpace > 0) {
@@ -223,8 +225,10 @@ public class VGridLayout extends ComplexPanel {
         if (!isUndefinedWidth()) {
             int usedSpace = calcColumnUsedSpace();
             int[] actualExpandRatio = calcColumnExpandRatio();
-            int availableSpace = LayoutManager.get(client).getInnerWidth(
-                    getElement());
+            // Round down to avoid problems with fractions (100.1px available ->
+            // can use 100, not 101)
+            int availableSpace = (int) LayoutManager.get(client)
+                    .getInnerWidthDouble(getElement());
             int excessSpace = availableSpace - usedSpace;
             int distributed = 0;
             if (excessSpace > 0) {
diff --git a/uitest/src/com/vaadin/tests/components/gridlayout/GridLayoutFractionalSizeAndAlignment.java b/uitest/src/com/vaadin/tests/components/gridlayout/GridLayoutFractionalSizeAndAlignment.java
new file mode 100644 (file)
index 0000000..32d01f9
--- /dev/null
@@ -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.tests.components.gridlayout;
+
+import com.vaadin.annotations.Widgetset;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUIWithLog;
+import com.vaadin.tests.widgetset.TestingWidgetSet;
+import com.vaadin.tests.widgetset.server.ScrollableGridLayout;
+import com.vaadin.ui.Alignment;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.GridLayout;
+
+@Widgetset(TestingWidgetSet.NAME)
+public class GridLayoutFractionalSizeAndAlignment extends AbstractTestUIWithLog {
+
+    @Override
+    protected void setup(VaadinRequest request) {
+        widthTest();
+        heightTest();
+    }
+
+    private void widthTest() {
+        final GridLayout layout = new ScrollableGridLayout(1, 1);
+        layout.setMargin(false);
+        layout.setSpacing(true);
+
+        layout.setWidth(525.04f, Unit.PIXELS);
+
+        Button button = new Button("Button");
+
+        layout.addComponent(button);
+        layout.setComponentAlignment(button, Alignment.BOTTOM_RIGHT);
+
+        addComponent(layout);
+    }
+
+    private void heightTest() {
+        final GridLayout layout = new ScrollableGridLayout(1, 1);
+        layout.setMargin(false);
+        layout.setSpacing(true);
+
+        layout.setWidth(525.04f, Unit.PIXELS);
+        layout.setHeight(525.04f, Unit.PIXELS);
+
+        Button button = new Button("Button");
+
+        layout.addComponent(button);
+        layout.setComponentAlignment(button, Alignment.BOTTOM_RIGHT);
+
+        addComponent(layout);
+    }
+}
diff --git a/uitest/src/com/vaadin/tests/components/gridlayout/GridLayoutFractionalSizeAndAlignmentTest.java b/uitest/src/com/vaadin/tests/components/gridlayout/GridLayoutFractionalSizeAndAlignmentTest.java
new file mode 100644 (file)
index 0000000..b60aea4
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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.gridlayout;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import com.vaadin.tests.tb3.MultiBrowserTest;
+
+public class GridLayoutFractionalSizeAndAlignmentTest extends MultiBrowserTest {
+
+    @Test
+    public void ensureNoScrollbarsWithAlignBottomRight() throws IOException {
+        openTestURL();
+        compareScreen("noscrollbars");
+    }
+}
diff --git a/uitest/src/com/vaadin/tests/widgetset/client/ScrollableGridLayoutConnector.java b/uitest/src/com/vaadin/tests/widgetset/client/ScrollableGridLayoutConnector.java
new file mode 100644 (file)
index 0000000..d6431ac
--- /dev/null
@@ -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.widgetset.client;
+
+import com.vaadin.client.ConnectorHierarchyChangeEvent;
+import com.vaadin.client.ui.VGridLayout;
+import com.vaadin.client.ui.gridlayout.GridLayoutConnector;
+import com.vaadin.client.ui.layout.MayScrollChildren;
+import com.vaadin.shared.ui.Connect;
+import com.vaadin.tests.widgetset.server.ScrollableGridLayout;
+
+@Connect(ScrollableGridLayout.class)
+public class ScrollableGridLayoutConnector extends GridLayoutConnector
+        implements MayScrollChildren {
+    @Override
+    public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
+        super.onConnectorHierarchyChange(event);
+
+        for (VGridLayout.Cell cell : getWidget().widgetToCell.values()) {
+            cell.slot.getWrapperElement().addClassName("v-scrollable");
+        }
+    }
+}
diff --git a/uitest/src/com/vaadin/tests/widgetset/server/ScrollableGridLayout.java b/uitest/src/com/vaadin/tests/widgetset/server/ScrollableGridLayout.java
new file mode 100644 (file)
index 0000000..6958172
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+import com.vaadin.ui.Component;
+import com.vaadin.ui.GridLayout;
+
+public class ScrollableGridLayout extends GridLayout {
+
+    public ScrollableGridLayout() {
+        super();
+    }
+
+    public ScrollableGridLayout(int columns, int rows, Component... children) {
+        super(columns, rows, children);
+    }
+
+    public ScrollableGridLayout(int columns, int rows) {
+        super(columns, rows);
+    }
+}