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;