changeset 3751:c375e7b5faf1

HBox optimizations + minor layout optimizations in Region and Shapes
author Martin Sladecek <martin.sladecek@oracle.com>
date Wed, 29 May 2013 16:02:44 +0200
parents 531ed15ca4e1
children 6d06aa1a268b
files javafx-ui-common/src/javafx/scene/layout/FlowPane.java javafx-ui-common/src/javafx/scene/layout/GridPane.java javafx-ui-common/src/javafx/scene/layout/HBox.java javafx-ui-common/src/javafx/scene/layout/Region.java javafx-ui-common/src/javafx/scene/layout/StackPane.java javafx-ui-common/src/javafx/scene/layout/TilePane.java javafx-ui-common/src/javafx/scene/layout/VBox.java javafx-ui-common/src/javafx/scene/shape/Arc.java javafx-ui-common/src/javafx/scene/shape/Circle.java javafx-ui-common/src/javafx/scene/shape/Ellipse.java javafx-ui-common/src/javafx/scene/shape/Line.java javafx-ui-common/src/javafx/scene/shape/Rectangle.java javafx-ui-common/test/unit/javafx/scene/layout/HBoxTest.java
diffstat 13 files changed, 350 insertions(+), 333 deletions(-) [+]
line wrap: on
line diff
--- a/javafx-ui-common/src/javafx/scene/layout/FlowPane.java	Mon May 27 14:38:58 2013 +0400
+++ b/javafx-ui-common/src/javafx/scene/layout/FlowPane.java	Wed May 29 16:02:44 2013 +0200
@@ -46,6 +46,7 @@
 import javafx.css.Styleable;
 
 import static javafx.geometry.Orientation.*;
+import javafx.util.Callback;
 
 /**
  * FlowPane lays out its children in a flow that wraps at the flowpane's boundary.
@@ -79,7 +80,7 @@
  *
  *<p>
  * Example of a vertical flowpane:
- * <pre><code>     FlowPane flow = new FlowPane(Orientation.VERTICAL); 
+ * <pre><code>     FlowPane flow = new FlowPane(Orientation.VERTICAL);
  *     flow.setColumnHalignment(HPos.LEFT); // align labels on left
  *     flow.setPrefWrapLength(200); // preferred height = 200
  *     for (int i = 0; i < titles.size(); i++) {
@@ -171,6 +172,12 @@
         return (Insets)getConstraint(child, MARGIN_CONSTRAINT);
     }
 
+    private static final Callback<Node, Insets> marginAccessor = new Callback<Node, Insets>() {
+        public Insets call(Node n) {
+            return getMargin(n);
+        }
+    };
+
     /**
      * Removes all flowpane constraints from the child node.
      * @param child the child node
@@ -285,7 +292,7 @@
                 public void invalidated() {
                     requestLayout();
                 }
-                
+
                 @Override
                 public CssMetaData<FlowPane, Orientation> getCssMetaData() {
                     return StyleableProperties.ORIENTATION;
@@ -304,7 +311,7 @@
         }
         return orientation;
     }
-    
+
     private ObjectProperty<Orientation> orientation;
     public final void setOrientation(Orientation value) { orientationProperty().set(value); }
     public final Orientation getOrientation() { return orientation == null ? HORIZONTAL : orientation.get();  }
@@ -321,7 +328,7 @@
                 public void invalidated() {
                     requestLayout();
                 }
-                
+
                 @Override
                 public CssMetaData<FlowPane, Number> getCssMetaData() {
                     return StyleableProperties.HGAP;
@@ -340,7 +347,7 @@
         }
         return hgap;
     }
-    
+
     private DoubleProperty hgap;
     public final void setHgap(double value) { hgapProperty().set(value); }
     public final double getHgap() { return hgap == null ? 0 : hgap.get(); }
@@ -375,7 +382,7 @@
         }
         return vgap;
     }
-    
+
     private DoubleProperty vgap;
     public final void setVgap(double value) { vgapProperty().set(value); }
     public final double getVgap() { return vgap == null ? 0 : vgap.get(); }
@@ -436,7 +443,7 @@
                 public void invalidated() {
                     requestLayout();
                 }
-                
+
                 @Override
                 public CssMetaData<FlowPane, Pos> getCssMetaData() {
                     return StyleableProperties.ALIGNMENT;
@@ -455,7 +462,7 @@
         }
         return alignment;
     }
-    
+
     private ObjectProperty<Pos> alignment;
     public final void setAlignment(Pos value) { alignmentProperty().set(value); }
     public final Pos getAlignment() { return alignment == null ? Pos.TOP_LEFT : alignment.get(); }
@@ -476,7 +483,7 @@
                 public void invalidated() {
                     requestLayout();
                 }
-                
+
                 @Override
                 public CssMetaData<FlowPane, HPos> getCssMetaData() {
                     return StyleableProperties.COLUMN_HALIGNMENT;
@@ -495,7 +502,7 @@
         }
         return columnHalignment;
     }
-    
+
     private ObjectProperty<HPos> columnHalignment;
     public final void setColumnHalignment(HPos value) { columnHalignmentProperty().set(value); }
     public final HPos getColumnHalignment() { return columnHalignment == null ? HPos.LEFT : columnHalignment.get(); }
@@ -519,11 +526,11 @@
                     requestLayout();
                 }
 
-                @Override 
+                @Override
                 public CssMetaData<FlowPane, VPos> getCssMetaData() {
                     return StyleableProperties.ROW_VALIGNMENT;
                 }
-                
+
                 @Override
                 public Object getBean() {
                     return FlowPane.this;
@@ -537,7 +544,7 @@
         }
         return rowValignment;
     }
-    
+
     private ObjectProperty<VPos> rowValignment;
     public final void setRowValignment(VPos value) { rowValignmentProperty().set(value); }
     public final VPos getRowValignment() { return rowValignment == null ? VPos.CENTER : rowValignment.get(); }
@@ -587,9 +594,9 @@
         if (getOrientation() == HORIZONTAL) {
             // horizontal
             double maxRunWidth = getPrefWrapLength();
-            List<Run> hruns = getRuns(maxRunWidth);            
+            List<Run> hruns = getRuns(maxRunWidth);
             double w = computeContentWidth(hruns);
-            w = getPrefWrapLength() > w ? getPrefWrapLength() : w;            
+            w = getPrefWrapLength() > w ? getPrefWrapLength() : w;
             return insets.getLeft() + snapSize(w) + insets.getRight();
         } else {
             // vertical
@@ -613,7 +620,7 @@
             double maxRunHeight = getPrefWrapLength();
             List<Run> vruns = getRuns(maxRunHeight);
             double h = computeContentHeight(vruns);
-            h = getPrefWrapLength() > h ? getPrefWrapLength() : h;            
+            h = getPrefWrapLength() > h ? getPrefWrapLength() : h;
             return insets.getTop() + snapSize(h) + insets.getBottom();
         }
     }
@@ -690,16 +697,14 @@
             // horizontal
             ArrayList<Node> rownodes = new ArrayList();
             run.width = (run.rects.size()-1)*snapSpace(getHgap());
-            Insets margins[] = new Insets[run.rects.size()];
             for (int i=0, max=run.rects.size(); i<max; i++) {
                 LayoutRect lrect = run.rects.get(i);
-                margins[i] = getMargin(lrect.node);
                 rownodes.add(lrect.node);
                 run.width += lrect.width;
                 lrect.y = runOffset;
             }
-            run.height = computeMaxPrefAreaHeight(rownodes, margins, getRowValignment());
-            run.baselineOffset = getRowValignment() == VPos.BASELINE? getMaxAreaBaselineOffset(rownodes, margins) : run.height;
+            run.height = computeMaxPrefAreaHeight(rownodes, marginAccessor, getRowValignment());
+            run.baselineOffset = getRowValignment() == VPos.BASELINE? getMaxAreaBaselineOffset(rownodes, marginAccessor) : run.height;
 
         } else {
             // vertical
@@ -797,7 +802,7 @@
       */
      private static class StyleableProperties {
 
-         private static final CssMetaData<FlowPane,Pos> ALIGNMENT = 
+         private static final CssMetaData<FlowPane,Pos> ALIGNMENT =
              new CssMetaData<FlowPane,Pos>("-fx-alignment",
                  new EnumConverter<Pos>(Pos.class), Pos.TOP_LEFT) {
 
@@ -810,10 +815,10 @@
             public StyleableProperty<Pos> getStyleableProperty(FlowPane node) {
                 return (StyleableProperty<Pos>)node.alignmentProperty();
             }
-                 
+
          };
 
-         private static final CssMetaData<FlowPane,HPos> COLUMN_HALIGNMENT = 
+         private static final CssMetaData<FlowPane,HPos> COLUMN_HALIGNMENT =
              new CssMetaData<FlowPane,HPos>("-fx-column-halignment",
                  new EnumConverter<HPos>(HPos.class), HPos.LEFT) {
 
@@ -826,10 +831,10 @@
             public StyleableProperty<HPos> getStyleableProperty(FlowPane node) {
                 return (StyleableProperty<HPos>)node.columnHalignmentProperty();
             }
-                     
+
          };
-         
-         private static final CssMetaData<FlowPane,Number> HGAP = 
+
+         private static final CssMetaData<FlowPane,Number> HGAP =
              new CssMetaData<FlowPane,Number>("-fx-hgap",
                  SizeConverter.getInstance(), 0.0){
 
@@ -842,10 +847,10 @@
             public StyleableProperty<Number> getStyleableProperty(FlowPane node) {
                 return (StyleableProperty<Number>)node.hgapProperty();
             }
-                     
+
          };
-         
-         private static final CssMetaData<FlowPane,VPos> ROW_VALIGNMENT = 
+
+         private static final CssMetaData<FlowPane,VPos> ROW_VALIGNMENT =
              new CssMetaData<FlowPane,VPos>("-fx-row-valignment",
                  new EnumConverter<VPos>(VPos.class), VPos.CENTER) {
 
@@ -858,20 +863,20 @@
             public StyleableProperty<VPos> getStyleableProperty(FlowPane node) {
                 return (StyleableProperty<VPos>)node.rowValignmentProperty();
             }
-                     
-         }; 
 
-         private static final CssMetaData<FlowPane,Orientation> ORIENTATION = 
+         };
+
+         private static final CssMetaData<FlowPane,Orientation> ORIENTATION =
              new CssMetaData<FlowPane,Orientation>("-fx-orientation",
-                 new EnumConverter<Orientation>(Orientation.class), 
+                 new EnumConverter<Orientation>(Orientation.class),
                  Orientation.HORIZONTAL) {
-                
+
             @Override
             public Orientation getInitialValue(FlowPane node) {
-                // A vertical flow pane should remain vertical 
+                // A vertical flow pane should remain vertical
                 return node.getOrientation();
             }
-                     
+
             @Override
             public boolean isSettable(FlowPane node) {
                 return node.orientation == null || !node.orientation.isBound();
@@ -881,10 +886,10 @@
             public StyleableProperty<Orientation> getStyleableProperty(FlowPane node) {
                 return (StyleableProperty<Orientation>)node.orientationProperty();
             }
-                     
-         };  
-         
-         private static final CssMetaData<FlowPane,Number> VGAP = 
+
+         };
+
+         private static final CssMetaData<FlowPane,Number> VGAP =
              new CssMetaData<FlowPane,Number>("-fx-vgap",
                  SizeConverter.getInstance(), 0.0){
 
@@ -897,8 +902,8 @@
             public StyleableProperty<Number> getStyleableProperty(FlowPane node) {
                 return (StyleableProperty<Number>)node.vgapProperty();
             }
-                     
-         }; 
+
+         };
 
          private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
          static {
@@ -929,8 +934,8 @@
      * {@inheritDoc}
      *
      */
-    
-    
+
+
     @Override
     public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
         return getClassCssMetaData();
--- a/javafx-ui-common/src/javafx/scene/layout/GridPane.java	Mon May 27 14:38:58 2013 +0400
+++ b/javafx-ui-common/src/javafx/scene/layout/GridPane.java	Wed May 29 16:02:44 2013 +0200
@@ -67,6 +67,7 @@
 import static javafx.scene.layout.Region.USE_COMPUTED_SIZE;
 import static javafx.scene.layout.Region.boundedSize;
 import static javafx.scene.layout.Region.getMaxAreaBaselineOffset;
+import javafx.util.Callback;
 
 
 
@@ -400,6 +401,12 @@
         return (Insets)getConstraint(child, MARGIN_CONSTRAINT);
     }
 
+    private static final Callback<Node, Insets> marginAccessor = new Callback<Node, Insets>() {
+        public Insets call(Node n) {
+            return getMargin(n);
+        }
+    };
+
     /**
      * Sets the horizontal alignment for the child when contained by a gridpane.
      * If set, will override the gridpane's default horizontal alignment.
@@ -905,7 +912,7 @@
      * Returns list of row constraints. Row constraints can be added to
      * explicitly control individual row sizing and layout behavior.
      * If not set, row sizing and layout behavior is computed based on content.
-     * 
+     *
      * Index in the ObservableList denotes the row number, so the row constraint for the first row
      * is at the position of 0.
      */
@@ -938,7 +945,7 @@
      * Returns list of column constraints. Column constraints can be added to
      * explicitly control individual column sizing and layout behavior.
      * If not set, column sizing and layout behavior is computed based on content.
-     * 
+     *
      * Index in the ObservableList denotes the column number, so the column constraint for the first column
      * is at the position of 0.
      */
@@ -1096,16 +1103,14 @@
                     rowGrow[i] = vGrow;
 
                 VPos rowVPos = getRowValignment(i);
-                List<Insets> margins = new ArrayList<>(numColumns);
                 List<Node> baselineNodes = new ArrayList<>(numColumns);
                 for (int j = 0, size = children.size(); j < size; j++) {
                     Node n = children.get(j);
                     if (getNodeRowIndex(n) == i && (rowVPos == VPos.BASELINE || getValignment(n) == VPos.BASELINE)) {
                         baselineNodes.add(n);
-                        margins.add(getMargin(n));
                     }
                 }
-                rowBaseline[i] = getMaxAreaBaselineOffset(baselineNodes, margins.toArray(new Insets[margins.size()]));
+                rowBaseline[i] = getMaxAreaBaselineOffset(baselineNodes, marginAccessor);
                 baselineNodes.clear();
 
             }
--- a/javafx-ui-common/src/javafx/scene/layout/HBox.java	Mon May 27 14:38:58 2013 +0400
+++ b/javafx-ui-common/src/javafx/scene/layout/HBox.java	Wed May 29 16:02:44 2013 +0200
@@ -45,6 +45,7 @@
 import com.sun.javafx.css.converters.EnumConverter;
 import com.sun.javafx.css.converters.SizeConverter;
 import javafx.css.Styleable;
+import javafx.util.Callback;
 
 
 
@@ -145,6 +146,8 @@
  */
 public class HBox extends Pane {
 
+    private double[][] tempArray;
+
     /********************************************************************
      *  BEGIN static methods
      ********************************************************************/
@@ -196,6 +199,12 @@
         return (Insets)getConstraint(child, MARGIN_CONSTRAINT);
     }
 
+    private static final Callback<Node, Insets> marginAccessor = new Callback<Node, Insets>() {
+        public Insets call(Node n) {
+            return getMargin(n);
+        }
+    };
+
     /**
      * Removes all hbox constraints from the child node.
      * @param child the child node
@@ -379,7 +388,7 @@
     @Override protected double computeMinWidth(double height) {
         Insets insets = getInsets();
         return snapSpace(insets.getLeft()) +
-               computeContentWidth(getAreaWidths(getManagedChildren(), height, true)) +
+               computeContentWidth(getManagedChildren(), height, true) +
                snapSpace(insets.getRight());
     }
 
@@ -387,14 +396,12 @@
         Insets insets = getInsets();
         List<Node>managed = getManagedChildren();
         double contentHeight = 0;
-        if (getContentBias() == Orientation.HORIZONTAL) {
-            // if width is different than preferred, then child widths may grow or shrink,
-            // altering the height of any child with a horizontal contentBias.
-            double prefWidths[] = getAreaWidths(managed, -1, false);
+        if (width != -1 && getContentBias() != null) {
+            double prefWidths[][] = getAreaWidths(managed, -1, false);
             adjustAreaWidths(managed, prefWidths, width, -1);
-            contentHeight = computeMaxMinAreaHeight(managed, getMargins(managed), prefWidths, getAlignmentInternal().getVpos());
+            contentHeight = computeMaxMinAreaHeight(managed, marginAccessor, prefWidths[0], getAlignmentInternal().getVpos());
         } else {
-            contentHeight = computeMaxMinAreaHeight(managed, getMargins(managed), getAlignmentInternal().getVpos());
+            contentHeight = computeMaxMinAreaHeight(managed, marginAccessor, getAlignmentInternal().getVpos());
         }
         return snapSpace(insets.getTop()) +
                contentHeight +
@@ -402,133 +409,133 @@
     }
 
     @Override protected double computePrefWidth(double height) {
-         Insets insets = getInsets();
-         return snapSpace(insets.getLeft()) +
-                computeContentWidth(getAreaWidths(getManagedChildren(), height, false)) +
-                snapSpace(insets.getRight());
+        Insets insets = getInsets();
+        return snapSpace(insets.getLeft()) +
+               computeContentWidth(getManagedChildren(), height, false) +
+               snapSpace(insets.getRight());
     }
 
     @Override protected double computePrefHeight(double width) {
         Insets insets = getInsets();
         List<Node>managed = getManagedChildren();
         double contentHeight = 0;
-        if (getContentBias() == Orientation.HORIZONTAL) {
-            // if width is different than preferred, then child widths may grow or shrink,
-            // altering the height of any child with a horizontal contentBias.
-            double prefWidths[] = getAreaWidths(managed, -1, false);
+        if (width != -1 && getContentBias() != null) {
+            double prefWidths[][] = getAreaWidths(managed, -1, false);
             adjustAreaWidths(managed, prefWidths, width, -1);
-            contentHeight = computeMaxPrefAreaHeight(managed, getMargins(managed), prefWidths, getAlignmentInternal().getVpos());
+            contentHeight = computeMaxPrefAreaHeight(managed, marginAccessor, prefWidths[0], getAlignmentInternal().getVpos());
         } else {
-            contentHeight = computeMaxPrefAreaHeight(managed, getMargins(managed), getAlignmentInternal().getVpos());
+            contentHeight = computeMaxPrefAreaHeight(managed, marginAccessor, getAlignmentInternal().getVpos());
         }
         return snapSpace(insets.getTop()) +
                contentHeight +
                snapSpace(insets.getBottom());
     }
 
-    private Insets[] getMargins(List<Node>managed) {
-        Insets margins[] = new Insets[managed.size()];
-        for(int i = 0; i < margins.length; i++) {
-            margins[i] = getMargin(managed.get(i));
-        }
-        return margins;
-    }
-
-    private double[] getAreaWidths(List<Node>managed, double height, boolean minimum) {
+    private double[][] getAreaWidths(List<Node>managed, double height, boolean minimum) {
         // height could be -1
-        double[] prefAreaWidths = new double [managed.size()];
+        double[][] temp = getTempArray(managed.size());
         final double insideHeight = height == -1? -1 : height -
                                      snapSpace(getInsets().getTop()) - snapSpace(getInsets().getBottom());
+        final boolean shouldFillHeight = shouldFillHeight();
         for (int i = 0, size = managed.size(); i < size; i++) {
             Node child = managed.get(i);
             Insets margin = getMargin(child);
-            prefAreaWidths[i] = minimum?
-                               computeChildMinAreaWidth(child, margin,
-                                   shouldFillHeight()? insideHeight : child.minHeight(-1)) :
-                                   computeChildPrefAreaWidth(child, margin,
-                                       shouldFillHeight()? insideHeight : child.prefHeight(-1));
+            if (minimum) {
+                if (insideHeight != -1 && shouldFillHeight) {
+                    temp[0][i] = computeChildMinAreaWidth(child, margin, insideHeight);
+                } else {
+                    temp[0][i] = computeChildMinAreaWidth(child, margin, -1);
+                }
+            } else {
+                if (insideHeight != -1 && shouldFillHeight) {
+                    temp[0][i] = computeChildPrefAreaWidth(child, margin, insideHeight);
+                } else {
+                    temp[0][i] = computeChildPrefAreaWidth(child, margin, -1);
+                }
+            }
         }
-        return prefAreaWidths;
+        return temp;
     }
 
-    private double adjustAreaWidths(List<Node>managed, double areaWidths[], double width, double height) {
+    private double adjustAreaWidths(List<Node>managed, double areaWidths[][], double width, double height) {
         Insets insets = getInsets();
         double top = snapSpace(insets.getTop());
         double bottom = snapSpace(insets.getBottom());
 
-        double contentWidth = computeContentWidth(areaWidths);
-        double extraWidth = (width == -1? prefWidth(-1) : width) -
+        double contentWidth = sum(areaWidths[0], managed.size()) + (managed.size()-1)*snapSpace(getSpacing());
+        double extraWidth = width -
                 snapSpace(insets.getLeft()) - snapSpace(insets.getRight()) - contentWidth;
 
         if (extraWidth != 0) {
-            double remaining = growOrShrinkAreaWidths(managed, areaWidths, Priority.ALWAYS, extraWidth,
-                    shouldFillHeight() && height != -1? height - top - bottom : -1);
-            remaining = growOrShrinkAreaWidths(managed, areaWidths, Priority.SOMETIMES, remaining,
-                    shouldFillHeight() && height != -1? height - top - bottom : -1);
+            final double refHeight = shouldFillHeight() && height != -1? height - top - bottom : -1;
+            double remaining = growOrShrinkAreaWidths(managed, areaWidths, Priority.ALWAYS, extraWidth, refHeight);
+            remaining = growOrShrinkAreaWidths(managed, areaWidths, Priority.SOMETIMES, remaining, refHeight);
             contentWidth += (extraWidth - remaining);
         }
         return contentWidth;
     }
 
-    private double growOrShrinkAreaWidths(List<Node>managed, double areaWidths[], Priority priority, double extraWidth, double height) {
+    private double growOrShrinkAreaWidths(List<Node>managed, double areaWidths[][], Priority priority, double extraWidth, double height) {
         final boolean shrinking = extraWidth < 0;
-        List<Node> adjustList = new ArrayList<Node>();
-        List<Node> adjusting = new ArrayList<Node>();
+        int adjustingNumber = 0;
 
-        for (int i = 0, size = managed.size(); i < size; i++) {
+        double[] usedWidths = areaWidths[0];
+        double[] temp = areaWidths[1];
+
+        if (shrinking) {
+            adjustingNumber = managed.size();
+            for (int i = 0, size = managed.size(); i < size; i++) {
+                final Node child = managed.get(i);
+                temp[i] = computeChildMinAreaWidth(child, getMargin(child), height);
+            }
+        } else {
+            for (int i = 0, size = managed.size(); i < size; i++) {
             final Node child = managed.get(i);
-            if (shrinking || getHgrow(child) == priority) {
-                adjustList.add(child);
-                adjusting.add(child);
+            if (getHgrow(child) == priority) {
+                temp[i] = computeChildMaxAreaWidth(child, getMargin(child), height);
+                adjustingNumber++;
+            } else {
+                temp[i] = -1;
+            }
+        }
+        }
+
+        double available = extraWidth; // will be negative in shrinking case
+        while (Math.abs(available) > 1 && adjustingNumber > 0) {
+            final double portion = snapPortion(available / adjustingNumber); // negative in shrinking case
+            for (int i = 0, size = managed.size(); i < size; i++) {
+                if (temp[i] == -1) {
+                    continue;
+                }
+                final double limit = temp[i] - usedWidths[i]; // negative in shrinking case
+                final double change = Math.abs(limit) <= Math.abs(portion)? limit : portion;
+                usedWidths[i] += change;
+                available -= change;
+                if (available == 0) {
+                    break;
+                }
+                if (Math.abs(change) < Math.abs(portion)) {
+                    temp[i] = -1;
+                    adjustingNumber--;
+                }
             }
         }
 
-        double[] areaLimitWidths = new double[adjustList.size()];
-        for (int i = 0, size = adjustList.size(); i < size; i++) {
-            final Node child = adjustList.get(i);
-            final Insets margin = getMargin(child);
-            areaLimitWidths[i] = shrinking?
-                computeChildMinAreaWidth(child, margin, height) : computeChildMaxAreaWidth(child, margin, height);
-        }
-
-        double available = extraWidth; // will be negative in shrinking case
-        while (Math.abs(available) > 1.0 && adjusting.size() > 0) {
-            Node[] adjusted = new Node[adjustList.size()];
-            final double portion = available / adjusting.size(); // negative in shrinking case
-            for (int i = 0, size = adjusting.size(); i < size; i++) {
-                final Node child = adjusting.get(i);
-                final int childIndex = managed.indexOf(child);
-                final double limit = areaLimitWidths[adjustList.indexOf(child)] - areaWidths[childIndex]; // negative in shrinking case
-                final double change = Math.abs(limit) <= Math.abs(portion)? limit : portion;
-                areaWidths[childIndex] += change;
-                //if (node.id.startsWith("debug.")) println("{if (shrinking) "shrink" else "grow"}: {node.id} portion({portion})=available({available})/({sizeof adjusting}) change={change}");
-                available -= change;
-                if (Math.abs(change) < Math.abs(portion)) {
-                    adjusted[i] = child;
-                }
-            }
-            for (int i = 0; i < adjusted.length; i++) {
-                Node node = adjusted[i];
-                if (node != null) {
-                    adjusting.remove(node);
-                }
-            }
-        }
-        for (int i = 0; i < areaWidths.length; i++) {
-            areaWidths[i] = snapSpace(areaWidths[i]);
-        }
         return available; // might be negative in shrinking case
     }
 
-    private double computeContentWidth(double[] widths) {
-        double total = 0;
-        for (double w : widths) {
-            total += w;
-        }
-        return total + (widths.length-1)*snapSpace(getSpacing());
+    private double computeContentWidth(List<Node> managedChildren, double height, boolean minimum) {
+        return sum(getAreaWidths(managedChildren, height, minimum)[0], managedChildren.size())
+                + (managedChildren.size()-1)*snapSpace(getSpacing());
     }
 
-    private double[] actualAreaWidths;
+    private static double sum(double[] array, int size) {
+        int i = 0;
+        double res = 0;
+        while (i != size) res += array[i++];
+        return res;
+    }
+
 
     @Override protected void layoutChildren() {
         List<Node> managed = getManagedChildren();
@@ -542,7 +549,7 @@
         double right = snapSpace(insets.getRight());
         double space = snapSpace(getSpacing());
 
-        actualAreaWidths = getAreaWidths(managed, height, false);
+        double[][] actualAreaWidths = getAreaWidths(managed, height, false);
         double contentWidth = adjustAreaWidths(managed, actualAreaWidths, width, height);
         double contentHeight = height - top - bottom;
 
@@ -551,16 +558,26 @@
         double baselineOffset = align.getVpos() == VPos.BASELINE ? getMaxBaselineOffset(managed)
                                     : height/2;
 
+        final boolean shouldFillHeight = shouldFillHeight();
         for (int i = 0, size = managed.size(); i < size; i++) {
             Node child = managed.get(i);
             Insets margin = getMargin(child);
-            layoutInArea(child, x, y, actualAreaWidths[i], contentHeight,
-                    baselineOffset, margin, true, shouldFillHeight(),
+            layoutInArea(child, x, y, actualAreaWidths[0][i], contentHeight,
+                    baselineOffset, margin, true, shouldFillHeight,
                     align.getHpos(), align.getVpos());
-            x += actualAreaWidths[i] + space;
+            x += actualAreaWidths[0][i] + space;
         }
     }
 
+    private double[][] getTempArray(int size) {
+        if (tempArray == null) {
+            tempArray = new double[2][size]; // First array for the result, second for temporary computations
+        } else if (tempArray[0].length < size) {
+            tempArray = new double[2][Math.max(tempArray.length * 3, size)];
+        }
+        return tempArray;
+
+    }
 
     /***************************************************************************
      *                                                                         *
--- a/javafx-ui-common/src/javafx/scene/layout/Region.java	Mon May 27 14:38:58 2013 +0400
+++ b/javafx-ui-common/src/javafx/scene/layout/Region.java	Wed May 29 16:02:44 2013 +0200
@@ -72,6 +72,7 @@
 import javafx.css.StyleOrigin;
 import javafx.css.Styleable;
 import javafx.css.StyleableDoubleProperty;
+import javafx.util.Callback;
 import sun.util.logging.PlatformLogger;
 
 /**
@@ -251,11 +252,19 @@
         return snapToPixel ? Math.round(value) : value;
     }
 
-    static double getMaxAreaBaselineOffset(List<Node> content, Insets margins[]) {
+    private static double snapPortion(double value, boolean snapToPixel) {
+        if (snapToPixel) {
+            return (value > 0 ? Math.max(1, Math.floor(value)) : Math.min(-1, Math.ceil(value)));
+        }
+        return value;
+    }
+
+    static double getMaxAreaBaselineOffset(List<Node> content, Callback<Node, Insets> margins) {
         double max = 0;
         for (int i = 0, maxPos = content.size(); i < maxPos; i++) {
             final Node node = content.get(i);
-            final double topMargin = margins[i] != null ? margins[i].getTop() : 0;
+            final Insets margin = margins.call(node);
+            final double topMargin = margin != null ? margin.getTop() : 0;
             final double position = topMargin + node.getBaselineOffset();
             max = max >= position ? max : position; // Math.max
         }
@@ -531,7 +540,7 @@
      * within the content area. The insets are computed based on the Border which has been specified,
      * if any, and also the padding.
      */
-    private InsetsProperty insets = new InsetsProperty();
+    private final InsetsProperty insets = new InsetsProperty();
     public final Insets getInsets() { return insets.get(); }
     public final ReadOnlyObjectProperty<Insets> insetsProperty() { return insets; }
     private final class InsetsProperty extends ReadOnlyObjectProperty<Insets> {
@@ -1382,6 +1391,10 @@
         return snapPosition(value, isSnapToPixel());
     }
 
+    double snapPortion(double value) {
+        return snapPortion(value, isSnapToPixel());
+    }
+
 
     /**
      * Utility method to get the top inset which includes padding and border
@@ -1537,57 +1550,57 @@
 
     /* Max of children's minimum area widths */
 
-    double computeMaxMinAreaWidth(List<Node> children, Insets margins[], HPos halignment /* ignored for now */) {
+    double computeMaxMinAreaWidth(List<Node> children, Callback<Node, Insets> margins, HPos halignment /* ignored for now */) {
         return getMaxAreaWidth(children, margins, new double[] { -1 }, true);
     }
 
-    double computeMaxMinAreaWidth(List<Node> children, Insets margins[], HPos halignment /* ignored for now */, double height) {
+    double computeMaxMinAreaWidth(List<Node> children, Callback<Node, Insets> margins, HPos halignment /* ignored for now */, double height) {
         return getMaxAreaWidth(children, margins, new double[] { height }, true);
     }
 
-    double computeMaxMinAreaWidth(List<Node> children, Insets childMargins[], double childHeights[], HPos halignment /* ignored for now */) {
+    double computeMaxMinAreaWidth(List<Node> children, Callback<Node, Insets> childMargins, double childHeights[], HPos halignment /* ignored for now */) {
         return getMaxAreaWidth(children, childMargins, childHeights, true);
     }
 
     /* Max of children's minimum area heights */
 
-    double computeMaxMinAreaHeight(List<Node>children, Insets margins[], VPos valignment) {
-        return getMaxAreaHeight(children, margins, new double[] { -1 }, valignment, true);
+    double computeMaxMinAreaHeight(List<Node>children, Callback<Node, Insets> margins, VPos valignment) {
+        return getMaxAreaHeight(children, margins, null, valignment, true);
     }
 
-    double computeMaxMinAreaHeight(List<Node>children, Insets margins[], VPos valignment, double width) {
+    double computeMaxMinAreaHeight(List<Node>children, Callback<Node, Insets> margins, VPos valignment, double width) {
         return getMaxAreaHeight(children, margins, new double[] { width }, valignment, true);
     }
 
-    double computeMaxMinAreaHeight(List<Node>children, Insets childMargins[], double childWidths[], VPos valignment) {
+    double computeMaxMinAreaHeight(List<Node>children, Callback<Node, Insets> childMargins, double childWidths[], VPos valignment) {
         return getMaxAreaHeight(children, childMargins, childWidths, valignment, true);
     }
 
     /* Max of children's pref area widths */
 
-    double computeMaxPrefAreaWidth(List<Node>children, Insets margins[], HPos halignment /* ignored for now */) {
+    double computeMaxPrefAreaWidth(List<Node>children, Callback<Node, Insets> margins, HPos halignment /* ignored for now */) {
         return getMaxAreaWidth(children, margins, new double[] { -1 }, false);
     }
 
-    double computeMaxPrefAreaWidth(List<Node>children, Insets margins[], double height, HPos halignment /* ignored for now */) {
+    double computeMaxPrefAreaWidth(List<Node>children, Callback<Node, Insets> margins, double height, HPos halignment /* ignored for now */) {
         return getMaxAreaWidth(children, margins, new double[] { height }, false);
     }
 
-    double computeMaxPrefAreaWidth(List<Node>children, Insets childMargins[], double childHeights[], HPos halignment /* ignored for now */) {
+    double computeMaxPrefAreaWidth(List<Node>children, Callback<Node, Insets> childMargins, double childHeights[], HPos halignment /* ignored for now */) {
         return getMaxAreaWidth(children, childMargins, childHeights, false);
     }
 
     /* Max of children's pref area heights */
 
-    double computeMaxPrefAreaHeight(List<Node>children, Insets margins[], VPos valignment) {
-        return getMaxAreaHeight(children, margins, createDoubleArray(children.size(), -1), valignment, false);
+    double computeMaxPrefAreaHeight(List<Node>children, Callback<Node, Insets> margins, VPos valignment) {
+        return getMaxAreaHeight(children, margins, null, valignment, false);
     }
 
-    double computeMaxPrefAreaHeight(List<Node>children, Insets margins[], double width, VPos valignment) {
-        return getMaxAreaHeight(children, margins, createDoubleArray(children.size(), width), valignment, false);
+    double computeMaxPrefAreaHeight(List<Node>children, Callback<Node, Insets> margins, double width, VPos valignment) {
+        return getMaxAreaHeight(children, margins, new double[] { width }, valignment, false);
     }
 
-    double computeMaxPrefAreaHeight(List<Node>children, Insets childMargins[], double childWidths[], VPos valignment) {
+    double computeMaxPrefAreaHeight(List<Node>children, Callback<Node, Insets> childMargins, double childWidths[], VPos valignment) {
         return getMaxAreaHeight(children, childMargins, childWidths, valignment, false);
     }
 
@@ -1650,17 +1663,18 @@
     }
 
     /* utility method for computing the max of children's min or pref heights, taking into account baseline alignment */
-    private double getMaxAreaHeight(List<Node> children, Insets childMargins[],  double childWidths[], VPos valignment, boolean minimum) {
-        final double lastChildWidth = childWidths.length > 0 ? childWidths[childWidths.length - 1] : 0;
+    private double getMaxAreaHeight(List<Node> children, Callback<Node,Insets> childMargins,  double childWidths[], VPos valignment, boolean minimum) {
+        final double singleChildWidth = childWidths == null ? -1 : childWidths.length == 1 ? childWidths[0] : Double.NaN;
         if (valignment == VPos.BASELINE) {
             double maxAbove = 0;
             double maxBelow = 0;
             for (int i = 0, maxPos = children.size(); i < maxPos; i++) {
                 final Node child = children.get(i);
                 final double baseline = child.getBaselineOffset();
-                final double top = childMargins[i] != null? snapSpace(childMargins[i].getTop()) : 0;
-                final double bottom = childMargins[i] != null? snapSpace(childMargins[i].getBottom()) : 0;
-                final double childWidth = i < childWidths.length ? childWidths[i] : lastChildWidth;
+                Insets margin = childMargins.call(child);
+                final double top = margin != null? snapSpace(margin.getTop()) : 0;
+                final double bottom = margin != null? snapSpace(margin.getBottom()) : 0;
+                final double childWidth = Double.isNaN(singleChildWidth) ? childWidths[i] : singleChildWidth;
                 maxAbove = Math.max(maxAbove, baseline + top);
                 maxBelow = Math.max(maxBelow,
                         snapSpace(minimum?snapSize(child.minHeight(childWidth)) : snapSize(child.prefHeight(childWidth))) -
@@ -1671,25 +1685,27 @@
             double max = 0;
             for (int i = 0, maxPos = children.size(); i < maxPos; i++) {
                 final Node child = children.get(i);
-                final double childWidth = i < childWidths.length ? childWidths[i] : lastChildWidth;
+                Insets margin = childMargins.call(child);
+                final double childWidth = Double.isNaN(singleChildWidth) ? childWidths[i] : singleChildWidth;
                 max = Math.max(max, minimum?
-                    computeChildMinAreaHeight(child, childMargins[i], childWidth) :
-                        computeChildPrefAreaHeight(child, childMargins[i], childWidth));
+                    computeChildMinAreaHeight(child, margin, childWidth) :
+                        computeChildPrefAreaHeight(child, margin, childWidth));
             }
             return max;
         }
     }
 
     /* utility method for computing the max of children's min or pref width, horizontal alignment is ignored for now */
-    private double getMaxAreaWidth(List<javafx.scene.Node> children, Insets childMargins[], double childHeights[], boolean minimum) {
+    private double getMaxAreaWidth(List<javafx.scene.Node> children, Callback<Node, Insets> childMargins, double childHeights[], boolean minimum) {
         final double lastChildHeight = childHeights.length > 0 ? childHeights[childHeights.length - 1] : 0;
         double max = 0;
         for (int i = 0, maxPos = children.size(); i < maxPos; i++) {
             final Node child = children.get(i);
+            final Insets margin = childMargins.call(child);
             final double childHeight = i < childHeights.length ? childHeights[i] : lastChildHeight;
             max = Math.max(max, minimum?
-                computeChildMinAreaWidth(children.get(i), childMargins[i], childHeight) :
-                    computeChildPrefAreaWidth(child, childMargins[i], childHeight));
+                computeChildMinAreaWidth(children.get(i), margin, childHeight) :
+                    computeChildPrefAreaWidth(child, margin, childHeight));
         }
         return max;
     }
--- a/javafx-ui-common/src/javafx/scene/layout/StackPane.java	Mon May 27 14:38:58 2013 +0400
+++ b/javafx-ui-common/src/javafx/scene/layout/StackPane.java	Wed May 29 16:02:44 2013 +0200
@@ -40,6 +40,7 @@
 import com.sun.javafx.css.converters.EnumConverter;
 import javafx.css.Styleable;
 import javafx.geometry.HPos;
+import javafx.util.Callback;
 
 /**
  *
@@ -175,6 +176,12 @@
         return (Insets)getConstraint(child, MARGIN_CONSTRAINT);
     }
 
+    private static final Callback<Node, Insets> marginAccessor = new Callback<Node, Insets>() {
+        public Insets call(Node n) {
+            return getMargin(n);
+        }
+    };
+
     /**
      * Removes all stackpane constraints from the child node.
      * @param child the child node
@@ -262,20 +269,19 @@
     @Override protected double computeMinWidth(double height) {
         List<Node>managed = getManagedChildren();
         return getInsets().getLeft() +
-               computeMaxMinAreaWidth(managed, getMargins(managed), getAlignmentInternal().getHpos(), height) +
+               computeMaxMinAreaWidth(managed, marginAccessor, getAlignmentInternal().getHpos(), height) +
                getInsets().getRight();
     }
 
     @Override protected double computeMinHeight(double width) {
         List<Node>managed = getManagedChildren();
         return getInsets().getTop() +
-               computeMaxMinAreaHeight(managed, getMargins(managed), getAlignmentInternal().getVpos(), width) +
+               computeMaxMinAreaHeight(managed, marginAccessor, getAlignmentInternal().getVpos(), width) +
                getInsets().getBottom();
     }
 
     @Override protected double computePrefWidth(double height) {
         List<Node>managed = getManagedChildren();
-        Insets[] margins = getMargins(managed);
 //        double h = -1;
 //        boolean vertBias = false;
 //        for (Node child: managed) {
@@ -290,7 +296,7 @@
 //        }
         Insets padding = getInsets();
         return padding.getLeft() +
-               computeMaxPrefAreaWidth(managed, margins,
+               computeMaxPrefAreaWidth(managed, marginAccessor,
                                        (height == -1) ? -1 : (height - padding.getTop() - padding.getBottom()),
                                        getAlignmentInternal().getHpos()) +
                padding.getRight();
@@ -298,7 +304,6 @@
 
     @Override protected double computePrefHeight(double width) {
         List<Node>managed = getManagedChildren();
-        Insets[] margins = getMargins(managed);
 //        double w = -1;
 //        boolean horizBias = false;
 //        for (Node child: managed) {
@@ -313,19 +318,12 @@
 //        }
         Insets padding = getInsets();
         return padding.getTop() +
-               computeMaxPrefAreaHeight(managed, margins,
+               computeMaxPrefAreaHeight(managed, marginAccessor,
                                         (width == -1) ? -1 : (width - padding.getLeft() - padding.getRight()),
                                         getAlignmentInternal().getVpos()) +
                padding.getBottom();
     }
 
-    private Insets[] getMargins(List<Node>managed) {
-        Insets margins[] = new Insets[managed.size()];
-        for(int i = 0; i < margins.length; i++) {
-            margins[i] = getMargin(managed.get(i));
-        }
-        return margins;
-    }
 
     @Override protected void layoutChildren() {
         List<Node> managed = getManagedChildren();
@@ -402,8 +400,8 @@
      * {@inheritDoc}
      *
      */
-    
-    
+
+
     @Override
     public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
         return getClassCssMetaData();
--- a/javafx-ui-common/src/javafx/scene/layout/TilePane.java	Mon May 27 14:38:58 2013 +0400
+++ b/javafx-ui-common/src/javafx/scene/layout/TilePane.java	Wed May 29 16:02:44 2013 +0200
@@ -34,7 +34,6 @@
 import javafx.beans.property.ReadOnlyDoubleProperty;
 import javafx.beans.property.ReadOnlyDoubleWrapper;
 import javafx.css.CssMetaData;
-import javafx.css.StyleOrigin;
 import javafx.css.StyleableDoubleProperty;
 import javafx.css.StyleableIntegerProperty;
 import javafx.css.StyleableObjectProperty;
@@ -50,6 +49,7 @@
 import javafx.css.Styleable;
 
 import static javafx.geometry.Orientation.*;
+import javafx.util.Callback;
 
 
 /**
@@ -57,7 +57,7 @@
  * <p>
  * A horizontal tilepane (the default) will tile nodes in rows, wrapping at the
  * tilepane's width.  A vertical tilepane will tile nodes in columns,
- * wrapping at the tilepane's height.  
+ * wrapping at the tilepane's height.
  * <p>
  * The size of each "tile" defaults to the size needed to encompass the largest
  * preferred width and height of the tilepane's children and the tilepane
@@ -169,7 +169,7 @@
  *     TilePane tilepane = new TilePane();
  *     for (int i = 0; i < 20; i++) {
  *        Label title = new Label(imageTitle[i]):
- *        Imageview imageview = new ImageView(new Image(imageName[i])); 
+ *        Imageview imageview = new ImageView(new Image(imageName[i]));
  *        TilePane.setAlignment(label, Pos.BOTTOM_RIGHT);
  *        tilepane.getChildren().addAll(title, imageview);
  *     }
@@ -225,6 +225,12 @@
         return (Insets)getConstraint(node, MARGIN_CONSTRAINT);
     }
 
+    private static final Callback<Node, Insets> marginAccessor = new Callback<Node, Insets>() {
+        public Insets call(Node n) {
+            return getMargin(n);
+        }
+    };
+
     /**
      * Removes all tilepane constraints from the child node.
      * @param child the child node
@@ -349,8 +355,8 @@
                 public void invalidated() {
                     requestLayout();
                 }
-                
-                @Override 
+
+                @Override
                 public CssMetaData<TilePane, Orientation> getCssMetaData() {
                     return StyleableProperties.ORIENTATION;
                 }
@@ -368,7 +374,7 @@
         }
         return orientation;
     }
-    
+
     private ObjectProperty<Orientation> orientation;
     public final void setOrientation(Orientation value) { orientationProperty().set(value); }
     public final Orientation getOrientation() { return orientation == null ? HORIZONTAL : orientation.get();  }
@@ -391,7 +397,7 @@
                 public void invalidated() {
                     requestLayout();
                 }
-                
+
                 @Override
                 public CssMetaData<TilePane, Number> getCssMetaData() {
                     return StyleableProperties.PREF_ROWS;
@@ -410,7 +416,7 @@
         }
         return prefRows;
     }
-    
+
     private IntegerProperty prefRows;
     public final void setPrefRows(int value) { prefRowsProperty().set(value); }
     public final int getPrefRows() { return prefRows == null ? 5 : prefRows.get(); }
@@ -432,7 +438,7 @@
                 public void invalidated() {
                     requestLayout();
                 }
-                
+
                 @Override
                 public CssMetaData<TilePane, Number> getCssMetaData() {
                     return StyleableProperties.PREF_COLUMNS;
@@ -451,7 +457,7 @@
         }
         return prefColumns;
     }
-    
+
     private IntegerProperty prefColumns;
     public final void setPrefColumns(int value) { prefColumnsProperty().set(value); }
     public final int getPrefColumns() { return prefColumns == null ? 5 : prefColumns.get(); }
@@ -472,7 +478,7 @@
                 public void invalidated() {
                     requestLayout();
                 }
-                
+
                 @Override
                 public CssMetaData<TilePane, Number> getCssMetaData() {
                     return StyleableProperties.PREF_TILE_WIDTH;
@@ -513,8 +519,8 @@
                 public void invalidated() {
                     requestLayout();
                 }
-                
-                @Override 
+
+                @Override
                 public CssMetaData<TilePane, Number> getCssMetaData() {
                     return StyleableProperties.PREF_TILE_HEIGHT;
                 }
@@ -580,8 +586,8 @@
                 public void invalidated() {
                     requestLayout();
                 }
-                
-                @Override 
+
+                @Override
                 public CssMetaData<TilePane, Number> getCssMetaData() {
                     return StyleableProperties.HGAP;
                 }
@@ -599,11 +605,11 @@
         }
         return hgap;
     }
-    
+
     private DoubleProperty hgap;
     public final void setHgap(double value) { hgapProperty().set(value); }
     public final double getHgap() { return hgap == null ? 0 : hgap.get(); }
-    
+
     /**
      * The amount of vertical space between each tile in a column.
      */
@@ -614,8 +620,8 @@
                 public void invalidated() {
                     requestLayout();
                 }
-                
-                @Override 
+
+                @Override
                 public CssMetaData<TilePane, Number> getCssMetaData() {
                     return StyleableProperties.VGAP;
                 }
@@ -633,7 +639,7 @@
         }
         return vgap;
     }
-    
+
     private DoubleProperty vgap;
     public final void setVgap(double value) { vgapProperty().set(value); }
     public final double getVgap() { return vgap == null ? 0 : vgap.get(); }
@@ -646,7 +652,7 @@
      * <p>For a vertical tilepane, each column will be aligned within the tilepane's height
      * using the alignment's vpos value, and the columns will be aligned within the
      * tilepane's width using the alignment's hpos value.
-     * 
+     *
      */
     public final ObjectProperty<Pos> alignmentProperty() {
         if (alignment == null) {
@@ -655,8 +661,8 @@
                 public void invalidated() {
                     requestLayout();
                 }
-                
-                @Override 
+
+                @Override
                 public CssMetaData<TilePane, Pos> getCssMetaData() {
                     return StyleableProperties.ALIGNMENT;
                 }
@@ -695,7 +701,7 @@
                 public void invalidated() {
                     requestLayout();
                 }
-                
+
                 @Override
                 public CssMetaData<TilePane, Pos> getCssMetaData() {
                     return StyleableProperties.TILE_ALIGNMENT;
@@ -714,7 +720,7 @@
         }
         return tileAlignment;
     }
-    
+
     private ObjectProperty<Pos> tileAlignment;
     public final void setTileAlignment(Pos value) { tileAlignmentProperty().set(value); }
     public final Pos getTileAlignment() { return tileAlignment == null ? Pos.CENTER : tileAlignment.get(); }
@@ -785,19 +791,11 @@
                snapSpace(insets.getBottom());
     }
 
-    private Insets[] getMargins(List<Node>managed) {
-        Insets margins[] = new Insets[managed.size()];
-        for(int i = 0; i < margins.length; i++) {
-            margins[i] = getMargin(managed.get(i));
-        }
-        return margins;
-    }
 
     private double computeTileWidth(List<Node>managed) {
         double preftilewidth = getPrefTileWidth();
         if (preftilewidth == USE_COMPUTED_SIZE) {
             if (computedTileWidth == -1) {
-                Insets[] margins = getMargins(managed);
                 double h = -1;
                 boolean vertBias = false;
                 for (int i = 0, size = managed.size(); i < size; i++) {
@@ -809,9 +807,9 @@
                 }
                 if (vertBias) {
                     // widest may depend on height of tile
-                    h = computeMaxPrefAreaHeight(managed, margins, -1, getTileAlignmentInternal().getVpos());
+                    h = computeMaxPrefAreaHeight(managed, marginAccessor, -1, getTileAlignmentInternal().getVpos());
                 }
-                computedTileWidth = computeMaxPrefAreaWidth(managed, margins, h, getTileAlignmentInternal().getHpos());
+                computedTileWidth = computeMaxPrefAreaWidth(managed, marginAccessor, h, getTileAlignmentInternal().getHpos());
             }
             return snapSize(computedTileWidth);
         }
@@ -822,7 +820,6 @@
         double preftileheight = getPrefTileHeight();
         if (preftileheight == USE_COMPUTED_SIZE) {
             if (computedTileHeight == -1) {
-                Insets[] margins = getMargins(managed);
                 double w = -1;
                 boolean horizBias = false;
                 for (int i = 0, size = managed.size(); i < size; i++) {
@@ -834,9 +831,9 @@
                 }
                 if (horizBias) {
                     // tallest may depend on width of tile
-                    w = computeMaxPrefAreaWidth(managed, margins, -1, getTileAlignmentInternal().getHpos());
+                    w = computeMaxPrefAreaWidth(managed, marginAccessor, -1, getTileAlignmentInternal().getHpos());
                 }
-                computedTileHeight = computeMaxPrefAreaHeight(managed, getMargins(managed), w, getTileAlignmentInternal().getVpos());
+                computedTileHeight = computeMaxPrefAreaHeight(managed, marginAccessor, w, getTileAlignmentInternal().getVpos());
             }
             return snapSize(computedTileHeight);
         }
@@ -926,7 +923,7 @@
             double tileY = yoffset + (r * (getTileHeight() + vgap));
 
             Pos childAlignment = getAlignment(child);
-            
+
             layoutInArea(child, tileX, tileY, getTileWidth(), getTileHeight(), baselineOffset,
                     getMargin(child),
                     childAlignment != null? childAlignment.getHpos() : getTileAlignmentInternal().getHpos(),
@@ -963,9 +960,9 @@
       */
      private static class StyleableProperties {
 
-         private static final CssMetaData<TilePane,Pos> ALIGNMENT = 
+         private static final CssMetaData<TilePane,Pos> ALIGNMENT =
              new CssMetaData<TilePane,Pos>("-fx-alignment",
-                 new EnumConverter<Pos>(Pos.class), 
+                 new EnumConverter<Pos>(Pos.class),
                  Pos.TOP_LEFT) {
 
             @Override
@@ -978,8 +975,8 @@
                 return (StyleableProperty<Pos>)node.alignmentProperty();
             }
         };
-         
-         private static final CssMetaData<TilePane,Number> PREF_COLUMNS = 
+
+         private static final CssMetaData<TilePane,Number> PREF_COLUMNS =
              new CssMetaData<TilePane,Number>("-fx-pref-columns",
                  SizeConverter.getInstance(), 5.0) {
 
@@ -994,8 +991,8 @@
                 return (StyleableProperty<Number>)node.prefColumnsProperty();
             }
         };
-                 
-         private static final CssMetaData<TilePane,Number> HGAP = 
+
+         private static final CssMetaData<TilePane,Number> HGAP =
              new CssMetaData<TilePane,Number>("-fx-hgap",
                  SizeConverter.getInstance(), 0.0) {
 
@@ -1010,8 +1007,8 @@
                 return (StyleableProperty<Number>)node.hgapProperty();
             }
         };
-         
-         private static final CssMetaData<TilePane,Number> PREF_ROWS = 
+
+         private static final CssMetaData<TilePane,Number> PREF_ROWS =
              new CssMetaData<TilePane,Number>("-fx-pref-rows",
                  SizeConverter.getInstance(), 5.0) {
 
@@ -1027,9 +1024,9 @@
             }
         };
 
-         private static final CssMetaData<TilePane,Pos> TILE_ALIGNMENT = 
+         private static final CssMetaData<TilePane,Pos> TILE_ALIGNMENT =
              new CssMetaData<TilePane,Pos>("-fx-tile-alignment",
-                 new EnumConverter<Pos>(Pos.class), 
+                 new EnumConverter<Pos>(Pos.class),
                  Pos.CENTER) {
 
             @Override
@@ -1043,8 +1040,8 @@
                 return (StyleableProperty<Pos>)node.tileAlignmentProperty();
             }
          };
-         
-         private static final CssMetaData<TilePane,Number> PREF_TILE_WIDTH = 
+
+         private static final CssMetaData<TilePane,Number> PREF_TILE_WIDTH =
              new CssMetaData<TilePane,Number>("-fx-pref-tile-width",
                  SizeConverter.getInstance(), USE_COMPUTED_SIZE) {
 
@@ -1060,7 +1057,7 @@
             }
         };
 
-         private static final CssMetaData<TilePane,Number> PREF_TILE_HEIGHT = 
+         private static final CssMetaData<TilePane,Number> PREF_TILE_HEIGHT =
              new CssMetaData<TilePane,Number>("-fx-pref-tile-height",
                  SizeConverter.getInstance(), USE_COMPUTED_SIZE) {
 
@@ -1076,17 +1073,17 @@
             }
          };
 
-         private static final CssMetaData<TilePane,Orientation> ORIENTATION = 
+         private static final CssMetaData<TilePane,Orientation> ORIENTATION =
              new CssMetaData<TilePane,Orientation>("-fx-orientation",
-                 new EnumConverter<Orientation>(Orientation.class), 
+                 new EnumConverter<Orientation>(Orientation.class),
                  Orientation.HORIZONTAL) {
 
                 @Override
                 public Orientation getInitialValue(TilePane node) {
-                    // A vertical TilePane should remain vertical 
+                    // A vertical TilePane should remain vertical
                     return node.getOrientation();
                 }
-                                          
+
                 @Override
                 public boolean isSettable(TilePane node) {
                     return node.orientation == null ||
@@ -1098,8 +1095,8 @@
                     return (StyleableProperty<Orientation>)node.orientationProperty();
                 }
          };
-         
-         private static final CssMetaData<TilePane,Number> VGAP = 
+
+         private static final CssMetaData<TilePane,Number> VGAP =
              new CssMetaData<TilePane,Number>("-fx-vgap",
                  SizeConverter.getInstance(), 0.0) {
 
@@ -1117,7 +1114,7 @@
 
          private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
          static {
-            final List<CssMetaData<? extends Styleable, ?>> styleables = 
+            final List<CssMetaData<? extends Styleable, ?>> styleables =
                 new ArrayList<CssMetaData<? extends Styleable, ?>>(Region.getClassCssMetaData());
             styleables.add(ALIGNMENT);
             styleables.add(HGAP);
@@ -1144,8 +1141,8 @@
      * {@inheritDoc}
      *
      */
-    
-    
+
+
     @Override
     public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
         return getClassCssMetaData();
--- a/javafx-ui-common/src/javafx/scene/layout/VBox.java	Mon May 27 14:38:58 2013 +0400
+++ b/javafx-ui-common/src/javafx/scene/layout/VBox.java	Wed May 29 16:02:44 2013 +0200
@@ -46,6 +46,7 @@
 import com.sun.javafx.css.converters.EnumConverter;
 import com.sun.javafx.css.converters.SizeConverter;
 import javafx.css.Styleable;
+import javafx.util.Callback;
 
 /**
  * VBox lays out its children in a single vertical column.
@@ -186,6 +187,12 @@
         return (Insets)getConstraint(child, MARGIN_CONSTRAINT);
     }
 
+    private static final Callback<Node, Insets> marginAccessor = new Callback<Node, Insets>() {
+        public Insets call(Node n) {
+            return getMargin(n);
+        }
+    };
+
     /**
      * Removes all vbox constraints from the child node.
      * @param child the child node
@@ -365,9 +372,9 @@
         if (getContentBias() == Orientation.VERTICAL) {
             double prefHeights[] = getAreaHeights(managed, -1, false);
             adjustAreaHeights(managed, prefHeights, height, -1);
-            contentWidth = computeMaxMinAreaWidth(managed, getMargins(managed), prefHeights, getAlignmentInternal().getHpos());
+            contentWidth = computeMaxMinAreaWidth(managed, marginAccessor, prefHeights, getAlignmentInternal().getHpos());
         } else {
-            contentWidth = computeMaxMinAreaWidth(managed, getMargins(managed), getAlignmentInternal().getHpos());
+            contentWidth = computeMaxMinAreaWidth(managed, marginAccessor, getAlignmentInternal().getHpos());
         }
         return snapSpace(insets.getLeft()) + contentWidth + snapSpace(insets.getRight());
     }
@@ -386,9 +393,9 @@
         if (getContentBias() == Orientation.VERTICAL) {
             double prefHeights[] = getAreaHeights(managed, -1, false);
             adjustAreaHeights(managed, prefHeights, height, -1);
-            contentWidth = computeMaxPrefAreaWidth(managed, getMargins(managed), prefHeights, getAlignmentInternal().getHpos());
+            contentWidth = computeMaxPrefAreaWidth(managed, marginAccessor, prefHeights, getAlignmentInternal().getHpos());
         } else {
-            contentWidth = computeMaxPrefAreaWidth(managed, getMargins(managed), getAlignmentInternal().getHpos());
+            contentWidth = computeMaxPrefAreaWidth(managed, marginAccessor, getAlignmentInternal().getHpos());
         }
         return snapSpace(insets.getLeft()) + contentWidth + snapSpace(insets.getRight());
     }
@@ -401,13 +408,6 @@
         return d;
     }
 
-    private Insets[] getMargins(List<Node>managed) {
-        Insets margins[] = new Insets[managed.size()];
-        for(int i = 0; i < margins.length; i++) {
-            margins[i] = getMargin(managed.get(i));
-        }
-        return margins;
-    }
 
     private double[] getAreaHeights(List<Node>managed, double width, boolean minimum) {
         double[] prefAreaHeights = new double [managed.size()];
--- a/javafx-ui-common/src/javafx/scene/shape/Arc.java	Mon May 27 14:38:58 2013 +0400
+++ b/javafx-ui-common/src/javafx/scene/shape/Arc.java	Wed May 29 16:02:44 2013 +0200
@@ -190,7 +190,7 @@
      *
      * @defaultValue 0.0
      */
-    private DoubleProperty radiusX = new DoublePropertyBase() {
+    private final DoubleProperty radiusX = new DoublePropertyBase() {
 
         @Override
         public void invalidated() {
@@ -227,7 +227,7 @@
      *
      * @defaultValue 0.0
      */
-    private DoubleProperty radiusY = new DoublePropertyBase() {
+    private final DoubleProperty radiusY = new DoublePropertyBase() {
 
         @Override
         public void invalidated() {
@@ -304,7 +304,7 @@
      *
      * @defaultValue 0.0
      */
-    private DoubleProperty length = new DoublePropertyBase() {
+    private final DoubleProperty length = new DoublePropertyBase() {
 
         @Override
         public void invalidated() {
--- a/javafx-ui-common/src/javafx/scene/shape/Circle.java	Mon May 27 14:38:58 2013 +0400
+++ b/javafx-ui-common/src/javafx/scene/shape/Circle.java	Wed May 29 16:02:44 2013 +0200
@@ -202,7 +202,7 @@
      *
      * @defaultValue 0.0
      */
-    private DoubleProperty radius = new DoublePropertyBase() {
+    private final DoubleProperty radius = new DoublePropertyBase() {
 
         @Override
         public void invalidated() {
--- a/javafx-ui-common/src/javafx/scene/shape/Ellipse.java	Mon May 27 14:38:58 2013 +0400
+++ b/javafx-ui-common/src/javafx/scene/shape/Ellipse.java	Wed May 29 16:02:44 2013 +0200
@@ -180,7 +180,7 @@
      *
      * @defaultValue 0.0
      */
-    private DoubleProperty radiusX = new DoublePropertyBase() {
+    private final DoubleProperty radiusX = new DoublePropertyBase() {
 
         @Override
         public void invalidated() {
@@ -216,7 +216,7 @@
      *
      * @defaultValue 0.0
      */
-    private DoubleProperty radiusY = new DoublePropertyBase() {
+    private final DoubleProperty radiusY = new DoublePropertyBase() {
 
         @Override
         public void invalidated() {
--- a/javafx-ui-common/src/javafx/scene/shape/Line.java	Mon May 27 14:38:58 2013 +0400
+++ b/javafx-ui-common/src/javafx/scene/shape/Line.java	Wed May 29 16:02:44 2013 +0200
@@ -99,22 +99,7 @@
      *
      * @defaultValue 0.0
      */
-    private DoubleProperty startX;
-
-
-    public final void setStartX(double value) {
-        if (startX != null || value != 0.0) {
-            startXProperty().set(value);
-        }
-    }
-
-    public final double getStartX() {
-        return startX == null ? 0.0 : startX.get();
-    }
-
-    public final DoubleProperty startXProperty() {
-        if (startX == null) {
-            startX = new DoublePropertyBase() {
+    private final DoubleProperty startX = new DoublePropertyBase() {
 
                 @Override
                 public void invalidated() {
@@ -132,7 +117,17 @@
                     return "startX";
                 }
             };
-        }
+
+
+    public final void setStartX(double value) {
+        startX.set(value);
+    }
+
+    public final double getStartX() {
+        return startX.get();
+    }
+
+    public final DoubleProperty startXProperty() {
         return startX;
     }
 
@@ -141,23 +136,7 @@
      *
      * @defaultValue 0.0
      */
-    private DoubleProperty startY;
-
-
-
-    public final void setStartY(double value) {
-        if (startY != null || value != 0.0) {
-            startYProperty().set(value);
-        }
-    }
-
-    public final double getStartY() {
-        return startY == null ? 0.0 : startY.get();
-    }
-
-    public final DoubleProperty startYProperty() {
-        if (startY == null) {
-            startY = new DoublePropertyBase() {
+    private final DoubleProperty startY = new DoublePropertyBase() {
 
                 @Override
                 public void invalidated() {
@@ -175,7 +154,17 @@
                     return "startY";
                 }
             };
-        }
+
+
+    public final void setStartY(double value) {
+        startY.set(value);
+    }
+
+    public final double getStartY() {
+        return startY.get();
+    }
+
+    public final DoubleProperty startYProperty() {
         return startY;
     }
 
@@ -184,23 +173,7 @@
      *
      * @defaultValue 0.0
      */
-    private DoubleProperty endX;
-
-
-
-    public final void setEndX(double value) {
-        if (endX != null || value != 0.0) {
-            endXProperty().set(value);
-        }
-    }
-
-    public final double getEndX() {
-        return endX == null ? 0.0 : endX.get();
-    }
-
-    public final DoubleProperty endXProperty() {
-        if (endX == null) {
-            endX = new DoublePropertyBase() {
+    private final DoubleProperty endX = new DoublePropertyBase() {
 
         @Override
         public void invalidated() {
@@ -218,7 +191,18 @@
             return "endX";
         }
     };
+
+
+
+    public final void setEndX(double value) {
+        endX.set(value);
     }
+
+    public final double getEndX() {
+        return endX.get();
+    }
+
+    public final DoubleProperty endXProperty() {
         return endX;
     }
 
@@ -227,21 +211,7 @@
      *
      * @defaultValue 0.0
      */
-    private DoubleProperty endY;
-
-    public final void setEndY(double value) {
-        if (endY != null || value != 0.0) {
-            endYProperty().set(value);
-        }
-    }
-
-    public final double getEndY() {
-        return endY == null ? 0.0 : endY.get();
-    }
-
-    public final DoubleProperty endYProperty() {
-        if (endY == null) {
-            endY = new DoublePropertyBase() {
+    private final DoubleProperty endY = new DoublePropertyBase() {
 
         @Override
         public void invalidated() {
@@ -259,7 +229,16 @@
             return "endY";
         }
     };
+
+    public final void setEndY(double value) {
+        endY.set(value);
     }
+
+    public final double getEndY() {
+        return endY.get();
+    }
+
+    public final DoubleProperty endYProperty() {
         return endY;
     }
 
--- a/javafx-ui-common/src/javafx/scene/shape/Rectangle.java	Mon May 27 14:38:58 2013 +0400
+++ b/javafx-ui-common/src/javafx/scene/shape/Rectangle.java	Wed May 29 16:02:44 2013 +0200
@@ -210,7 +210,7 @@
      *
      * @defaultValue 0.0
      */
-    private DoubleProperty width = new DoublePropertyBase() {
+    private final DoubleProperty width = new DoublePropertyBase() {
 
         @Override
         public void invalidated() {
@@ -246,7 +246,7 @@
      *
      * @defaultValue 0.0
      */
-    private DoubleProperty height = new DoublePropertyBase() {
+    private final DoubleProperty height = new DoublePropertyBase() {
 
         @Override
         public void invalidated() {
--- a/javafx-ui-common/test/unit/javafx/scene/layout/HBoxTest.java	Mon May 27 14:38:58 2013 +0400
+++ b/javafx-ui-common/test/unit/javafx/scene/layout/HBoxTest.java	Wed May 29 16:02:44 2013 +0200
@@ -734,7 +734,7 @@
         assertEquals(300, hbox.prefWidth(-1), 1e-100);
         assertEquals(200, hbox.prefHeight(-1), 1e-100);
         assertEquals(20, hbox.minWidth(-1), 1e-100); //MockBias minWidth == 10
-        assertEquals(200, hbox.minHeight(-1), 1e-100);
+        assertEquals(1, hbox.minHeight(-1), 1e-100); // If both have max width, min height can be indeed 1
         assertEquals(Math.ceil(Math.max(100 * 100 / 50.0, 200 * 200 / 150.0)), hbox.minHeight(200), 1e-100);
         System.out.println("************************************");