modules/controls/src/main/java/javafx/scene/control/skin/SplitPaneSkin.java

Print this page
rev 9240 : 8076423: JEP 253: Prepare JavaFX UI Controls & CSS APIs for Modularization

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -21,11 +21,11 @@
  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
 
-package com.sun.javafx.scene.control.skin;
+package javafx.scene.control.skin;
 
 import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ObservableValue;
 import javafx.collections.FXCollections;
 import javafx.collections.ListChangeListener;

@@ -34,30 +34,59 @@
 import javafx.geometry.NodeOrientation;
 import javafx.geometry.Orientation;
 import javafx.geometry.VPos;
 import javafx.scene.Cursor;
 import javafx.scene.Node;
+import javafx.scene.control.Accordion;
+import javafx.scene.control.Control;
+import javafx.scene.control.SkinBase;
 import javafx.scene.control.SplitPane;
 import javafx.scene.input.MouseEvent;
 import javafx.scene.layout.StackPane;
 import javafx.scene.shape.Rectangle;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
-import com.sun.javafx.scene.control.behavior.BehaviorBase;
 
-public class SplitPaneSkin extends BehaviorSkinBase<SplitPane, BehaviorBase<SplitPane>>  {
+/**
+ * Default skin implementation for the {@link SplitPane} control.
+ *
+ * @see SplitPane
+ * @since 9
+ */
+public class SplitPaneSkin extends SkinBase<SplitPane> {
+
+    /***************************************************************************
+     *                                                                         *
+     * Private fields                                                          *
+     *                                                                         *
+     **************************************************************************/
 
     private ObservableList<Content> contentRegions;
     private ObservableList<ContentDivider> contentDividers;
     private boolean horizontal;
     
-    public SplitPaneSkin(final SplitPane splitPane) {
-        super(splitPane, new BehaviorBase<>(splitPane, Collections.emptyList()));
-//        splitPane.setManaged(false);
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Constructors                                                            *
+     *                                                                         *
+     **************************************************************************/
+
+    /**
+     * Creates a new SplitPaneSkin instance, installing the necessary child
+     * nodes into the Control {@link Control#getChildren() children} list, as
+     * well as the necessary {@link Node#getInputMap() input mappings} for
+     * handling key, mouse, etc events.
+     *
+     * @param control The control that this skin should be installed onto.
+     */
+    public SplitPaneSkin(final SplitPane control) {
+        super(control);
+//        control.setManaged(false);
         horizontal = getSkinnable().getOrientation() == Orientation.HORIZONTAL;
         
         contentRegions = FXCollections.<Content>observableArrayList();
         contentDividers = FXCollections.<ContentDivider>observableArrayList();
 

@@ -69,869 +98,869 @@
 
         for (SplitPane.Divider d: getSkinnable().getDividers()) {
             addDivider(d);
         }
 
-        registerChangeListener(splitPane.orientationProperty(), "ORIENTATION");
-        registerChangeListener(splitPane.widthProperty(), "WIDTH");
-        registerChangeListener(splitPane.heightProperty(), "HEIGHT");
-    }
-
-    private void addContent(int index, Node n) {
-        Content c = new Content(n);
-        contentRegions.add(index, c);
-        getChildren().add(index, c);
-    }
-
-    private void removeContent(Node n) {
-        for (Content c: contentRegions) {
-            if (c.getContent().equals(n)) {
-                getChildren().remove(c);
-                contentRegions.remove(c);
-                break;
-            }
+        registerChangeListener(control.orientationProperty(), e -> {
+            this.horizontal = getSkinnable().getOrientation() == Orientation.HORIZONTAL;
+            this.previousSize = -1;
+            for (ContentDivider c: contentDividers) {
+                c.setGrabberStyle(horizontal);
         }
+            getSkinnable().requestLayout();
+        });
+        registerChangeListener(control.widthProperty(), e -> getSkinnable().requestLayout());
+        registerChangeListener(control.heightProperty(), e -> getSkinnable().requestLayout());
     }
 
-    private void initializeContentListener() {
-        getSkinnable().getItems().addListener((ListChangeListener<Node>) c -> {
-            while (c.next()) {
-                if (c.wasPermutated() || c.wasUpdated()) {
-                    /**
-                     * the contents were either moved, or updated.
-                     * rebuild the contents to re-sync
-                     */
-                    getChildren().clear();
-                    contentRegions.clear();
-                    int index = 0;
-                    for (Node n : c.getList()) {
-                        addContent(index++, n);
-                    }
 
-                } else {
-                    for (Node n : c.getRemoved()) {
-                        removeContent(n);
-                    }
 
-                    int index = c.getFrom();
-                    for (Node n : c.getAddedSubList()) {
-                        addContent(index++, n);
-                    }
-                }
-            }
-            // TODO there may be a more efficient way than rebuilding all the dividers
-            // everytime the list changes.
-            removeAllDividers();
-            for (SplitPane.Divider d: getSkinnable().getDividers()) {
-                addDivider(d);
-            }
-        });
-    }
+    /***************************************************************************
+     *                                                                         *
+     * Public API                                                              *
+     *                                                                         *
+     **************************************************************************/
           
-    // This listener is to be removed from 'removed' dividers and added to 'added' dividers
-    class PosPropertyListener implements ChangeListener<Number> {
-        ContentDivider divider;
+    /** {@inheritDoc} */
+    @Override protected void layoutChildren(final double x, final double y,
+                                            final double w, final double h) {
+        final SplitPane s = getSkinnable();
+        final double sw = s.getWidth();
+        final double sh = s.getHeight();
         
-        public PosPropertyListener(ContentDivider divider) {
-            this.divider = divider;
+        if (!s.isVisible() ||
+                (horizontal ? sw == 0 : sh == 0) ||
+                contentRegions.isEmpty()) {
+            return;
         }
         
-        @Override public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {  
-            if (checkDividerPos) {
-                // When checking is enforced, we know that the position was set explicitly
-                divider.posExplicit = true;
-            }
-            getSkinnable().requestLayout();
+        double dividerWidth = contentDividers.isEmpty() ? 0 : contentDividers.get(0).prefWidth(-1);
+
+        if (contentDividers.size() > 0 && previousSize != -1 && previousSize != (horizontal ? sw  : sh)) {
+            //This algorithm adds/subtracts a little to each panel on every resize
+            List<Content> resizeList = new ArrayList<Content>();
+            for (Content c: contentRegions) {
+                if (c.isResizableWithParent()) {
+                    resizeList.add(c);
         }
     }
        
-    private void checkDividerPosition(ContentDivider divider, double newPos, double oldPos) {
-        double dividerWidth = divider.prefWidth(-1);
-        Content left = getLeft(divider);
-        Content right = getRight(divider);
-        double minLeft = left == null ? 0 : (horizontal) ? left.minWidth(-1) : left.minHeight(-1);
-        double minRight = right == null ? 0 : (horizontal) ? right.minWidth(-1) : right.minHeight(-1);
-        double maxLeft = left == null ? 0 :
-            left.getContent() != null ? (horizontal) ? left.getContent().maxWidth(-1) : left.getContent().maxHeight(-1) : 0;
-        double maxRight = right == null ? 0 :
-            right.getContent() != null ? (horizontal) ? right.getContent().maxWidth(-1) : right.getContent().maxHeight(-1) : 0;                        
+            double delta = (horizontal ? s.getWidth() : s.getHeight()) - previousSize;
+            boolean growing = delta > 0;
                
-        double previousDividerPos = 0;
-        double nextDividerPos = getSize();
-        int index = contentDividers.indexOf(divider);
+            delta = Math.abs(delta);
 
-        if (index - 1 >= 0) {
-            previousDividerPos = contentDividers.get(index - 1).getDividerPos();
-            if (previousDividerPos == -1) {
-                // Get the divider position if it hasn't been initialized.
-                previousDividerPos = getAbsoluteDividerPos(contentDividers.get(index - 1));
-            }
-        }
-        if (index + 1 < contentDividers.size()) {
-            nextDividerPos = contentDividers.get(index + 1).getDividerPos();
-            if (nextDividerPos == -1) {
-                // Get the divider position if it hasn't been initialized.
-                nextDividerPos = getAbsoluteDividerPos(contentDividers.get(index + 1));
-            }
+            if (delta != 0 && !resizeList.isEmpty()) {
+                int portion = (int)(delta)/resizeList.size();
+                int remainder = (int)delta%resizeList.size();
+                int size = 0;
+                if (portion == 0) {
+                    portion = remainder;
+                    size = remainder;
+                    remainder = 0;
+                } else {
+                    size = portion * resizeList.size();
         }
         
-        // Set the divider into the correct position by looking at the max and min content sizes.
-        checkDividerPos = false;
-        if (newPos > oldPos) {
-            double max = previousDividerPos == 0 ? maxLeft : previousDividerPos + dividerWidth + maxLeft;
-            double min = nextDividerPos - minRight - dividerWidth;                
-            double stopPos = Math.min(max, min);
-            if (newPos >= stopPos) {                
-                setAbsoluteDividerPos(divider, stopPos);
-            } else {
-                double rightMax = nextDividerPos - maxRight - dividerWidth;                    
-                if (newPos <= rightMax) {
-                    setAbsoluteDividerPos(divider, rightMax);
+                while (size > 0 && !resizeList.isEmpty()) {
+                    if (growing) {
+                        lastDividerUpdate++;
                 } else {                    
-                    setAbsoluteDividerPos(divider, newPos);
+                        lastDividerUpdate--;
+                        if (lastDividerUpdate < 0) {
+                            lastDividerUpdate = contentRegions.size() - 1;
                 }
             }             
+                    int id = lastDividerUpdate%contentRegions.size();
+                    Content content = contentRegions.get(id);
+                    if (content.isResizableWithParent() && resizeList.contains(content)) {
+                        double area = content.getArea();
+                        if (growing) {
+                            double max = horizontal ? content.maxWidth(-1) : content.maxHeight(-1);
+                            if ((area + portion) <= max) {
+                                area += portion;
         } else {
-            double max = nextDividerPos - maxRight - dividerWidth;
-            double min = previousDividerPos == 0 ? minLeft : previousDividerPos + minLeft + dividerWidth;
-            double stopPos = Math.max(max, min);
-            if (newPos <= stopPos) {
-                setAbsoluteDividerPos(divider, stopPos);
+                                resizeList.remove(content);
+                                continue;
+                            }
             } else {
-                double leftMax = previousDividerPos + maxLeft + dividerWidth;
-                if (newPos >= leftMax) {
-                    setAbsoluteDividerPos(divider, leftMax);
+                            double min = horizontal ? content.minWidth(-1) : content.minHeight(-1);
+                            if ((area - portion) >= min) {
+                                area -= portion;
                 } else {
-                    setAbsoluteDividerPos(divider, newPos);
+                                resizeList.remove(content);
+                                continue;
                 }
             }                
+                        content.setArea(area);
+                        size -= portion;
+                        if (size == 0 && remainder != 0) {
+                            portion = remainder;
+                            size = remainder;
+                            remainder = 0;
+                        } else if (size == 0) {
+                            break;
         }                    
-        checkDividerPos = true;
     }
-    
-    private void addDivider(SplitPane.Divider d) {
-        ContentDivider c = new ContentDivider(d);        
-        c.setInitialPos(d.getPosition());
-        c.setDividerPos(-1);
-        ChangeListener<Number> posPropertyListener = new PosPropertyListener(c);
-        c.setPosPropertyListener(posPropertyListener);
-        d.positionProperty().addListener(posPropertyListener);
-        initializeDivderEventHandlers(c);
-        contentDividers.add(c);
-        getChildren().add(c);
     }
 
-    private void removeAllDividers() {
-        ListIterator<ContentDivider> dividers = contentDividers.listIterator();
-        while (dividers.hasNext()) {
-            ContentDivider c = dividers.next();
-            getChildren().remove(c);
-            c.getDivider().positionProperty().removeListener(c.getPosPropertyListener());
-            dividers.remove();
+                // If we are resizing the window save the current area into
+                // resizableWithParentArea.  We use this value during layout.
+                {
+                    for (Content c: contentRegions) {
+                        c.setResizableWithParentArea(c.getArea());
+                        c.setAvailable(0);
         }
-        lastDividerUpdate = 0;
+                }
+                resize = true;
     }
 
-    private void initializeDivderEventHandlers(final ContentDivider divider) {
-        // TODO: do we need to consume all mouse events?
-        // they only bubble to the skin which consumes them by default
-        divider.addEventHandler(MouseEvent.ANY, event -> {
-            event.consume();
-        });
-
-        divider.setOnMousePressed(e -> {
-            if (horizontal) {
-                divider.setInitialPos(divider.getDividerPos());
-                divider.setPressPos(e.getSceneX());
-                divider.setPressPos(getSkinnable().getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT
-                        ? getSkinnable().getWidth() - e.getSceneX() : e.getSceneX());
+            previousSize = horizontal ? sw : sh;
             } else {
-                divider.setInitialPos(divider.getDividerPos());
-                divider.setPressPos(e.getSceneY());
+            previousSize = horizontal ? sw : sh;
             }
-            e.consume();
-        });
 
-        divider.setOnMouseDragged(e -> {
-            double delta = 0;
-            if (horizontal) {
-                delta = getSkinnable().getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT
-                        ? getSkinnable().getWidth() - e.getSceneX() : e.getSceneX();
-            } else {
-                delta = e.getSceneY();
+        // If the window is less than the min size we want to resize
+        // proportionally
+        double minSize = totalMinSize();
+        if (minSize > (horizontal ? w : h)) {
+            double percentage = 0;
+            for (int i = 0; i < contentRegions.size(); i++) {
+                Content c = contentRegions.get(i);
+                double min = horizontal ? c.minWidth(-1) : c.minHeight(-1);
+                percentage = min/minSize;
+                c.setArea(snapSpace(percentage * (horizontal ? w : h)));
+                c.setAvailable(0);
             }
-            delta -= divider.getPressPos();
-            setAndCheckAbsoluteDividerPos(divider, Math.ceil(divider.getInitialPos() + delta));
-            e.consume();
-        });
+            setupContentAndDividerForLayout();
+            layoutDividersAndContent(w, h);
+            resize = false;
+            return;
     }
 
-    private Content getLeft(ContentDivider d) {
-        int index = contentDividers.indexOf(d);
-        if (index != -1) {
-            return contentRegions.get(index);
+        for(int trys = 0; trys < 10; trys++) {
+            // Compute the area in between each divider.
+            ContentDivider previousDivider = null;
+            ContentDivider divider = null;
+            for (int i = 0; i < contentRegions.size(); i++) {
+                double space = 0;
+                if (i < contentDividers.size()) {
+                    divider = contentDividers.get(i);
+                    if (divider.posExplicit) {
+                        checkDividerPosition(divider, posToDividerPos(divider, divider.d.getPosition()),
+                                divider.getDividerPos());
         }
-        return null;
+                    if (i == 0) {
+                        // First panel
+                        space = getAbsoluteDividerPos(divider);
+                    } else {
+                        double newPos = getAbsoluteDividerPos(previousDivider) + dividerWidth;
+                        // Middle panels
+                        if (getAbsoluteDividerPos(divider) <= getAbsoluteDividerPos(previousDivider)) {
+                            // The current divider and the previous divider share the same position
+                            // or the current divider position is less than the previous position.
+                            // We will set the divider next to the previous divider.
+                            setAndCheckAbsoluteDividerPos(divider, newPos);
     }
-
-    private Content getRight(ContentDivider d) {
-        int index = contentDividers.indexOf(d);
-        if (index != -1) {
-            return contentRegions.get(index + 1);
+                        space = getAbsoluteDividerPos(divider) - newPos;
         }
-        return null;
+                } else if (i == contentDividers.size()) {
+                    // Last panel
+                    space = (horizontal ? w : h) - (previousDivider != null ? getAbsoluteDividerPos(previousDivider) + dividerWidth : 0);
     }
-
-    @Override protected void handleControlPropertyChanged(String property) {
-        super.handleControlPropertyChanged(property);
-        if ("ORIENTATION".equals(property)) {
-            this.horizontal = getSkinnable().getOrientation() == Orientation.HORIZONTAL;
-            this.previousSize = -1;
-            for (ContentDivider c: contentDividers) {
-                c.setGrabberStyle(horizontal);
+                if (!resize || divider.posExplicit) {
+                    contentRegions.get(i).setArea(space);
             }
-            getSkinnable().requestLayout();
-        } else if ("WIDTH".equals(property) || "HEIGHT".equals(property)) {
-            getSkinnable().requestLayout();
+                previousDivider = divider;
         }
+
+            // Compute the amount of space we have available.
+            // Available is amount of space we can take from a panel before we reach its min.
+            // If available is negative we don't have enough space and we will
+            // proportionally take the space from the other availables.  If we have extra space
+            // we will porportionally give it to the others
+            double spaceRequested = 0;
+            double extraSpace = 0;
+            for (Content c: contentRegions) {
+                double max = 0;
+                double min = 0;
+                if (c != null) {
+                    max = horizontal ? c.maxWidth(-1) : c.maxHeight(-1);
+                    min = horizontal ? c.minWidth(-1) : c.minHeight(-1);
     }
 
-    // Value is the left edge of the divider
-    private void setAbsoluteDividerPos(ContentDivider divider, double value) {
-        if (getSkinnable().getWidth() > 0 && getSkinnable().getHeight() > 0 && divider != null) {
-            SplitPane.Divider paneDivider = divider.getDivider();
-            divider.setDividerPos(value);
-            double size = getSize();
-            if (size != 0) {
-                // Adjust the position to the center of the
-                // divider and convert its position to a percentage.
-                double pos = value + divider.prefWidth(-1)/2;
-                paneDivider.setPosition(pos / size);
-            } else {
-                paneDivider.setPosition(0);
+                if (c.getArea() >= max) {
+                    // Add the space that needs to be distributed to the others
+                    extraSpace += (c.getArea() - max);
+                    c.setArea(max);
             }
+                c.setAvailable(c.getArea() - min);
+                if (c.getAvailable() < 0) {
+                    spaceRequested += c.getAvailable();
         }
     }
    
-    // Updates the divider with the SplitPane.Divider's position
-    // The value updated to SplitPane.Divider will be the center of the divider.
-    // The returned position will be the left edge of the divider
-    private double getAbsoluteDividerPos(ContentDivider divider) {
-        if (getSkinnable().getWidth() > 0 && getSkinnable().getHeight() > 0 && divider != null) {
-            SplitPane.Divider paneDivider = divider.getDivider();
-            double newPos = posToDividerPos(divider, paneDivider.getPosition());
-            divider.setDividerPos(newPos);
-            return newPos;
-        }
-        return 0;
+            spaceRequested = Math.abs(spaceRequested);
+
+            // Add the panels where we can take space from
+            List<Content> availableList = new ArrayList<Content>();
+            List<Content> storageList = new ArrayList<Content>();
+            List<Content> spaceRequestor = new ArrayList<Content>();
+            double available = 0;
+            for (Content c: contentRegions) {
+                if (c.getAvailable() >= 0) {
+                    available += c.getAvailable();
+                    availableList.add(c);
     }
 
-    // Returns the left edge of the divider at pos
-    // Pos is the percentage location from SplitPane.Divider.
-    private double posToDividerPos(ContentDivider divider, double pos) {
-        double newPos = getSize() * pos;
-        if (pos == 1) {                
-            newPos -= divider.prefWidth(-1);
+                if (resize && !c.isResizableWithParent()) {
+                    // We are making the SplitPane bigger and will need to
+                    // distribute the extra space.
+                    if (c.getArea() >= c.getResizableWithParentArea()) {
+                        extraSpace += (c.getArea() - c.getResizableWithParentArea());
         } else {
-            newPos -= divider.prefWidth(-1)/2;
+                        // We are making the SplitPane smaller and will need to
+                        // distribute the space requested.
+                        spaceRequested += (c.getResizableWithParentArea() - c.getArea());
         }       
-        return Math.round(newPos);                    
+                    c.setAvailable(0);
+                }
+                // Add the panels where we can add space to;
+                if (resize) {
+                    if (c.isResizableWithParent()) {
+                        storageList.add(c);
     }
-    
-    private double totalMinSize() {
-        double dividerWidth = !contentDividers.isEmpty() ? contentDividers.size() * contentDividers.get(0).prefWidth(-1) : 0;
-        double minSize = 0;
-        for (Content c: contentRegions) {
-            if (horizontal) {
-                minSize += c.minWidth(-1);
             } else {
-                minSize += c.minHeight(-1);
+                    storageList.add(c);
             }
+                // List of panels that need space.
+                if (c.getAvailable() < 0) {
+                    spaceRequestor.add(c);
         }
-        return minSize + dividerWidth;
     }
 
-    private double getSize() {
-        final SplitPane s = getSkinnable();
-        double size = totalMinSize();
-        if (horizontal) {
-            if (s.getWidth() > size) {
-                size = s.getWidth() - snappedLeftInset() - snappedRightInset();
-            }
+            if (extraSpace > 0) {
+                extraSpace = distributeTo(storageList, extraSpace);
+                // After distributing add any panels that may still need space to the
+                // spaceRequestor list.
+                spaceRequested = 0;
+                spaceRequestor.clear();
+                available = 0;
+                availableList.clear();
+                for (Content c: contentRegions) {
+                    if (c.getAvailable() < 0) {
+                        spaceRequested += c.getAvailable();
+                        spaceRequestor.add(c);
         } else {
-            if (s.getHeight() > size) {
-                size = s.getHeight() - snappedTopInset() - snappedBottomInset();
+                        available += c.getAvailable();
+                        availableList.add(c);
             }
         }
-        return size;
+                spaceRequested = Math.abs(spaceRequested);
     }
 
-    // Evenly distribute the size to the available list.
-    // size is the amount to distribute.
-    private double distributeTo(List<Content> available, double size) {
-        if (available.isEmpty()) {
-            return size;
+            if (available >= spaceRequested) {
+                for (Content requestor: spaceRequestor) {
+                    double min = horizontal ? requestor.minWidth(-1) : requestor.minHeight(-1);
+                    requestor.setArea(min);
+                    requestor.setAvailable(0);
+                }
+                // After setting all the space requestors to their min we have to
+                // redistribute the space requested to any panel that still
+                // has available space.
+                if (spaceRequested > 0 && !spaceRequestor.isEmpty()) {
+                    distributeFrom(spaceRequested, availableList);
         }
                 
-        size = snapSize(size);
-        int portion = (int)(size)/available.size();
-        int remainder;
-
-        while (size > 0 && !available.isEmpty()) {
-            Iterator<Content> i = available.iterator();
-            while (i.hasNext()) {
-                Content c = i.next();
-                double max = Math.min((horizontal ? c.maxWidth(-1) : c.maxHeight(-1)), Double.MAX_VALUE);
-                double min = horizontal ? c.minWidth(-1) : c.minHeight(-1);
-
-                // We have too much space
-                if (c.getArea() >= max) {
-                    c.setAvailable(c.getArea() - min);
-                    i.remove();
-                    continue;
+                // Only for resizing.  We should have all the panel areas
+                // available computed.  We can total them up and see
+                // how much space we have left or went over and redistribute.
+                if (resize) {
+                    double total = 0;
+                    for (Content c: contentRegions) {
+                        if (c.isResizableWithParent()) {
+                            total += c.getArea();
+                        } else {
+                            total += c.getResizableWithParentArea();
                 }
-                // Not enough space
-                if (portion >= (max - c.getArea())) {
-                    size -= (max - c.getArea());
-                    c.setArea(max);
-                    c.setAvailable(max - min);
-                    i.remove();
+                    }
+                    total += (dividerWidth * contentDividers.size());
+                    if (total < (horizontal ? w : h)) {
+                        extraSpace += ((horizontal ? w : h) - total);
+                        distributeTo(storageList, extraSpace);
                 } else {
-                    // Enough space
-                    c.setArea(c.getArea() + portion);
-                    c.setAvailable(c.getArea() - min);
-                    size -= portion;
+                        spaceRequested += (total - (horizontal ? w : h));
+                        distributeFrom(spaceRequested, storageList);
                 }
-                if ((int)size == 0) {
-                    return size;
                 }
             }
-            if (available.isEmpty()) {
-                // We reached the max size for everything just return
-                return size;
+
+            setupContentAndDividerForLayout();
+
+            // Check the bounds of every panel
+            boolean passed = true;
+            for (Content c: contentRegions) {
+                double max = horizontal ? c.maxWidth(-1) : c.maxHeight(-1);
+                double min = horizontal ? c.minWidth(-1) : c.minHeight(-1);
+                if (c.getArea() < min || c.getArea() > max) {
+                    passed = false;
+                    break;
             }
-            portion = (int)(size)/available.size();
-            remainder = (int)(size)%available.size();
-            if (portion == 0 && remainder != 0) {
-                portion = remainder;
-                remainder = 0;
             }
+            if (passed) {
+                break;
         }
-        return size;
     }
 
-    // Evenly distribute the size from the available list.
-    // size is the amount to distribute.
-    private double distributeFrom(double size, List<Content> available) {
-        if (available.isEmpty()) {
-            return size;
+        layoutDividersAndContent(w, h);
+        resize = false;
         }
         
-        size = snapSize(size);
-        int portion = (int)(size)/available.size();
-        int remainder;
-
-        while (size > 0 && !available.isEmpty()) {
-            Iterator<Content> i = available.iterator();
-            while (i.hasNext()) {
-                Content c = i.next();
-                //not enough space taking available and setting min
-                if (portion >= c.getAvailable()) {
-                    c.setArea(c.getArea() - c.getAvailable()); // Min size
-                    size -= c.getAvailable();
-                    c.setAvailable(0);
-                    i.remove();
-                } else {
-                    //enough space
-                    c.setArea(c.getArea() - portion);
-                    c.setAvailable(c.getAvailable() - portion);
-                    size -= portion;
-                }
-                if ((int)size == 0) {
-                    return size;
-                }
-            }
-            if (available.isEmpty()) {
-                // We reached the min size for everything just return
-                return size;
+    /** {@inheritDoc} */
+    @Override protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
+        double minWidth = 0;
+        double maxMinWidth = 0;
+        for (Content c: contentRegions) {
+            minWidth += c.minWidth(-1);
+            maxMinWidth = Math.max(maxMinWidth, c.minWidth(-1));
             }
-            portion = (int)(size)/available.size();
-            remainder = (int)(size)%available.size();
-            if (portion == 0 && remainder != 0) {
-                portion = remainder;
-                remainder = 0;
+        for (ContentDivider d: contentDividers) {
+            minWidth += d.prefWidth(-1);
             }
+        if (horizontal) {
+            return minWidth + leftInset + rightInset;
+        } else {
+            return maxMinWidth + leftInset + rightInset;
         }
-        return size;
     }
 
-    private void setupContentAndDividerForLayout() {
-        // Set all the value to prepare for layout
-        double dividerWidth = contentDividers.isEmpty() ? 0 : contentDividers.get(0).prefWidth(-1);
-        double startX = 0;
-        double startY = 0;
+    /** {@inheritDoc} */
+    @Override protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
+        double minHeight = 0;
+        double maxMinHeight = 0;
         for (Content c: contentRegions) {
-            if (resize && !c.isResizableWithParent()) {
-                c.setArea(c.getResizableWithParentArea());
+            minHeight += c.minHeight(-1);
+            maxMinHeight = Math.max(maxMinHeight, c.minHeight(-1));
+        }
+        for (ContentDivider d: contentDividers) {
+            minHeight += d.prefWidth(-1);
             }
-            
-            c.setX(startX);
-            c.setY(startY);
             if (horizontal) {
-                startX += (c.getArea() + dividerWidth);
+            return maxMinHeight + topInset + bottomInset;
             } else {
-                startY += (c.getArea() + dividerWidth);
+            return minHeight + topInset + bottomInset;
             }
         }
 
-        startX = 0;
-        startY = 0;
-        // The dividers are already in the correct positions.  Disable
-        // checking the divider positions.
-        checkDividerPos = false;
-        for (int i = 0; i < contentDividers.size(); i++) {
-            ContentDivider d = contentDividers.get(i);
+    /** {@inheritDoc} */
+    @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
+        double prefWidth = 0;
+        double prefMaxWidth = 0;
+        for (Content c: contentRegions) {
+            prefWidth += c.prefWidth(-1);
+            prefMaxWidth = Math.max(prefMaxWidth, c.prefWidth(-1));
+        }
+        for (ContentDivider d: contentDividers) {
+            prefWidth += d.prefWidth(-1);
+        }
             if (horizontal) {
-                startX += getLeft(d).getArea() + (i == 0 ? 0 : dividerWidth);
+            return prefWidth + leftInset + rightInset;
             } else {
-                startY += getLeft(d).getArea() + (i == 0 ? 0 : dividerWidth);
-            }
-            d.setX(startX);
-            d.setY(startY);              
-            setAbsoluteDividerPos(d, (horizontal ? d.getX() : d.getY()));
-            d.posExplicit = false;
+            return prefMaxWidth + leftInset + rightInset;
         }
-        checkDividerPos = true;
     }
 
-    private void layoutDividersAndContent(double width, double height) {
-        final double paddingX = snappedLeftInset();
-        final double paddingY = snappedTopInset();
-        final double dividerWidth = contentDividers.isEmpty() ? 0 : contentDividers.get(0).prefWidth(-1);
-
+    /** {@inheritDoc} */
+    @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
+        double prefHeight = 0;
+        double maxPrefHeight = 0;
         for (Content c: contentRegions) {
-//            System.out.println("LAYOUT " + c.getId() + " PANELS X " + c.getX() + " Y " + c.getY() + " W " + (horizontal ? c.getArea() : width) + " H " + (horizontal ? height : c.getArea()));
-            if (horizontal) {
-                c.setClipSize(c.getArea(), height);
-                layoutInArea(c, c.getX() + paddingX, c.getY() + paddingY, c.getArea(), height,
-                    0/*baseline*/,HPos.CENTER, VPos.CENTER);
-            } else {
-                c.setClipSize(width, c.getArea());
-                layoutInArea(c, c.getX() + paddingX, c.getY() + paddingY, width, c.getArea(),
-                    0/*baseline*/,HPos.CENTER, VPos.CENTER);
+            prefHeight += c.prefHeight(-1);
+            maxPrefHeight = Math.max(maxPrefHeight, c.prefHeight(-1));
             }
+        for (ContentDivider d: contentDividers) {
+            prefHeight += d.prefWidth(-1);
         }
-        for (ContentDivider c: contentDividers) {
-//            System.out.println("LAYOUT DIVIDERS X " + c.getX() + " Y " + c.getY() + " W " + (horizontal ? dividerWidth : width) + " H " + (horizontal ? height : dividerWidth));
             if (horizontal) {
-                c.resize(dividerWidth, height);
-                positionInArea(c, c.getX() + paddingX, c.getY() + paddingY, dividerWidth, height,
-                    /*baseline ignored*/0, HPos.CENTER, VPos.CENTER);
+            return maxPrefHeight + topInset + bottomInset;
             } else {
-                c.resize(width, dividerWidth);                
-                positionInArea(c, c.getX() + paddingX, c.getY() + paddingY, width, dividerWidth,
-                    /*baseline ignored*/0, HPos.CENTER, VPos.CENTER);
-            }
+            return prefHeight + topInset + bottomInset;
         }
     }
 
-    private double previousSize = -1;
-    private int lastDividerUpdate = 0;
-    private boolean resize = false;
-    private boolean checkDividerPos = true;
 
-    @Override protected void layoutChildren(final double x, final double y,
-            final double w, final double h) {
-        final SplitPane s = getSkinnable();
-        final double sw = s.getWidth();
-        final double sh = s.getHeight();
         
-        if (!s.isVisible() || 
-            (horizontal ? sw == 0 : sh == 0) ||
-            contentRegions.isEmpty()) {
-            return;
-        }
+    /***************************************************************************
+     *                                                                         *
+     * Private implementation                                                  *
+     *                                                                         *
+     **************************************************************************/
         
-        double dividerWidth = contentDividers.isEmpty() ? 0 : contentDividers.get(0).prefWidth(-1);
+    private void addContent(int index, Node n) {
+        Content c = new Content(n);
+        contentRegions.add(index, c);
+        getChildren().add(index, c);
+    }
 
-        if (contentDividers.size() > 0 && previousSize != -1 && previousSize != (horizontal ? sw  : sh)) {
-            //This algorithm adds/subtracts a little to each panel on every resize
-            List<Content> resizeList = new ArrayList<Content>();
+    private void removeContent(Node n) {
             for (Content c: contentRegions) {
-                if (c.isResizableWithParent()) {
-                    resizeList.add(c);
+            if (c.getContent().equals(n)) {
+                getChildren().remove(c);
+                contentRegions.remove(c);
+                break;
+            }
                 }
             }
                         
-            double delta = (horizontal ? s.getWidth() : s.getHeight()) - previousSize;
-            boolean growing = delta > 0;
-            
-            delta = Math.abs(delta);            
+    private void initializeContentListener() {
+        getSkinnable().getItems().addListener((ListChangeListener<Node>) c -> {
+            while (c.next()) {
+                if (c.wasPermutated() || c.wasUpdated()) {
+                    /**
+                     * the contents were either moved, or updated.
+                     * rebuild the contents to re-sync
+                     */
+                    getChildren().clear();
+                    contentRegions.clear();
+                    int index = 0;
+                    for (Node n : c.getList()) {
+                        addContent(index++, n);
+                    }
 
-            if (delta != 0 && !resizeList.isEmpty()) {
-                int portion = (int)(delta)/resizeList.size();
-                int remainder = (int)delta%resizeList.size();
-                int size = 0;
-                if (portion == 0) {
-                    portion = remainder;
-                    size = remainder;
-                    remainder = 0;
                 } else {
-                    size = portion * resizeList.size();
+                    for (Node n : c.getRemoved()) {
+                        removeContent(n);
                 }
 
-                while (size > 0 && !resizeList.isEmpty()) {
-                    if (growing) {
-                        lastDividerUpdate++;
-                    } else {
-                        lastDividerUpdate--;
-                        if (lastDividerUpdate < 0) {
-                            lastDividerUpdate = contentRegions.size() - 1;
+                    int index = c.getFrom();
+                    for (Node n : c.getAddedSubList()) {
+                        addContent(index++, n);
                         }
                     }
-                    int id = lastDividerUpdate%contentRegions.size();
-                    Content content = contentRegions.get(id);
-                    if (content.isResizableWithParent() && resizeList.contains(content)) {
-                        double area = content.getArea();
-                        if (growing) {
-                            double max = horizontal ? content.maxWidth(-1) : content.maxHeight(-1);
-                            if ((area + portion) <= max) {
-                                area += portion;
-                            } else {
-                                resizeList.remove(content);
-                                continue;
                             }
-                        } else {
-                            double min = horizontal ? content.minWidth(-1) : content.minHeight(-1);
-                            if ((area - portion) >= min) {
-                                area -= portion;
-                            } else {
-                                resizeList.remove(content);
-                                continue;
+            // TODO there may be a more efficient way than rebuilding all the dividers
+            // everytime the list changes.
+            removeAllDividers();
+            for (SplitPane.Divider d: getSkinnable().getDividers()) {
+                addDivider(d);
                             }
+        });
+    }
+          
+    private void checkDividerPosition(ContentDivider divider, double newPos, double oldPos) {
+        double dividerWidth = divider.prefWidth(-1);
+        Content left = getLeft(divider);
+        Content right = getRight(divider);
+        double minLeft = left == null ? 0 : (horizontal) ? left.minWidth(-1) : left.minHeight(-1);
+        double minRight = right == null ? 0 : (horizontal) ? right.minWidth(-1) : right.minHeight(-1);
+        double maxLeft = left == null ? 0 :
+            left.getContent() != null ? (horizontal) ? left.getContent().maxWidth(-1) : left.getContent().maxHeight(-1) : 0;
+        double maxRight = right == null ? 0 :
+            right.getContent() != null ? (horizontal) ? right.getContent().maxWidth(-1) : right.getContent().maxHeight(-1) : 0;                        
+               
+        double previousDividerPos = 0;
+        double nextDividerPos = getSize();
+        int index = contentDividers.indexOf(divider);
+
+        if (index - 1 >= 0) {
+            previousDividerPos = contentDividers.get(index - 1).getDividerPos();
+            if (previousDividerPos == -1) {
+                // Get the divider position if it hasn't been initialized.
+                previousDividerPos = getAbsoluteDividerPos(contentDividers.get(index - 1));
                         }
-                        content.setArea(area);
-                        size -= portion;
-                        if (size == 0 && remainder != 0) {
-                            portion = remainder;
-                            size = remainder;
-                            remainder = 0;
-                        } else if (size == 0) {
-                            break;
                         }
+        if (index + 1 < contentDividers.size()) {
+            nextDividerPos = contentDividers.get(index + 1).getDividerPos();
+            if (nextDividerPos == -1) {
+                // Get the divider position if it hasn't been initialized.
+                nextDividerPos = getAbsoluteDividerPos(contentDividers.get(index + 1));
                     }
                 }
 
-                // If we are resizing the window save the current area into
-                // resizableWithParentArea.  We use this value during layout.
-                {
-                    for (Content c: contentRegions) {
-                        c.setResizableWithParentArea(c.getArea());
-                        c.setAvailable(0);
+        // Set the divider into the correct position by looking at the max and min content sizes.
+        checkDividerPos = false;
+        if (newPos > oldPos) {
+            double max = previousDividerPos == 0 ? maxLeft : previousDividerPos + dividerWidth + maxLeft;
+            double min = nextDividerPos - minRight - dividerWidth;                
+            double stopPos = Math.min(max, min);
+            if (newPos >= stopPos) {                
+                setAbsoluteDividerPos(divider, stopPos);
+            } else {
+                double rightMax = nextDividerPos - maxRight - dividerWidth;                    
+                if (newPos <= rightMax) {
+                    setAbsoluteDividerPos(divider, rightMax);
+                } else {                    
+                    setAbsoluteDividerPos(divider, newPos);
                     }
                 }
-                resize = true;
+        } else {
+            double max = nextDividerPos - maxRight - dividerWidth;
+            double min = previousDividerPos == 0 ? minLeft : previousDividerPos + minLeft + dividerWidth;
+            double stopPos = Math.max(max, min);
+            if (newPos <= stopPos) {
+                setAbsoluteDividerPos(divider, stopPos);
+            } else {
+                double leftMax = previousDividerPos + maxLeft + dividerWidth;
+                if (newPos >= leftMax) {
+                    setAbsoluteDividerPos(divider, leftMax);
+                } else {
+                    setAbsoluteDividerPos(divider, newPos);
+                }
+            }                
+        }                    
+        checkDividerPos = true;
             }
 
-            previousSize = horizontal ? sw : sh;
-        } else {
-            previousSize = horizontal ? sw : sh;
+    private void addDivider(SplitPane.Divider d) {
+        ContentDivider c = new ContentDivider(d);        
+        c.setInitialPos(d.getPosition());
+        c.setDividerPos(-1);
+        ChangeListener<Number> posPropertyListener = new PosPropertyListener(c);
+        c.setPosPropertyListener(posPropertyListener);
+        d.positionProperty().addListener(posPropertyListener);
+        initializeDivderEventHandlers(c);
+        contentDividers.add(c);
+        getChildren().add(c);
         }
         
-        // If the window is less than the min size we want to resize
-        // proportionally
-        double minSize = totalMinSize();
-        if (minSize > (horizontal ? w : h)) {
-            double percentage = 0;
-            for (int i = 0; i < contentRegions.size(); i++) {
-                Content c = contentRegions.get(i);
-                double min = horizontal ? c.minWidth(-1) : c.minHeight(-1);
-                percentage = min/minSize;
-                c.setArea(snapSpace(percentage * (horizontal ? w : h)));
-                c.setAvailable(0);
+    private void removeAllDividers() {
+        ListIterator<ContentDivider> dividers = contentDividers.listIterator();
+        while (dividers.hasNext()) {
+            ContentDivider c = dividers.next();
+            getChildren().remove(c);
+            c.getDivider().positionProperty().removeListener(c.getPosPropertyListener());
+            dividers.remove();
             }
-            setupContentAndDividerForLayout();
-            layoutDividersAndContent(w, h);
-            resize = false;
-            return;
+        lastDividerUpdate = 0;
         }
 
-        for(int trys = 0; trys < 10; trys++) {
-            // Compute the area in between each divider.            
-            ContentDivider previousDivider = null;
-            ContentDivider divider = null;            
-            for (int i = 0; i < contentRegions.size(); i++) {
-                double space = 0;
-                if (i < contentDividers.size()) {
-                    divider = contentDividers.get(i);
-                    if (divider.posExplicit) {
-                        checkDividerPosition(divider, posToDividerPos(divider, divider.d.getPosition()),
-                                divider.getDividerPos());
-                    }
-                    if (i == 0) {
-                        // First panel
-                        space = getAbsoluteDividerPos(divider);
+    private void initializeDivderEventHandlers(final ContentDivider divider) {
+        // TODO: do we need to consume all mouse events?
+        // they only bubble to the skin which consumes them by default
+        divider.addEventHandler(MouseEvent.ANY, event -> {
+            event.consume();
+        });
+
+        divider.setOnMousePressed(e -> {
+            if (horizontal) {
+                divider.setInitialPos(divider.getDividerPos());
+                divider.setPressPos(e.getSceneX());
+                divider.setPressPos(getSkinnable().getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT
+                        ? getSkinnable().getWidth() - e.getSceneX() : e.getSceneX());
                     } else {
-                        double newPos = getAbsoluteDividerPos(previousDivider) + dividerWidth;
-                        // Middle panels
-                        if (getAbsoluteDividerPos(divider) <= getAbsoluteDividerPos(previousDivider)) {
-                            // The current divider and the previous divider share the same position
-                            // or the current divider position is less than the previous position.
-                            // We will set the divider next to the previous divider.
-                            setAndCheckAbsoluteDividerPos(divider, newPos);
+                divider.setInitialPos(divider.getDividerPos());
+                divider.setPressPos(e.getSceneY());
                         }
-                        space = getAbsoluteDividerPos(divider) - newPos;
+            e.consume();
+        });
+
+        divider.setOnMouseDragged(e -> {
+            double delta = 0;
+            if (horizontal) {
+                delta = getSkinnable().getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT
+                        ? getSkinnable().getWidth() - e.getSceneX() : e.getSceneX();
+            } else {
+                delta = e.getSceneY();
                     }
-                } else if (i == contentDividers.size()) {
-                    // Last panel
-                    space = (horizontal ? w : h) - (previousDivider != null ? getAbsoluteDividerPos(previousDivider) + dividerWidth : 0);
+            delta -= divider.getPressPos();
+            setAndCheckAbsoluteDividerPos(divider, Math.ceil(divider.getInitialPos() + delta));
+            e.consume();
+        });
                 }
-                if (!resize || divider.posExplicit) {
-                    contentRegions.get(i).setArea(space);
+
+    private Content getLeft(ContentDivider d) {
+        int index = contentDividers.indexOf(d);
+        if (index != -1) {
+            return contentRegions.get(index);
                 }
-                previousDivider = divider;
+        return null;
             }
 
-            // Compute the amount of space we have available.
-            // Available is amount of space we can take from a panel before we reach its min.
-            // If available is negative we don't have enough space and we will
-            // proportionally take the space from the other availables.  If we have extra space
-            // we will porportionally give it to the others
-            double spaceRequested = 0;
-            double extraSpace = 0;
-            for (Content c: contentRegions) {
-                double max = 0;
-                double min = 0;
-                if (c != null) {
-                    max = horizontal ? c.maxWidth(-1) : c.maxHeight(-1);
-                    min = horizontal ? c.minWidth(-1) : c.minHeight(-1);
+    private Content getRight(ContentDivider d) {
+        int index = contentDividers.indexOf(d);
+        if (index != -1) {
+            return contentRegions.get(index + 1);
+        }
+        return null;
                 }
 
-                if (c.getArea() >= max) {
-                    // Add the space that needs to be distributed to the others
-                    extraSpace += (c.getArea() - max);
-                    c.setArea(max);
+    // Value is the left edge of the divider
+    private void setAbsoluteDividerPos(ContentDivider divider, double value) {
+        if (getSkinnable().getWidth() > 0 && getSkinnable().getHeight() > 0 && divider != null) {
+            SplitPane.Divider paneDivider = divider.getDivider();
+            divider.setDividerPos(value);
+            double size = getSize();
+            if (size != 0) {
+                // Adjust the position to the center of the
+                // divider and convert its position to a percentage.
+                double pos = value + divider.prefWidth(-1)/2;
+                paneDivider.setPosition(pos / size);
+            } else {
+                paneDivider.setPosition(0);
                 }
-                c.setAvailable(c.getArea() - min);
-                if (c.getAvailable() < 0) {
-                    spaceRequested += c.getAvailable();
                 }
             }
 
-            spaceRequested = Math.abs(spaceRequested);
-
-            // Add the panels where we can take space from
-            List<Content> availableList = new ArrayList<Content>();
-            List<Content> storageList = new ArrayList<Content>();
-            List<Content> spaceRequestor = new ArrayList<Content>();
-            double available = 0;
-            for (Content c: contentRegions) {
-                if (c.getAvailable() >= 0) {
-                    available += c.getAvailable();
-                    availableList.add(c);
+    // Updates the divider with the SplitPane.Divider's position
+    // The value updated to SplitPane.Divider will be the center of the divider.
+    // The returned position will be the left edge of the divider
+    private double getAbsoluteDividerPos(ContentDivider divider) {
+        if (getSkinnable().getWidth() > 0 && getSkinnable().getHeight() > 0 && divider != null) {
+            SplitPane.Divider paneDivider = divider.getDivider();
+            double newPos = posToDividerPos(divider, paneDivider.getPosition());
+            divider.setDividerPos(newPos);
+            return newPos;
+        }
+        return 0;
                 }
 
-                if (resize && !c.isResizableWithParent()) {
-                    // We are making the SplitPane bigger and will need to
-                    // distribute the extra space.
-                    if (c.getArea() >= c.getResizableWithParentArea()) {                        
-                        extraSpace += (c.getArea() - c.getResizableWithParentArea());
+    // Returns the left edge of the divider at pos
+    // Pos is the percentage location from SplitPane.Divider.
+    private double posToDividerPos(ContentDivider divider, double pos) {
+        double newPos = getSize() * pos;
+        if (pos == 1) {                
+            newPos -= divider.prefWidth(-1);
                     } else {
-                        // We are making the SplitPane smaller and will need to
-                        // distribute the space requested.
-                        spaceRequested += (c.getResizableWithParentArea() - c.getArea());
-                    }
-                    c.setAvailable(0);
+            newPos -= divider.prefWidth(-1)/2;
                 }
-                // Add the panels where we can add space to;
-                if (resize) {
-                    if (c.isResizableWithParent()) {
-                        storageList.add(c);
+        return Math.round(newPos);                    
                     }
+    
+    private double totalMinSize() {
+        double dividerWidth = !contentDividers.isEmpty() ? contentDividers.size() * contentDividers.get(0).prefWidth(-1) : 0;
+        double minSize = 0;
+        for (Content c: contentRegions) {
+            if (horizontal) {
+                minSize += c.minWidth(-1);
                 } else {
-                    storageList.add(c);
+                minSize += c.minHeight(-1);
                 }
-                // List of panels that need space.
-                if (c.getAvailable() < 0) {
-                    spaceRequestor.add(c);
                 }
+        return minSize + dividerWidth;
             }
 
-            if (extraSpace > 0) {
-                extraSpace = distributeTo(storageList, extraSpace);
-                // After distributing add any panels that may still need space to the
-                // spaceRequestor list.
-                spaceRequested = 0;
-                spaceRequestor.clear();
-                available = 0;
-                availableList.clear();
-                for (Content c: contentRegions) {
-                    if (c.getAvailable() < 0) {
-                        spaceRequested += c.getAvailable();
-                        spaceRequestor.add(c);
+    private double getSize() {
+        final SplitPane s = getSkinnable();
+        double size = totalMinSize();
+        if (horizontal) {
+            if (s.getWidth() > size) {
+                size = s.getWidth() - snappedLeftInset() - snappedRightInset();
+            }
                     } else {
-                        available += c.getAvailable();
-                        availableList.add(c);
+            if (s.getHeight() > size) {
+                size = s.getHeight() - snappedTopInset() - snappedBottomInset();
                     }
                 }
-                spaceRequested = Math.abs(spaceRequested);
+        return size;
             }
 
-            if (available >= spaceRequested) {
-                for (Content requestor: spaceRequestor) {
-                    double min = horizontal ? requestor.minWidth(-1) : requestor.minHeight(-1);
-                    requestor.setArea(min);
-                    requestor.setAvailable(0);
-                }
-                // After setting all the space requestors to their min we have to
-                // redistribute the space requested to any panel that still
-                // has available space.
-                if (spaceRequested > 0 && !spaceRequestor.isEmpty()) {
-                    distributeFrom(spaceRequested, availableList);
+    // Evenly distribute the size to the available list.
+    // size is the amount to distribute.
+    private double distributeTo(List<Content> available, double size) {
+        if (available.isEmpty()) {
+            return size;
                 }
 
-                // Only for resizing.  We should have all the panel areas
-                // available computed.  We can total them up and see
-                // how much space we have left or went over and redistribute.
-                if (resize) {
-                    double total = 0;
-                    for (Content c: contentRegions) {
-                        if (c.isResizableWithParent()) {
-                            total += c.getArea();
+        size = snapSize(size);
+        int portion = (int)(size)/available.size();
+        int remainder;
+
+        while (size > 0 && !available.isEmpty()) {
+            Iterator<Content> i = available.iterator();
+            while (i.hasNext()) {
+                Content c = i.next();
+                double max = Math.min((horizontal ? c.maxWidth(-1) : c.maxHeight(-1)), Double.MAX_VALUE);
+                double min = horizontal ? c.minWidth(-1) : c.minHeight(-1);
+
+                // We have too much space
+                if (c.getArea() >= max) {
+                    c.setAvailable(c.getArea() - min);
+                    i.remove();
+                    continue;
+                }
+                // Not enough space
+                if (portion >= (max - c.getArea())) {
+                    size -= (max - c.getArea());
+                    c.setArea(max);
+                    c.setAvailable(max - min);
+                    i.remove();
                         } else {
-                            total += c.getResizableWithParentArea();
+                    // Enough space
+                    c.setArea(c.getArea() + portion);
+                    c.setAvailable(c.getArea() - min);
+                    size -= portion;
                         }
+                if ((int)size == 0) {
+                    return size;
                     }
-                    total += (dividerWidth * contentDividers.size());
-                    if (total < (horizontal ? w : h)) {
-                        extraSpace += ((horizontal ? w : h) - total);
-                        distributeTo(storageList, extraSpace);
-                    } else {
-                        spaceRequested += (total - (horizontal ? w : h));
-                        distributeFrom(spaceRequested, storageList);
+            }
+            if (available.isEmpty()) {
+                // We reached the max size for everything just return
+                return size;
+            }
+            portion = (int)(size)/available.size();
+            remainder = (int)(size)%available.size();
+            if (portion == 0 && remainder != 0) {
+                portion = remainder;
+                remainder = 0;
                     }
                 }
+        return size;
             }
 
-            setupContentAndDividerForLayout();
+    // Evenly distribute the size from the available list.
+    // size is the amount to distribute.
+    private double distributeFrom(double size, List<Content> available) {
+        if (available.isEmpty()) {
+            return size;
+        }
 
-            // Check the bounds of every panel
-            boolean passed = true;
-            for (Content c: contentRegions) {
-                double max = horizontal ? c.maxWidth(-1) : c.maxHeight(-1);
-                double min = horizontal ? c.minWidth(-1) : c.minHeight(-1);
-                if (c.getArea() < min || c.getArea() > max) {
-                    passed = false;
-                    break;
+        size = snapSize(size);
+        int portion = (int)(size)/available.size();
+        int remainder;
+
+        while (size > 0 && !available.isEmpty()) {
+            Iterator<Content> i = available.iterator();
+            while (i.hasNext()) {
+                Content c = i.next();
+                //not enough space taking available and setting min
+                if (portion >= c.getAvailable()) {
+                    c.setArea(c.getArea() - c.getAvailable()); // Min size
+                    size -= c.getAvailable();
+                    c.setAvailable(0);
+                    i.remove();
+                } else {
+                    //enough space
+                    c.setArea(c.getArea() - portion);
+                    c.setAvailable(c.getAvailable() - portion);
+                    size -= portion;
                 }
+                if ((int)size == 0) {
+                    return size;
             }
-            if (passed) {
-                break;
             }
+            if (available.isEmpty()) {
+                // We reached the min size for everything just return
+                return size;
         }
-
-        layoutDividersAndContent(w, h);        
-        resize = false;        
+            portion = (int)(size)/available.size();
+            remainder = (int)(size)%available.size();
+            if (portion == 0 && remainder != 0) {
+                portion = remainder;
+                remainder = 0;
     }
-
-    private void setAndCheckAbsoluteDividerPos(ContentDivider divider, double value) {
-        double oldPos = divider.getDividerPos();
-        setAbsoluteDividerPos(divider, value);
-        checkDividerPosition(divider, value, oldPos);
+        }
+        return size;
     }
 
-    @Override protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
-        double minWidth = 0;
-        double maxMinWidth = 0;
+    private void setupContentAndDividerForLayout() {
+        // Set all the value to prepare for layout
+        double dividerWidth = contentDividers.isEmpty() ? 0 : contentDividers.get(0).prefWidth(-1);
+        double startX = 0;
+        double startY = 0;
         for (Content c: contentRegions) {
-            minWidth += c.minWidth(-1);
-            maxMinWidth = Math.max(maxMinWidth, c.minWidth(-1));
-        }
-        for (ContentDivider d: contentDividers) {
-            minWidth += d.prefWidth(-1);
+            if (resize && !c.isResizableWithParent()) {
+                c.setArea(c.getResizableWithParentArea());
         }
+            
+            c.setX(startX);
+            c.setY(startY);
         if (horizontal) {
-            return minWidth + leftInset + rightInset;
+                startX += (c.getArea() + dividerWidth);
         } else {
-            return maxMinWidth + leftInset + rightInset;
+                startY += (c.getArea() + dividerWidth);
         }
     }
 
-    @Override protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
-        double minHeight = 0;
-        double maxMinHeight = 0;
-        for (Content c: contentRegions) {
-            minHeight += c.minHeight(-1);
-            maxMinHeight = Math.max(maxMinHeight, c.minHeight(-1));
-        }
-        for (ContentDivider d: contentDividers) {
-            minHeight += d.prefWidth(-1);
-        }
+        startX = 0;
+        startY = 0;
+        // The dividers are already in the correct positions.  Disable
+        // checking the divider positions.
+        checkDividerPos = false;
+        for (int i = 0; i < contentDividers.size(); i++) {
+            ContentDivider d = contentDividers.get(i);
         if (horizontal) {
-            return maxMinHeight + topInset + bottomInset;
+                startX += getLeft(d).getArea() + (i == 0 ? 0 : dividerWidth);
         } else {
-            return minHeight + topInset + bottomInset;
+                startY += getLeft(d).getArea() + (i == 0 ? 0 : dividerWidth);
+            }
+            d.setX(startX);
+            d.setY(startY);              
+            setAbsoluteDividerPos(d, (horizontal ? d.getX() : d.getY()));
+            d.posExplicit = false;
         }
+        checkDividerPos = true;
     }
 
-    @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
-        double prefWidth = 0;
-        double prefMaxWidth = 0;
+    private void layoutDividersAndContent(double width, double height) {
+        final double paddingX = snappedLeftInset();
+        final double paddingY = snappedTopInset();
+        final double dividerWidth = contentDividers.isEmpty() ? 0 : contentDividers.get(0).prefWidth(-1);
+
         for (Content c: contentRegions) {
-            prefWidth += c.prefWidth(-1);
-            prefMaxWidth = Math.max(prefMaxWidth, c.prefWidth(-1));
+//            System.out.println("LAYOUT " + c.getId() + " PANELS X " + c.getX() + " Y " + c.getY() + " W " + (horizontal ? c.getArea() : width) + " H " + (horizontal ? height : c.getArea()));
+            if (horizontal) {
+                c.setClipSize(c.getArea(), height);
+                layoutInArea(c, c.getX() + paddingX, c.getY() + paddingY, c.getArea(), height,
+                    0/*baseline*/,HPos.CENTER, VPos.CENTER);
+            } else {
+                c.setClipSize(width, c.getArea());
+                layoutInArea(c, c.getX() + paddingX, c.getY() + paddingY, width, c.getArea(),
+                    0/*baseline*/,HPos.CENTER, VPos.CENTER);
         }
-        for (ContentDivider d: contentDividers) {
-            prefWidth += d.prefWidth(-1);
         }
+        for (ContentDivider c: contentDividers) {
+//            System.out.println("LAYOUT DIVIDERS X " + c.getX() + " Y " + c.getY() + " W " + (horizontal ? dividerWidth : width) + " H " + (horizontal ? height : dividerWidth));
         if (horizontal) {
-            return prefWidth + leftInset + rightInset;
+                c.resize(dividerWidth, height);
+                positionInArea(c, c.getX() + paddingX, c.getY() + paddingY, dividerWidth, height,
+                    /*baseline ignored*/0, HPos.CENTER, VPos.CENTER);
         } else {
-            return prefMaxWidth + leftInset + rightInset;
+                c.resize(width, dividerWidth);                
+                positionInArea(c, c.getX() + paddingX, c.getY() + paddingY, width, dividerWidth,
+                    /*baseline ignored*/0, HPos.CENTER, VPos.CENTER);
+            }
         }
     }
 
-    @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
-        double prefHeight = 0;
-        double maxPrefHeight = 0;
-        for (Content c: contentRegions) {
-            prefHeight += c.prefHeight(-1);
-            maxPrefHeight = Math.max(maxPrefHeight, c.prefHeight(-1));
+    private double previousSize = -1;
+    private int lastDividerUpdate = 0;
+    private boolean resize = false;
+    private boolean checkDividerPos = true;
+
+    private void setAndCheckAbsoluteDividerPos(ContentDivider divider, double value) {
+        double oldPos = divider.getDividerPos();
+        setAbsoluteDividerPos(divider, value);
+        checkDividerPosition(divider, value, oldPos);
         }
-        for (ContentDivider d: contentDividers) {
-            prefHeight += d.prefWidth(-1);
+
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Support classes                                                         *
+     *                                                                         *
+     **************************************************************************/
+
+    // This listener is to be removed from 'removed' dividers and added to 'added' dividers
+    class PosPropertyListener implements ChangeListener<Number> {
+        ContentDivider divider;
+
+        public PosPropertyListener(ContentDivider divider) {
+            this.divider = divider;
         }
-        if (horizontal) {
-            return maxPrefHeight + topInset + bottomInset;
-        } else {
-            return prefHeight + topInset + bottomInset;
+
+        @Override public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
+            if (checkDividerPos) {
+                // When checking is enforced, we know that the position was set explicitly
+                divider.posExplicit = true;
+            }
+            getSkinnable().requestLayout();
         }
     }
     
 
-//    private void printDividerPositions() {
-//        for (int i = 0; i < contentDividers.size(); i++) {
-//            System.out.print("DIVIDER[" + i + "] " + contentDividers.get(i).getDividerPos() + " ");
-//        } 
-//        System.out.println("");
-//    }
-//    
-//    private void printAreaAndAvailable() {
-//        for (int i = 0; i < contentRegions.size(); i++) {
-//            System.out.print("AREA[" + i + "] " + contentRegions.get(i).getArea() + " ");
-//        }
-//        System.out.println("");
-//        for (int i = 0; i < contentRegions.size(); i++) {
-//            System.out.print("AVAILABLE[" + i + "] " + contentRegions.get(i).getAvailable() + " ");
-//        }
-//        System.out.println("");
-//        for (int i = 0; i < contentRegions.size(); i++) {
-//            System.out.print("RESIZABLEWTIHPARENT[" + i + "] " + contentRegions.get(i).getResizableWithParentArea() + " ");
-//        }
-//        System.out.println("");
-//    }
-
     class ContentDivider extends StackPane {
         private double initialPos;
         private double dividerPos;
         private double pressPos;
         private SplitPane.Divider d;