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 /*
   2  * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.javafx.scene.control.skin;
  27 
  28 import javafx.beans.value.ChangeListener;
  29 import javafx.beans.value.ObservableValue;
  30 import javafx.collections.FXCollections;
  31 import javafx.collections.ListChangeListener;
  32 import javafx.collections.ObservableList;
  33 import javafx.geometry.HPos;
  34 import javafx.geometry.NodeOrientation;
  35 import javafx.geometry.Orientation;
  36 import javafx.geometry.VPos;
  37 import javafx.scene.Cursor;
  38 import javafx.scene.Node;



  39 import javafx.scene.control.SplitPane;
  40 import javafx.scene.input.MouseEvent;
  41 import javafx.scene.layout.StackPane;
  42 import javafx.scene.shape.Rectangle;
  43 import java.util.ArrayList;
  44 import java.util.Collections;
  45 import java.util.Iterator;
  46 import java.util.List;
  47 import java.util.ListIterator;
  48 import com.sun.javafx.scene.control.behavior.BehaviorBase;
  49 
  50 public class SplitPaneSkin extends BehaviorSkinBase<SplitPane, BehaviorBase<SplitPane>>  {












  51 
  52     private ObservableList<Content> contentRegions;
  53     private ObservableList<ContentDivider> contentDividers;
  54     private boolean horizontal;
  55     
  56     public SplitPaneSkin(final SplitPane splitPane) {
  57         super(splitPane, new BehaviorBase<>(splitPane, Collections.emptyList()));
  58 //        splitPane.setManaged(false);
















  59         horizontal = getSkinnable().getOrientation() == Orientation.HORIZONTAL;
  60         
  61         contentRegions = FXCollections.<Content>observableArrayList();
  62         contentDividers = FXCollections.<ContentDivider>observableArrayList();
  63 
  64         int index = 0;
  65         for (Node n: getSkinnable().getItems()) {
  66             addContent(index++, n);
  67         }
  68         initializeContentListener();
  69 
  70         for (SplitPane.Divider d: getSkinnable().getDividers()) {
  71             addDivider(d);
  72         }
  73 
  74         registerChangeListener(splitPane.orientationProperty(), "ORIENTATION");
  75         registerChangeListener(splitPane.widthProperty(), "WIDTH");
  76         registerChangeListener(splitPane.heightProperty(), "HEIGHT");
  77     }
  78 
  79     private void addContent(int index, Node n) {
  80         Content c = new Content(n);
  81         contentRegions.add(index, c);
  82         getChildren().add(index, c);
  83     }
  84 
  85     private void removeContent(Node n) {
  86         for (Content c: contentRegions) {
  87             if (c.getContent().equals(n)) {
  88                 getChildren().remove(c);
  89                 contentRegions.remove(c);
  90                 break;
  91             }
  92         }




  93     }
  94 
  95     private void initializeContentListener() {
  96         getSkinnable().getItems().addListener((ListChangeListener<Node>) c -> {
  97             while (c.next()) {
  98                 if (c.wasPermutated() || c.wasUpdated()) {
  99                     /**
 100                      * the contents were either moved, or updated.
 101                      * rebuild the contents to re-sync
 102                      */
 103                     getChildren().clear();
 104                     contentRegions.clear();
 105                     int index = 0;
 106                     for (Node n : c.getList()) {
 107                         addContent(index++, n);
 108                     }
 109 
 110                 } else {
 111                     for (Node n : c.getRemoved()) {
 112                         removeContent(n);
 113                     }
 114 
 115                     int index = c.getFrom();
 116                     for (Node n : c.getAddedSubList()) {
 117                         addContent(index++, n);
 118                     }
 119                 }
 120             }
 121             // TODO there may be a more efficient way than rebuilding all the dividers
 122             // everytime the list changes.
 123             removeAllDividers();
 124             for (SplitPane.Divider d: getSkinnable().getDividers()) {
 125                 addDivider(d);
 126             }
 127         });
 128     }
 129           
 130     // This listener is to be removed from 'removed' dividers and added to 'added' dividers
 131     class PosPropertyListener implements ChangeListener<Number> {
 132         ContentDivider divider;



 133         
 134         public PosPropertyListener(ContentDivider divider) {
 135             this.divider = divider;


 136         }
 137         
 138         @Override public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {  
 139             if (checkDividerPos) {
 140                 // When checking is enforced, we know that the position was set explicitly
 141                 divider.posExplicit = true;
 142             }
 143             getSkinnable().requestLayout();


 144         }
 145     }
 146        
 147     private void checkDividerPosition(ContentDivider divider, double newPos, double oldPos) {
 148         double dividerWidth = divider.prefWidth(-1);
 149         Content left = getLeft(divider);
 150         Content right = getRight(divider);
 151         double minLeft = left == null ? 0 : (horizontal) ? left.minWidth(-1) : left.minHeight(-1);
 152         double minRight = right == null ? 0 : (horizontal) ? right.minWidth(-1) : right.minHeight(-1);
 153         double maxLeft = left == null ? 0 :
 154             left.getContent() != null ? (horizontal) ? left.getContent().maxWidth(-1) : left.getContent().maxHeight(-1) : 0;
 155         double maxRight = right == null ? 0 :
 156             right.getContent() != null ? (horizontal) ? right.getContent().maxWidth(-1) : right.getContent().maxHeight(-1) : 0;                        
 157                
 158         double previousDividerPos = 0;
 159         double nextDividerPos = getSize();
 160         int index = contentDividers.indexOf(divider);
 161 
 162         if (index - 1 >= 0) {
 163             previousDividerPos = contentDividers.get(index - 1).getDividerPos();
 164             if (previousDividerPos == -1) {
 165                 // Get the divider position if it hasn't been initialized.
 166                 previousDividerPos = getAbsoluteDividerPos(contentDividers.get(index - 1));
 167             }
 168         }
 169         if (index + 1 < contentDividers.size()) {
 170             nextDividerPos = contentDividers.get(index + 1).getDividerPos();
 171             if (nextDividerPos == -1) {
 172                 // Get the divider position if it hasn't been initialized.
 173                 nextDividerPos = getAbsoluteDividerPos(contentDividers.get(index + 1));
 174             }
 175         }
 176         
 177         // Set the divider into the correct position by looking at the max and min content sizes.
 178         checkDividerPos = false;
 179         if (newPos > oldPos) {
 180             double max = previousDividerPos == 0 ? maxLeft : previousDividerPos + dividerWidth + maxLeft;
 181             double min = nextDividerPos - minRight - dividerWidth;                
 182             double stopPos = Math.min(max, min);
 183             if (newPos >= stopPos) {                
 184                 setAbsoluteDividerPos(divider, stopPos);
 185             } else {
 186                 double rightMax = nextDividerPos - maxRight - dividerWidth;                    
 187                 if (newPos <= rightMax) {
 188                     setAbsoluteDividerPos(divider, rightMax);
 189                 } else {                    
 190                     setAbsoluteDividerPos(divider, newPos);


 191                 }
 192             }             








 193         } else {
 194             double max = nextDividerPos - maxRight - dividerWidth;
 195             double min = previousDividerPos == 0 ? minLeft : previousDividerPos + minLeft + dividerWidth;
 196             double stopPos = Math.max(max, min);
 197             if (newPos <= stopPos) {
 198                 setAbsoluteDividerPos(divider, stopPos);
 199             } else {
 200                 double leftMax = previousDividerPos + maxLeft + dividerWidth;
 201                 if (newPos >= leftMax) {
 202                     setAbsoluteDividerPos(divider, leftMax);
 203                 } else {
 204                     setAbsoluteDividerPos(divider, newPos);

 205                 }
 206             }                








 207         }                    
 208         checkDividerPos = true;
 209     }
 210     
 211     private void addDivider(SplitPane.Divider d) {
 212         ContentDivider c = new ContentDivider(d);        
 213         c.setInitialPos(d.getPosition());
 214         c.setDividerPos(-1);
 215         ChangeListener<Number> posPropertyListener = new PosPropertyListener(c);
 216         c.setPosPropertyListener(posPropertyListener);
 217         d.positionProperty().addListener(posPropertyListener);
 218         initializeDivderEventHandlers(c);
 219         contentDividers.add(c);
 220         getChildren().add(c);
 221     }
 222 
 223     private void removeAllDividers() {
 224         ListIterator<ContentDivider> dividers = contentDividers.listIterator();
 225         while (dividers.hasNext()) {
 226             ContentDivider c = dividers.next();
 227             getChildren().remove(c);
 228             c.getDivider().positionProperty().removeListener(c.getPosPropertyListener());
 229             dividers.remove();
 230         }
 231         lastDividerUpdate = 0;

 232     }
 233 
 234     private void initializeDivderEventHandlers(final ContentDivider divider) {
 235         // TODO: do we need to consume all mouse events?
 236         // they only bubble to the skin which consumes them by default
 237         divider.addEventHandler(MouseEvent.ANY, event -> {
 238             event.consume();
 239         });
 240 
 241         divider.setOnMousePressed(e -> {
 242             if (horizontal) {
 243                 divider.setInitialPos(divider.getDividerPos());
 244                 divider.setPressPos(e.getSceneX());
 245                 divider.setPressPos(getSkinnable().getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT
 246                         ? getSkinnable().getWidth() - e.getSceneX() : e.getSceneX());
 247             } else {
 248                 divider.setInitialPos(divider.getDividerPos());
 249                 divider.setPressPos(e.getSceneY());
 250             }
 251             e.consume();
 252         });
 253 
 254         divider.setOnMouseDragged(e -> {
 255             double delta = 0;
 256             if (horizontal) {
 257                 delta = getSkinnable().getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT
 258                         ? getSkinnable().getWidth() - e.getSceneX() : e.getSceneX();
 259             } else {
 260                 delta = e.getSceneY();




 261             }
 262             delta -= divider.getPressPos();
 263             setAndCheckAbsoluteDividerPos(divider, Math.ceil(divider.getInitialPos() + delta));
 264             e.consume();
 265         });
 266     }
 267 
 268     private Content getLeft(ContentDivider d) {
 269         int index = contentDividers.indexOf(d);
 270         if (index != -1) {
 271             return contentRegions.get(index);







 272         }
 273         return null;










 274     }
 275 
 276     private Content getRight(ContentDivider d) {
 277         int index = contentDividers.indexOf(d);
 278         if (index != -1) {
 279             return contentRegions.get(index + 1);
 280         }
 281         return null;


 282     }
 283 
 284     @Override protected void handleControlPropertyChanged(String property) {
 285         super.handleControlPropertyChanged(property);
 286         if ("ORIENTATION".equals(property)) {
 287             this.horizontal = getSkinnable().getOrientation() == Orientation.HORIZONTAL;
 288             this.previousSize = -1;
 289             for (ContentDivider c: contentDividers) {
 290                 c.setGrabberStyle(horizontal);
 291             }
 292             getSkinnable().requestLayout();
 293         } else if ("WIDTH".equals(property) || "HEIGHT".equals(property)) {
 294             getSkinnable().requestLayout();
 295         }














 296     }
 297 
 298     // Value is the left edge of the divider
 299     private void setAbsoluteDividerPos(ContentDivider divider, double value) {
 300         if (getSkinnable().getWidth() > 0 && getSkinnable().getHeight() > 0 && divider != null) {
 301             SplitPane.Divider paneDivider = divider.getDivider();
 302             divider.setDividerPos(value);
 303             double size = getSize();
 304             if (size != 0) {
 305                 // Adjust the position to the center of the
 306                 // divider and convert its position to a percentage.
 307                 double pos = value + divider.prefWidth(-1)/2;
 308                 paneDivider.setPosition(pos / size);
 309             } else {
 310                 paneDivider.setPosition(0);
 311             }



 312         }
 313     }
 314    
 315     // Updates the divider with the SplitPane.Divider's position
 316     // The value updated to SplitPane.Divider will be the center of the divider.
 317     // The returned position will be the left edge of the divider
 318     private double getAbsoluteDividerPos(ContentDivider divider) {
 319         if (getSkinnable().getWidth() > 0 && getSkinnable().getHeight() > 0 && divider != null) {
 320             SplitPane.Divider paneDivider = divider.getDivider();
 321             double newPos = posToDividerPos(divider, paneDivider.getPosition());
 322             divider.setDividerPos(newPos);
 323             return newPos;
 324         }
 325         return 0;
 326     }
 327 
 328     // Returns the left edge of the divider at pos
 329     // Pos is the percentage location from SplitPane.Divider.
 330     private double posToDividerPos(ContentDivider divider, double pos) {
 331         double newPos = getSize() * pos;
 332         if (pos == 1) {                
 333             newPos -= divider.prefWidth(-1);
 334         } else {
 335             newPos -= divider.prefWidth(-1)/2;


 336         }       
 337         return Math.round(newPos);                    





 338     }
 339     
 340     private double totalMinSize() {
 341         double dividerWidth = !contentDividers.isEmpty() ? contentDividers.size() * contentDividers.get(0).prefWidth(-1) : 0;
 342         double minSize = 0;
 343         for (Content c: contentRegions) {
 344             if (horizontal) {
 345                 minSize += c.minWidth(-1);
 346             } else {
 347                 minSize += c.minHeight(-1);
 348             }



 349         }
 350         return minSize + dividerWidth;
 351     }
 352 
 353     private double getSize() {
 354         final SplitPane s = getSkinnable();
 355         double size = totalMinSize();
 356         if (horizontal) {
 357             if (s.getWidth() > size) {
 358                 size = s.getWidth() - snappedLeftInset() - snappedRightInset();
 359             }





 360         } else {
 361             if (s.getHeight() > size) {
 362                 size = s.getHeight() - snappedTopInset() - snappedBottomInset();
 363             }
 364         }
 365         return size;
 366     }
 367 
 368     // Evenly distribute the size to the available list.
 369     // size is the amount to distribute.
 370     private double distributeTo(List<Content> available, double size) {
 371         if (available.isEmpty()) {
 372             return size;






 373         }
 374                 
 375         size = snapSize(size);
 376         int portion = (int)(size)/available.size();
 377         int remainder;
 378 
 379         while (size > 0 && !available.isEmpty()) {
 380             Iterator<Content> i = available.iterator();
 381             while (i.hasNext()) {
 382                 Content c = i.next();
 383                 double max = Math.min((horizontal ? c.maxWidth(-1) : c.maxHeight(-1)), Double.MAX_VALUE);
 384                 double min = horizontal ? c.minWidth(-1) : c.minHeight(-1);
 385 
 386                 // We have too much space
 387                 if (c.getArea() >= max) {
 388                     c.setAvailable(c.getArea() - min);
 389                     i.remove();
 390                     continue;
 391                 }
 392                 // Not enough space
 393                 if (portion >= (max - c.getArea())) {
 394                     size -= (max - c.getArea());
 395                     c.setArea(max);
 396                     c.setAvailable(max - min);
 397                     i.remove();
 398                 } else {
 399                     // Enough space
 400                     c.setArea(c.getArea() + portion);
 401                     c.setAvailable(c.getArea() - min);
 402                     size -= portion;
 403                 }
 404                 if ((int)size == 0) {
 405                     return size;
 406                 }
 407             }
 408             if (available.isEmpty()) {
 409                 // We reached the max size for everything just return
 410                 return size;








 411             }
 412             portion = (int)(size)/available.size();
 413             remainder = (int)(size)%available.size();
 414             if (portion == 0 && remainder != 0) {
 415                 portion = remainder;
 416                 remainder = 0;
 417             }


 418         }
 419         return size;
 420     }
 421 
 422     // Evenly distribute the size from the available list.
 423     // size is the amount to distribute.
 424     private double distributeFrom(double size, List<Content> available) {
 425         if (available.isEmpty()) {
 426             return size;
 427         }
 428         
 429         size = snapSize(size);
 430         int portion = (int)(size)/available.size();
 431         int remainder;
 432 
 433         while (size > 0 && !available.isEmpty()) {
 434             Iterator<Content> i = available.iterator();
 435             while (i.hasNext()) {
 436                 Content c = i.next();
 437                 //not enough space taking available and setting min
 438                 if (portion >= c.getAvailable()) {
 439                     c.setArea(c.getArea() - c.getAvailable()); // Min size
 440                     size -= c.getAvailable();
 441                     c.setAvailable(0);
 442                     i.remove();
 443                 } else {
 444                     //enough space
 445                     c.setArea(c.getArea() - portion);
 446                     c.setAvailable(c.getAvailable() - portion);
 447                     size -= portion;
 448                 }
 449                 if ((int)size == 0) {
 450                     return size;
 451                 }
 452             }
 453             if (available.isEmpty()) {
 454                 // We reached the min size for everything just return
 455                 return size;
 456             }
 457             portion = (int)(size)/available.size();
 458             remainder = (int)(size)%available.size();
 459             if (portion == 0 && remainder != 0) {
 460                 portion = remainder;
 461                 remainder = 0;
 462             }




 463         }
 464         return size;
 465     }
 466 
 467     private void setupContentAndDividerForLayout() {
 468         // Set all the value to prepare for layout
 469         double dividerWidth = contentDividers.isEmpty() ? 0 : contentDividers.get(0).prefWidth(-1);
 470         double startX = 0;
 471         double startY = 0;
 472         for (Content c: contentRegions) {
 473             if (resize && !c.isResizableWithParent()) {
 474                 c.setArea(c.getResizableWithParentArea());



 475             }
 476             
 477             c.setX(startX);
 478             c.setY(startY);
 479             if (horizontal) {
 480                 startX += (c.getArea() + dividerWidth);
 481             } else {
 482                 startY += (c.getArea() + dividerWidth);
 483             }
 484         }
 485 
 486         startX = 0;
 487         startY = 0;
 488         // The dividers are already in the correct positions.  Disable
 489         // checking the divider positions.
 490         checkDividerPos = false;
 491         for (int i = 0; i < contentDividers.size(); i++) {
 492             ContentDivider d = contentDividers.get(i);




 493             if (horizontal) {
 494                 startX += getLeft(d).getArea() + (i == 0 ? 0 : dividerWidth);
 495             } else {
 496                 startY += getLeft(d).getArea() + (i == 0 ? 0 : dividerWidth);
 497             }
 498             d.setX(startX);
 499             d.setY(startY);              
 500             setAbsoluteDividerPos(d, (horizontal ? d.getX() : d.getY()));
 501             d.posExplicit = false;
 502         }
 503         checkDividerPos = true;
 504     }
 505 
 506     private void layoutDividersAndContent(double width, double height) {
 507         final double paddingX = snappedLeftInset();
 508         final double paddingY = snappedTopInset();
 509         final double dividerWidth = contentDividers.isEmpty() ? 0 : contentDividers.get(0).prefWidth(-1);
 510 
 511         for (Content c: contentRegions) {
 512 //            System.out.println("LAYOUT " + c.getId() + " PANELS X " + c.getX() + " Y " + c.getY() + " W " + (horizontal ? c.getArea() : width) + " H " + (horizontal ? height : c.getArea()));
 513             if (horizontal) {
 514                 c.setClipSize(c.getArea(), height);
 515                 layoutInArea(c, c.getX() + paddingX, c.getY() + paddingY, c.getArea(), height,
 516                     0/*baseline*/,HPos.CENTER, VPos.CENTER);
 517             } else {
 518                 c.setClipSize(width, c.getArea());
 519                 layoutInArea(c, c.getX() + paddingX, c.getY() + paddingY, width, c.getArea(),
 520                     0/*baseline*/,HPos.CENTER, VPos.CENTER);
 521             }


 522         }
 523         for (ContentDivider c: contentDividers) {
 524 //            System.out.println("LAYOUT DIVIDERS X " + c.getX() + " Y " + c.getY() + " W " + (horizontal ? dividerWidth : width) + " H " + (horizontal ? height : dividerWidth));
 525             if (horizontal) {
 526                 c.resize(dividerWidth, height);
 527                 positionInArea(c, c.getX() + paddingX, c.getY() + paddingY, dividerWidth, height,
 528                     /*baseline ignored*/0, HPos.CENTER, VPos.CENTER);
 529             } else {
 530                 c.resize(width, dividerWidth);                
 531                 positionInArea(c, c.getX() + paddingX, c.getY() + paddingY, width, dividerWidth,
 532                     /*baseline ignored*/0, HPos.CENTER, VPos.CENTER);
 533             }
 534         }
 535     }
 536 
 537     private double previousSize = -1;
 538     private int lastDividerUpdate = 0;
 539     private boolean resize = false;
 540     private boolean checkDividerPos = true;
 541 
 542     @Override protected void layoutChildren(final double x, final double y,
 543             final double w, final double h) {
 544         final SplitPane s = getSkinnable();
 545         final double sw = s.getWidth();
 546         final double sh = s.getHeight();
 547         
 548         if (!s.isVisible() || 
 549             (horizontal ? sw == 0 : sh == 0) ||
 550             contentRegions.isEmpty()) {
 551             return;
 552         }
 553         
 554         double dividerWidth = contentDividers.isEmpty() ? 0 : contentDividers.get(0).prefWidth(-1);




 555 
 556         if (contentDividers.size() > 0 && previousSize != -1 && previousSize != (horizontal ? sw  : sh)) {
 557             //This algorithm adds/subtracts a little to each panel on every resize
 558             List<Content> resizeList = new ArrayList<Content>();
 559             for (Content c: contentRegions) {
 560                 if (c.isResizableWithParent()) {
 561                     resizeList.add(c);



 562                 }
 563             }
 564                         
 565             double delta = (horizontal ? s.getWidth() : s.getHeight()) - previousSize;
 566             boolean growing = delta > 0;
 567             
 568             delta = Math.abs(delta);            










 569 
 570             if (delta != 0 && !resizeList.isEmpty()) {
 571                 int portion = (int)(delta)/resizeList.size();
 572                 int remainder = (int)delta%resizeList.size();
 573                 int size = 0;
 574                 if (portion == 0) {
 575                     portion = remainder;
 576                     size = remainder;
 577                     remainder = 0;
 578                 } else {
 579                     size = portion * resizeList.size();

 580                 }
 581 
 582                 while (size > 0 && !resizeList.isEmpty()) {
 583                     if (growing) {
 584                         lastDividerUpdate++;
 585                     } else {
 586                         lastDividerUpdate--;
 587                         if (lastDividerUpdate < 0) {
 588                             lastDividerUpdate = contentRegions.size() - 1;
 589                         }
 590                     }
 591                     int id = lastDividerUpdate%contentRegions.size();
 592                     Content content = contentRegions.get(id);
 593                     if (content.isResizableWithParent() && resizeList.contains(content)) {
 594                         double area = content.getArea();
 595                         if (growing) {
 596                             double max = horizontal ? content.maxWidth(-1) : content.maxHeight(-1);
 597                             if ((area + portion) <= max) {
 598                                 area += portion;
 599                             } else {
 600                                 resizeList.remove(content);
 601                                 continue;
 602                             }
 603                         } else {
 604                             double min = horizontal ? content.minWidth(-1) : content.minHeight(-1);
 605                             if ((area - portion) >= min) {
 606                                 area -= portion;
 607                             } else {
 608                                 resizeList.remove(content);
 609                                 continue;
 610                             }























 611                         }
 612                         content.setArea(area);
 613                         size -= portion;
 614                         if (size == 0 && remainder != 0) {
 615                             portion = remainder;
 616                             size = remainder;
 617                             remainder = 0;
 618                         } else if (size == 0) {
 619                             break;
 620                         }





 621                     }
 622                 }
 623 
 624                 // If we are resizing the window save the current area into
 625                 // resizableWithParentArea.  We use this value during layout.
 626                 {
 627                     for (Content c: contentRegions) {
 628                         c.setResizableWithParentArea(c.getArea());
 629                         c.setAvailable(0);








 630                     }
 631                 }
 632                 resize = true;















 633             }
 634 
 635             previousSize = horizontal ? sw : sh;
 636         } else {
 637             previousSize = horizontal ? sw : sh;







 638         }
 639         
 640         // If the window is less than the min size we want to resize
 641         // proportionally
 642         double minSize = totalMinSize();
 643         if (minSize > (horizontal ? w : h)) {
 644             double percentage = 0;
 645             for (int i = 0; i < contentRegions.size(); i++) {
 646                 Content c = contentRegions.get(i);
 647                 double min = horizontal ? c.minWidth(-1) : c.minHeight(-1);
 648                 percentage = min/minSize;
 649                 c.setArea(snapSpace(percentage * (horizontal ? w : h)));
 650                 c.setAvailable(0);
 651             }
 652             setupContentAndDividerForLayout();
 653             layoutDividersAndContent(w, h);
 654             resize = false;
 655             return;
 656         }
 657 
 658         for(int trys = 0; trys < 10; trys++) {
 659             // Compute the area in between each divider.            
 660             ContentDivider previousDivider = null;
 661             ContentDivider divider = null;            
 662             for (int i = 0; i < contentRegions.size(); i++) {
 663                 double space = 0;
 664                 if (i < contentDividers.size()) {
 665                     divider = contentDividers.get(i);
 666                     if (divider.posExplicit) {
 667                         checkDividerPosition(divider, posToDividerPos(divider, divider.d.getPosition()),
 668                                 divider.getDividerPos());
 669                     }
 670                     if (i == 0) {
 671                         // First panel
 672                         space = getAbsoluteDividerPos(divider);
 673                     } else {
 674                         double newPos = getAbsoluteDividerPos(previousDivider) + dividerWidth;
 675                         // Middle panels
 676                         if (getAbsoluteDividerPos(divider) <= getAbsoluteDividerPos(previousDivider)) {
 677                             // The current divider and the previous divider share the same position
 678                             // or the current divider position is less than the previous position.
 679                             // We will set the divider next to the previous divider.
 680                             setAndCheckAbsoluteDividerPos(divider, newPos);
 681                         }
 682                         space = getAbsoluteDividerPos(divider) - newPos;









 683                     }
 684                 } else if (i == contentDividers.size()) {
 685                     // Last panel
 686                     space = (horizontal ? w : h) - (previousDivider != null ? getAbsoluteDividerPos(previousDivider) + dividerWidth : 0);

 687                 }
 688                 if (!resize || divider.posExplicit) {
 689                     contentRegions.get(i).setArea(space);



 690                 }
 691                 previousDivider = divider;
 692             }
 693 
 694             // Compute the amount of space we have available.
 695             // Available is amount of space we can take from a panel before we reach its min.
 696             // If available is negative we don't have enough space and we will
 697             // proportionally take the space from the other availables.  If we have extra space
 698             // we will porportionally give it to the others
 699             double spaceRequested = 0;
 700             double extraSpace = 0;
 701             for (Content c: contentRegions) {
 702                 double max = 0;
 703                 double min = 0;
 704                 if (c != null) {
 705                     max = horizontal ? c.maxWidth(-1) : c.maxHeight(-1);
 706                     min = horizontal ? c.minWidth(-1) : c.minHeight(-1);
 707                 }
 708 
 709                 if (c.getArea() >= max) {
 710                     // Add the space that needs to be distributed to the others
 711                     extraSpace += (c.getArea() - max);
 712                     c.setArea(max);









 713                 }
 714                 c.setAvailable(c.getArea() - min);
 715                 if (c.getAvailable() < 0) {
 716                     spaceRequested += c.getAvailable();
 717                 }
 718             }
 719 
 720             spaceRequested = Math.abs(spaceRequested);
 721 
 722             // Add the panels where we can take space from
 723             List<Content> availableList = new ArrayList<Content>();
 724             List<Content> storageList = new ArrayList<Content>();
 725             List<Content> spaceRequestor = new ArrayList<Content>();
 726             double available = 0;
 727             for (Content c: contentRegions) {
 728                 if (c.getAvailable() >= 0) {
 729                     available += c.getAvailable();
 730                     availableList.add(c);
 731                 }
 732 
 733                 if (resize && !c.isResizableWithParent()) {
 734                     // We are making the SplitPane bigger and will need to
 735                     // distribute the extra space.
 736                     if (c.getArea() >= c.getResizableWithParentArea()) {                        
 737                         extraSpace += (c.getArea() - c.getResizableWithParentArea());

 738                     } else {
 739                         // We are making the SplitPane smaller and will need to
 740                         // distribute the space requested.
 741                         spaceRequested += (c.getResizableWithParentArea() - c.getArea());
 742                     }
 743                     c.setAvailable(0);
 744                 }
 745                 // Add the panels where we can add space to;
 746                 if (resize) {
 747                     if (c.isResizableWithParent()) {
 748                         storageList.add(c);
 749                     }







 750                 } else {
 751                     storageList.add(c);
 752                 }
 753                 // List of panels that need space.
 754                 if (c.getAvailable() < 0) {
 755                     spaceRequestor.add(c);
 756                 }

 757             }
 758 
 759             if (extraSpace > 0) {
 760                 extraSpace = distributeTo(storageList, extraSpace);
 761                 // After distributing add any panels that may still need space to the
 762                 // spaceRequestor list.
 763                 spaceRequested = 0;
 764                 spaceRequestor.clear();
 765                 available = 0;
 766                 availableList.clear();
 767                 for (Content c: contentRegions) {
 768                     if (c.getAvailable() < 0) {
 769                         spaceRequested += c.getAvailable();
 770                         spaceRequestor.add(c);
 771                     } else {
 772                         available += c.getAvailable();
 773                         availableList.add(c);
 774                     }
 775                 }
 776                 spaceRequested = Math.abs(spaceRequested);
 777             }
 778 
 779             if (available >= spaceRequested) {
 780                 for (Content requestor: spaceRequestor) {
 781                     double min = horizontal ? requestor.minWidth(-1) : requestor.minHeight(-1);
 782                     requestor.setArea(min);
 783                     requestor.setAvailable(0);
 784                 }
 785                 // After setting all the space requestors to their min we have to
 786                 // redistribute the space requested to any panel that still
 787                 // has available space.
 788                 if (spaceRequested > 0 && !spaceRequestor.isEmpty()) {
 789                     distributeFrom(spaceRequested, availableList);
 790                 }
 791 
 792                 // Only for resizing.  We should have all the panel areas
 793                 // available computed.  We can total them up and see
 794                 // how much space we have left or went over and redistribute.
 795                 if (resize) {
 796                     double total = 0;
 797                     for (Content c: contentRegions) {
 798                         if (c.isResizableWithParent()) {
 799                             total += c.getArea();















 800                         } else {
 801                             total += c.getResizableWithParentArea();



 802                         }


 803                     }
 804                     total += (dividerWidth * contentDividers.size());
 805                     if (total < (horizontal ? w : h)) {
 806                         extraSpace += ((horizontal ? w : h) - total);
 807                         distributeTo(storageList, extraSpace);
 808                     } else {
 809                         spaceRequested += (total - (horizontal ? w : h));
 810                         distributeFrom(spaceRequested, storageList);



 811                     }
 812                 }

 813             }
 814 
 815             setupContentAndDividerForLayout();





 816 
 817             // Check the bounds of every panel
 818             boolean passed = true;
 819             for (Content c: contentRegions) {
 820                 double max = horizontal ? c.maxWidth(-1) : c.maxHeight(-1);
 821                 double min = horizontal ? c.minWidth(-1) : c.minHeight(-1);
 822                 if (c.getArea() < min || c.getArea() > max) {
 823                     passed = false;
 824                     break;











 825                 }


 826             }
 827             if (passed) {
 828                 break;
 829             }



 830         }
 831 
 832         layoutDividersAndContent(w, h);        
 833         resize = false;        


 834     }
 835 
 836     private void setAndCheckAbsoluteDividerPos(ContentDivider divider, double value) {
 837         double oldPos = divider.getDividerPos();
 838         setAbsoluteDividerPos(divider, value);
 839         checkDividerPosition(divider, value, oldPos);
 840     }
 841 
 842     @Override protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
 843         double minWidth = 0;
 844         double maxMinWidth = 0;


 845         for (Content c: contentRegions) {
 846             minWidth += c.minWidth(-1);
 847             maxMinWidth = Math.max(maxMinWidth, c.minWidth(-1));
 848         }
 849         for (ContentDivider d: contentDividers) {
 850             minWidth += d.prefWidth(-1);
 851         }



 852         if (horizontal) {
 853             return minWidth + leftInset + rightInset;
 854         } else {
 855             return maxMinWidth + leftInset + rightInset;
 856         }
 857     }
 858 
 859     @Override protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
 860         double minHeight = 0;
 861         double maxMinHeight = 0;
 862         for (Content c: contentRegions) {
 863             minHeight += c.minHeight(-1);
 864             maxMinHeight = Math.max(maxMinHeight, c.minHeight(-1));
 865         }
 866         for (ContentDivider d: contentDividers) {
 867             minHeight += d.prefWidth(-1);
 868         }
 869         if (horizontal) {
 870             return maxMinHeight + topInset + bottomInset;
 871         } else {
 872             return minHeight + topInset + bottomInset;





 873         }

 874     }
 875 
 876     @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
 877         double prefWidth = 0;
 878         double prefMaxWidth = 0;


 879         for (Content c: contentRegions) {
 880             prefWidth += c.prefWidth(-1);
 881             prefMaxWidth = Math.max(prefMaxWidth, c.prefWidth(-1));







 882         }
 883         for (ContentDivider d: contentDividers) {
 884             prefWidth += d.prefWidth(-1);
 885         }


 886         if (horizontal) {
 887             return prefWidth + leftInset + rightInset;


 888         } else {
 889             return prefMaxWidth + leftInset + rightInset;



 890         }
 891     }
 892 
 893     @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
 894         double prefHeight = 0;
 895         double maxPrefHeight = 0;
 896         for (Content c: contentRegions) {
 897             prefHeight += c.prefHeight(-1);
 898             maxPrefHeight = Math.max(maxPrefHeight, c.prefHeight(-1));



 899         }
 900         for (ContentDivider d: contentDividers) {
 901             prefHeight += d.prefWidth(-1);













 902         }
 903         if (horizontal) {
 904             return maxPrefHeight + topInset + bottomInset;
 905         } else {
 906             return prefHeight + topInset + bottomInset;



 907         }
 908     }
 909     
 910 
 911 //    private void printDividerPositions() {
 912 //        for (int i = 0; i < contentDividers.size(); i++) {
 913 //            System.out.print("DIVIDER[" + i + "] " + contentDividers.get(i).getDividerPos() + " ");
 914 //        } 
 915 //        System.out.println("");
 916 //    }
 917 //    
 918 //    private void printAreaAndAvailable() {
 919 //        for (int i = 0; i < contentRegions.size(); i++) {
 920 //            System.out.print("AREA[" + i + "] " + contentRegions.get(i).getArea() + " ");
 921 //        }
 922 //        System.out.println("");
 923 //        for (int i = 0; i < contentRegions.size(); i++) {
 924 //            System.out.print("AVAILABLE[" + i + "] " + contentRegions.get(i).getAvailable() + " ");
 925 //        }
 926 //        System.out.println("");
 927 //        for (int i = 0; i < contentRegions.size(); i++) {
 928 //            System.out.print("RESIZABLEWTIHPARENT[" + i + "] " + contentRegions.get(i).getResizableWithParentArea() + " ");
 929 //        }
 930 //        System.out.println("");
 931 //    }
 932 
 933     class ContentDivider extends StackPane {
 934         private double initialPos;
 935         private double dividerPos;
 936         private double pressPos;
 937         private SplitPane.Divider d;
 938         private StackPane grabber;
 939         private double x;
 940         private double y;
 941         private boolean posExplicit;
 942         private ChangeListener<Number> listener;
 943 
 944         public ContentDivider(SplitPane.Divider d) {
 945             getStyleClass().setAll("split-pane-divider");
 946 
 947             this.d = d;
 948             this.initialPos = 0;
 949             this.dividerPos = 0;
 950             this.pressPos = 0;
 951 


   1 /*
   2  * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.scene.control.skin;
  27 
  28 import javafx.beans.value.ChangeListener;
  29 import javafx.beans.value.ObservableValue;
  30 import javafx.collections.FXCollections;
  31 import javafx.collections.ListChangeListener;
  32 import javafx.collections.ObservableList;
  33 import javafx.geometry.HPos;
  34 import javafx.geometry.NodeOrientation;
  35 import javafx.geometry.Orientation;
  36 import javafx.geometry.VPos;
  37 import javafx.scene.Cursor;
  38 import javafx.scene.Node;
  39 import javafx.scene.control.Accordion;
  40 import javafx.scene.control.Control;
  41 import javafx.scene.control.SkinBase;
  42 import javafx.scene.control.SplitPane;
  43 import javafx.scene.input.MouseEvent;
  44 import javafx.scene.layout.StackPane;
  45 import javafx.scene.shape.Rectangle;
  46 import java.util.ArrayList;

  47 import java.util.Iterator;
  48 import java.util.List;
  49 import java.util.ListIterator;

  50 
  51 /**
  52  * Default skin implementation for the {@link SplitPane} control.
  53  *
  54  * @see SplitPane
  55  * @since 9
  56  */
  57 public class SplitPaneSkin extends SkinBase<SplitPane> {
  58 
  59     /***************************************************************************
  60      *                                                                         *
  61      * Private fields                                                          *
  62      *                                                                         *
  63      **************************************************************************/
  64 
  65     private ObservableList<Content> contentRegions;
  66     private ObservableList<ContentDivider> contentDividers;
  67     private boolean horizontal;
  68 
  69 
  70 
  71     /***************************************************************************
  72      *                                                                         *
  73      * Constructors                                                            *
  74      *                                                                         *
  75      **************************************************************************/
  76 
  77     /**
  78      * Creates a new SplitPaneSkin instance, installing the necessary child
  79      * nodes into the Control {@link Control#getChildren() children} list, as
  80      * well as the necessary {@link Node#getInputMap() input mappings} for
  81      * handling key, mouse, etc events.
  82      *
  83      * @param control The control that this skin should be installed onto.
  84      */
  85     public SplitPaneSkin(final SplitPane control) {
  86         super(control);
  87 //        control.setManaged(false);
  88         horizontal = getSkinnable().getOrientation() == Orientation.HORIZONTAL;
  89         
  90         contentRegions = FXCollections.<Content>observableArrayList();
  91         contentDividers = FXCollections.<ContentDivider>observableArrayList();
  92 
  93         int index = 0;
  94         for (Node n: getSkinnable().getItems()) {
  95             addContent(index++, n);
  96         }
  97         initializeContentListener();
  98 
  99         for (SplitPane.Divider d: getSkinnable().getDividers()) {
 100             addDivider(d);
 101         }
 102 
 103         registerChangeListener(control.orientationProperty(), e -> {
 104             this.horizontal = getSkinnable().getOrientation() == Orientation.HORIZONTAL;
 105             this.previousSize = -1;
 106             for (ContentDivider c: contentDividers) {
 107                 c.setGrabberStyle(horizontal);













 108             }
 109             getSkinnable().requestLayout();
 110         });
 111         registerChangeListener(control.widthProperty(), e -> getSkinnable().requestLayout());
 112         registerChangeListener(control.heightProperty(), e -> getSkinnable().requestLayout());
 113     }
 114 














 115 




 116 
 117     /***************************************************************************
 118      *                                                                         *
 119      * Public API                                                              *
 120      *                                                                         *
 121      **************************************************************************/









 122 
 123     /** {@inheritDoc} */
 124     @Override protected void layoutChildren(final double x, final double y,
 125                                             final double w, final double h) {
 126         final SplitPane s = getSkinnable();
 127         final double sw = s.getWidth();
 128         final double sh = s.getHeight();
 129 
 130         if (!s.isVisible() ||
 131                 (horizontal ? sw == 0 : sh == 0) ||
 132                 contentRegions.isEmpty()) {
 133             return;
 134         }
 135 
 136         double dividerWidth = contentDividers.isEmpty() ? 0 : contentDividers.get(0).prefWidth(-1);
 137 
 138         if (contentDividers.size() > 0 && previousSize != -1 && previousSize != (horizontal ? sw  : sh)) {
 139             //This algorithm adds/subtracts a little to each panel on every resize
 140             List<Content> resizeList = new ArrayList<Content>();
 141             for (Content c: contentRegions) {
 142                 if (c.isResizableWithParent()) {
 143                     resizeList.add(c);
 144                 }
 145             }
 146 
 147             double delta = (horizontal ? s.getWidth() : s.getHeight()) - previousSize;
 148             boolean growing = delta > 0;








 149 
 150             delta = Math.abs(delta);


 151 
 152             if (delta != 0 && !resizeList.isEmpty()) {
 153                 int portion = (int)(delta)/resizeList.size();
 154                 int remainder = (int)delta%resizeList.size();
 155                 int size = 0;
 156                 if (portion == 0) {
 157                     portion = remainder;
 158                     size = remainder;
 159                     remainder = 0;
 160                 } else {
 161                     size = portion * resizeList.size();



 162                 }
 163 
 164                 while (size > 0 && !resizeList.isEmpty()) {
 165                     if (growing) {
 166                         lastDividerUpdate++;









 167                     } else {
 168                         lastDividerUpdate--;
 169                         if (lastDividerUpdate < 0) {
 170                             lastDividerUpdate = contentRegions.size() - 1;
 171                         }
 172                     }
 173                     int id = lastDividerUpdate%contentRegions.size();
 174                     Content content = contentRegions.get(id);
 175                     if (content.isResizableWithParent() && resizeList.contains(content)) {
 176                         double area = content.getArea();
 177                         if (growing) {
 178                             double max = horizontal ? content.maxWidth(-1) : content.maxHeight(-1);
 179                             if ((area + portion) <= max) {
 180                                 area += portion;
 181                             } else {
 182                                 resizeList.remove(content);
 183                                 continue;
 184                             }


 185                         } else {
 186                             double min = horizontal ? content.minWidth(-1) : content.minHeight(-1);
 187                             if ((area - portion) >= min) {
 188                                 area -= portion;
 189                             } else {
 190                                 resizeList.remove(content);
 191                                 continue;
 192                             }
 193                         }
 194                         content.setArea(area);
 195                         size -= portion;
 196                         if (size == 0 && remainder != 0) {
 197                             portion = remainder;
 198                             size = remainder;
 199                             remainder = 0;
 200                         } else if (size == 0) {
 201                             break;
 202                         }

 203                     }











 204                 }
 205 
 206                 // If we are resizing the window save the current area into
 207                 // resizableWithParentArea.  We use this value during layout.
 208                 {
 209                     for (Content c: contentRegions) {
 210                         c.setResizableWithParentArea(c.getArea());
 211                         c.setAvailable(0);

 212                     }
 213                 }
 214                 resize = true;
 215             }
 216 
 217             previousSize = horizontal ? sw : sh;












 218         } else {
 219             previousSize = horizontal ? sw : sh;

 220         }


 221 
 222         // If the window is less than the min size we want to resize
 223         // proportionally
 224         double minSize = totalMinSize();
 225         if (minSize > (horizontal ? w : h)) {
 226             double percentage = 0;
 227             for (int i = 0; i < contentRegions.size(); i++) {
 228                 Content c = contentRegions.get(i);
 229                 double min = horizontal ? c.minWidth(-1) : c.minHeight(-1);
 230                 percentage = min/minSize;
 231                 c.setArea(snapSpace(percentage * (horizontal ? w : h)));
 232                 c.setAvailable(0);
 233             }
 234             setupContentAndDividerForLayout();
 235             layoutDividersAndContent(w, h);
 236             resize = false;
 237             return;
 238         }
 239 
 240         for(int trys = 0; trys < 10; trys++) {
 241             // Compute the area in between each divider.
 242             ContentDivider previousDivider = null;
 243             ContentDivider divider = null;
 244             for (int i = 0; i < contentRegions.size(); i++) {
 245                 double space = 0;
 246                 if (i < contentDividers.size()) {
 247                     divider = contentDividers.get(i);
 248                     if (divider.posExplicit) {
 249                         checkDividerPosition(divider, posToDividerPos(divider, divider.d.getPosition()),
 250                                 divider.getDividerPos());
 251                     }
 252                     if (i == 0) {
 253                         // First panel
 254                         space = getAbsoluteDividerPos(divider);
 255                     } else {
 256                         double newPos = getAbsoluteDividerPos(previousDivider) + dividerWidth;
 257                         // Middle panels
 258                         if (getAbsoluteDividerPos(divider) <= getAbsoluteDividerPos(previousDivider)) {
 259                             // The current divider and the previous divider share the same position
 260                             // or the current divider position is less than the previous position.
 261                             // We will set the divider next to the previous divider.
 262                             setAndCheckAbsoluteDividerPos(divider, newPos);
 263                         }
 264                         space = getAbsoluteDividerPos(divider) - newPos;




 265                     }
 266                 } else if (i == contentDividers.size()) {
 267                     // Last panel
 268                     space = (horizontal ? w : h) - (previousDivider != null ? getAbsoluteDividerPos(previousDivider) + dividerWidth : 0);
 269                 }
 270                 if (!resize || divider.posExplicit) {
 271                     contentRegions.get(i).setArea(space);






 272                 }
 273                 previousDivider = divider;


 274             }
 275 
 276             // Compute the amount of space we have available.
 277             // Available is amount of space we can take from a panel before we reach its min.
 278             // If available is negative we don't have enough space and we will
 279             // proportionally take the space from the other availables.  If we have extra space
 280             // we will porportionally give it to the others
 281             double spaceRequested = 0;
 282             double extraSpace = 0;
 283             for (Content c: contentRegions) {
 284                 double max = 0;
 285                 double min = 0;
 286                 if (c != null) {
 287                     max = horizontal ? c.maxWidth(-1) : c.maxHeight(-1);
 288                     min = horizontal ? c.minWidth(-1) : c.minHeight(-1);
 289                 }
 290 
 291                 if (c.getArea() >= max) {
 292                     // Add the space that needs to be distributed to the others
 293                     extraSpace += (c.getArea() - max);
 294                     c.setArea(max);









 295                 }
 296                 c.setAvailable(c.getArea() - min);
 297                 if (c.getAvailable() < 0) {
 298                     spaceRequested += c.getAvailable();
 299                 }
 300             }
 301 
 302             spaceRequested = Math.abs(spaceRequested);
 303 
 304             // Add the panels where we can take space from
 305             List<Content> availableList = new ArrayList<Content>();
 306             List<Content> storageList = new ArrayList<Content>();
 307             List<Content> spaceRequestor = new ArrayList<Content>();
 308             double available = 0;
 309             for (Content c: contentRegions) {
 310                 if (c.getAvailable() >= 0) {
 311                     available += c.getAvailable();
 312                     availableList.add(c);
 313                 }
 314 
 315                 if (resize && !c.isResizableWithParent()) {
 316                     // We are making the SplitPane bigger and will need to
 317                     // distribute the extra space.
 318                     if (c.getArea() >= c.getResizableWithParentArea()) {
 319                         extraSpace += (c.getArea() - c.getResizableWithParentArea());

 320                     } else {
 321                         // We are making the SplitPane smaller and will need to
 322                         // distribute the space requested.
 323                         spaceRequested += (c.getResizableWithParentArea() - c.getArea());
 324                     }
 325                     c.setAvailable(0);
 326                 }
 327                 // Add the panels where we can add space to;
 328                 if (resize) {
 329                     if (c.isResizableWithParent()) {
 330                         storageList.add(c);
 331                     }







 332                 } else {
 333                     storageList.add(c);
 334                 }
 335                 // List of panels that need space.
 336                 if (c.getAvailable() < 0) {
 337                     spaceRequestor.add(c);
 338                 }

 339             }
 340 
 341             if (extraSpace > 0) {
 342                 extraSpace = distributeTo(storageList, extraSpace);
 343                 // After distributing add any panels that may still need space to the
 344                 // spaceRequestor list.
 345                 spaceRequested = 0;
 346                 spaceRequestor.clear();
 347                 available = 0;
 348                 availableList.clear();
 349                 for (Content c: contentRegions) {
 350                     if (c.getAvailable() < 0) {
 351                         spaceRequested += c.getAvailable();
 352                         spaceRequestor.add(c);
 353                     } else {
 354                         available += c.getAvailable();
 355                         availableList.add(c);
 356                     }
 357                 }
 358                 spaceRequested = Math.abs(spaceRequested);
 359             }
 360 
 361             if (available >= spaceRequested) {
 362                 for (Content requestor: spaceRequestor) {
 363                     double min = horizontal ? requestor.minWidth(-1) : requestor.minHeight(-1);
 364                     requestor.setArea(min);
 365                     requestor.setAvailable(0);
 366                 }
 367                 // After setting all the space requestors to their min we have to
 368                 // redistribute the space requested to any panel that still
 369                 // has available space.
 370                 if (spaceRequested > 0 && !spaceRequestor.isEmpty()) {
 371                     distributeFrom(spaceRequested, availableList);
 372                 }
 373 
 374                 // Only for resizing.  We should have all the panel areas
 375                 // available computed.  We can total them up and see
 376                 // how much space we have left or went over and redistribute.
 377                 if (resize) {
 378                     double total = 0;
 379                     for (Content c: contentRegions) {
 380                         if (c.isResizableWithParent()) {
 381                             total += c.getArea();
 382                         } else {
 383                             total += c.getResizableWithParentArea();






 384                         }
 385                     }
 386                     total += (dividerWidth * contentDividers.size());
 387                     if (total < (horizontal ? w : h)) {
 388                         extraSpace += ((horizontal ? w : h) - total);
 389                         distributeTo(storageList, extraSpace);

 390                     } else {
 391                         spaceRequested += (total - (horizontal ? w : h));
 392                         distributeFrom(spaceRequested, storageList);


 393                     }


 394                 }
 395             }
 396 
 397             setupContentAndDividerForLayout();
 398 
 399             // Check the bounds of every panel
 400             boolean passed = true;
 401             for (Content c: contentRegions) {
 402                 double max = horizontal ? c.maxWidth(-1) : c.maxHeight(-1);
 403                 double min = horizontal ? c.minWidth(-1) : c.minHeight(-1);
 404                 if (c.getArea() < min || c.getArea() > max) {
 405                     passed = false;
 406                     break;
 407                 }





 408             }
 409             if (passed) {
 410                 break;
 411             }

 412         }
 413 
 414         layoutDividersAndContent(w, h);
 415         resize = false;



 416     }
 417 
 418     /** {@inheritDoc} */
 419     @Override protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
 420         double minWidth = 0;
 421         double maxMinWidth = 0;
 422         for (Content c: contentRegions) {
 423             minWidth += c.minWidth(-1);
 424             maxMinWidth = Math.max(maxMinWidth, c.minWidth(-1));




















 425         }
 426         for (ContentDivider d: contentDividers) {
 427             minWidth += d.prefWidth(-1);



 428         }
 429         if (horizontal) {
 430             return minWidth + leftInset + rightInset;
 431         } else {
 432             return maxMinWidth + leftInset + rightInset;
 433         }

 434     }
 435 
 436     /** {@inheritDoc} */
 437     @Override protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
 438         double minHeight = 0;
 439         double maxMinHeight = 0;

 440         for (Content c: contentRegions) {
 441             minHeight += c.minHeight(-1);
 442             maxMinHeight = Math.max(maxMinHeight, c.minHeight(-1));
 443         }
 444         for (ContentDivider d: contentDividers) {
 445             minHeight += d.prefWidth(-1);
 446         }



 447         if (horizontal) {
 448             return maxMinHeight + topInset + bottomInset;
 449         } else {
 450             return minHeight + topInset + bottomInset;
 451         }
 452     }
 453 
 454     /** {@inheritDoc} */
 455     @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
 456         double prefWidth = 0;
 457         double prefMaxWidth = 0;
 458         for (Content c: contentRegions) {
 459             prefWidth += c.prefWidth(-1);
 460             prefMaxWidth = Math.max(prefMaxWidth, c.prefWidth(-1));
 461         }
 462         for (ContentDivider d: contentDividers) {
 463             prefWidth += d.prefWidth(-1);
 464         }
 465         if (horizontal) {
 466             return prefWidth + leftInset + rightInset;
 467         } else {
 468             return prefMaxWidth + leftInset + rightInset;





 469         }

 470     }
 471 
 472     /** {@inheritDoc} */
 473     @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
 474         double prefHeight = 0;
 475         double maxPrefHeight = 0;

 476         for (Content c: contentRegions) {
 477             prefHeight += c.prefHeight(-1);
 478             maxPrefHeight = Math.max(maxPrefHeight, c.prefHeight(-1));







 479         }
 480         for (ContentDivider d: contentDividers) {
 481             prefHeight += d.prefWidth(-1);
 482         }


 483         if (horizontal) {
 484             return maxPrefHeight + topInset + bottomInset;


 485         } else {
 486             return prefHeight + topInset + bottomInset;



 487         }
 488     }
 489 




 490 





 491 
 492     /***************************************************************************
 493      *                                                                         *
 494      * Private implementation                                                  *
 495      *                                                                         *
 496      **************************************************************************/
 497 
 498     private void addContent(int index, Node n) {
 499         Content c = new Content(n);
 500         contentRegions.add(index, c);
 501         getChildren().add(index, c);
 502     }
 503 
 504     private void removeContent(Node n) {


 505         for (Content c: contentRegions) {
 506             if (c.getContent().equals(n)) {
 507                 getChildren().remove(c);
 508                 contentRegions.remove(c);
 509                 break;
 510             }
 511         }
 512     }
 513 
 514     private void initializeContentListener() {
 515         getSkinnable().getItems().addListener((ListChangeListener<Node>) c -> {
 516             while (c.next()) {
 517                 if (c.wasPermutated() || c.wasUpdated()) {
 518                     /**
 519                      * the contents were either moved, or updated.
 520                      * rebuild the contents to re-sync
 521                      */
 522                     getChildren().clear();
 523                     contentRegions.clear();
 524                     int index = 0;
 525                     for (Node n : c.getList()) {
 526                         addContent(index++, n);
 527                     }
 528 








 529                 } else {
 530                     for (Node n : c.getRemoved()) {
 531                         removeContent(n);
 532                     }
 533 
 534                     int index = c.getFrom();
 535                     for (Node n : c.getAddedSubList()) {
 536                         addContent(index++, n);




 537                     }
 538                 }











 539             }
 540             // TODO there may be a more efficient way than rebuilding all the dividers
 541             // everytime the list changes.
 542             removeAllDividers();
 543             for (SplitPane.Divider d: getSkinnable().getDividers()) {
 544                 addDivider(d);


 545             }
 546         });
 547     }
 548           
 549     private void checkDividerPosition(ContentDivider divider, double newPos, double oldPos) {
 550         double dividerWidth = divider.prefWidth(-1);
 551         Content left = getLeft(divider);
 552         Content right = getRight(divider);
 553         double minLeft = left == null ? 0 : (horizontal) ? left.minWidth(-1) : left.minHeight(-1);
 554         double minRight = right == null ? 0 : (horizontal) ? right.minWidth(-1) : right.minHeight(-1);
 555         double maxLeft = left == null ? 0 :
 556             left.getContent() != null ? (horizontal) ? left.getContent().maxWidth(-1) : left.getContent().maxHeight(-1) : 0;
 557         double maxRight = right == null ? 0 :
 558             right.getContent() != null ? (horizontal) ? right.getContent().maxWidth(-1) : right.getContent().maxHeight(-1) : 0;                        
 559                
 560         double previousDividerPos = 0;
 561         double nextDividerPos = getSize();
 562         int index = contentDividers.indexOf(divider);
 563 
 564         if (index - 1 >= 0) {
 565             previousDividerPos = contentDividers.get(index - 1).getDividerPos();
 566             if (previousDividerPos == -1) {
 567                 // Get the divider position if it hasn't been initialized.
 568                 previousDividerPos = getAbsoluteDividerPos(contentDividers.get(index - 1));
 569             }








 570         }
 571         if (index + 1 < contentDividers.size()) {
 572             nextDividerPos = contentDividers.get(index + 1).getDividerPos();
 573             if (nextDividerPos == -1) {
 574                 // Get the divider position if it hasn't been initialized.
 575                 nextDividerPos = getAbsoluteDividerPos(contentDividers.get(index + 1));
 576             }
 577         }
 578         
 579         // Set the divider into the correct position by looking at the max and min content sizes.
 580         checkDividerPos = false;
 581         if (newPos > oldPos) {
 582             double max = previousDividerPos == 0 ? maxLeft : previousDividerPos + dividerWidth + maxLeft;
 583             double min = nextDividerPos - minRight - dividerWidth;                
 584             double stopPos = Math.min(max, min);
 585             if (newPos >= stopPos) {                
 586                 setAbsoluteDividerPos(divider, stopPos);
 587             } else {
 588                 double rightMax = nextDividerPos - maxRight - dividerWidth;                    
 589                 if (newPos <= rightMax) {
 590                     setAbsoluteDividerPos(divider, rightMax);
 591                 } else {                    
 592                     setAbsoluteDividerPos(divider, newPos);
 593                 }
 594             }             
 595         } else {
 596             double max = nextDividerPos - maxRight - dividerWidth;
 597             double min = previousDividerPos == 0 ? minLeft : previousDividerPos + minLeft + dividerWidth;
 598             double stopPos = Math.max(max, min);
 599             if (newPos <= stopPos) {
 600                 setAbsoluteDividerPos(divider, stopPos);
 601             } else {
 602                 double leftMax = previousDividerPos + maxLeft + dividerWidth;
 603                 if (newPos >= leftMax) {
 604                     setAbsoluteDividerPos(divider, leftMax);
 605                 } else {
 606                     setAbsoluteDividerPos(divider, newPos);
 607                 }
 608             }                
 609         }                    
 610         checkDividerPos = true;
 611     }
 612     
 613     private void addDivider(SplitPane.Divider d) {
 614         ContentDivider c = new ContentDivider(d);        
 615         c.setInitialPos(d.getPosition());
 616         c.setDividerPos(-1);
 617         ChangeListener<Number> posPropertyListener = new PosPropertyListener(c);
 618         c.setPosPropertyListener(posPropertyListener);
 619         d.positionProperty().addListener(posPropertyListener);
 620         initializeDivderEventHandlers(c);
 621         contentDividers.add(c);
 622         getChildren().add(c);
 623     }
 624 
 625     private void removeAllDividers() {
 626         ListIterator<ContentDivider> dividers = contentDividers.listIterator();
 627         while (dividers.hasNext()) {
 628             ContentDivider c = dividers.next();
 629             getChildren().remove(c);
 630             c.getDivider().positionProperty().removeListener(c.getPosPropertyListener());
 631             dividers.remove();




 632         }
 633         lastDividerUpdate = 0;



 634     }
 635 
 636     private void initializeDivderEventHandlers(final ContentDivider divider) {
 637         // TODO: do we need to consume all mouse events?
 638         // they only bubble to the skin which consumes them by default
 639         divider.addEventHandler(MouseEvent.ANY, event -> {
 640             event.consume();
 641         });
 642 
 643         divider.setOnMousePressed(e -> {
 644             if (horizontal) {
 645                 divider.setInitialPos(divider.getDividerPos());
 646                 divider.setPressPos(e.getSceneX());
 647                 divider.setPressPos(getSkinnable().getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT
 648                         ? getSkinnable().getWidth() - e.getSceneX() : e.getSceneX());


 649             } else {
 650                 divider.setInitialPos(divider.getDividerPos());
 651                 divider.setPressPos(e.getSceneY());





 652             }
 653             e.consume();
 654         });
 655 
 656         divider.setOnMouseDragged(e -> {
 657             double delta = 0;
 658             if (horizontal) {
 659                 delta = getSkinnable().getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT
 660                         ? getSkinnable().getWidth() - e.getSceneX() : e.getSceneX();
 661             } else {
 662                 delta = e.getSceneY();
 663             }
 664             delta -= divider.getPressPos();
 665             setAndCheckAbsoluteDividerPos(divider, Math.ceil(divider.getInitialPos() + delta));
 666             e.consume();
 667         });
 668     }
 669 
 670     private Content getLeft(ContentDivider d) {
 671         int index = contentDividers.indexOf(d);
 672         if (index != -1) {
 673             return contentRegions.get(index);
 674         }
 675         return null;
 676     }
 677 
 678     private Content getRight(ContentDivider d) {
 679         int index = contentDividers.indexOf(d);
 680         if (index != -1) {
 681             return contentRegions.get(index + 1);
 682         }
 683         return null;







 684     }
 685 
 686     // Value is the left edge of the divider
 687     private void setAbsoluteDividerPos(ContentDivider divider, double value) {
 688         if (getSkinnable().getWidth() > 0 && getSkinnable().getHeight() > 0 && divider != null) {
 689             SplitPane.Divider paneDivider = divider.getDivider();
 690             divider.setDividerPos(value);
 691             double size = getSize();
 692             if (size != 0) {
 693                 // Adjust the position to the center of the
 694                 // divider and convert its position to a percentage.
 695                 double pos = value + divider.prefWidth(-1)/2;
 696                 paneDivider.setPosition(pos / size);
 697             } else {
 698                 paneDivider.setPosition(0);
 699             }



 700         }
 701     }
 702    
 703     // Updates the divider with the SplitPane.Divider's position
 704     // The value updated to SplitPane.Divider will be the center of the divider.
 705     // The returned position will be the left edge of the divider
 706     private double getAbsoluteDividerPos(ContentDivider divider) {
 707         if (getSkinnable().getWidth() > 0 && getSkinnable().getHeight() > 0 && divider != null) {
 708             SplitPane.Divider paneDivider = divider.getDivider();
 709             double newPos = posToDividerPos(divider, paneDivider.getPosition());
 710             divider.setDividerPos(newPos);
 711             return newPos;
 712         }
 713         return 0;
 714     }
 715 
 716     // Returns the left edge of the divider at pos
 717     // Pos is the percentage location from SplitPane.Divider.
 718     private double posToDividerPos(ContentDivider divider, double pos) {
 719         double newPos = getSize() * pos;
 720         if (pos == 1) {                
 721             newPos -= divider.prefWidth(-1);
 722         } else {
 723             newPos -= divider.prefWidth(-1)/2;




 724         }       
 725         return Math.round(newPos);                    



 726     }
 727     
 728     private double totalMinSize() {
 729         double dividerWidth = !contentDividers.isEmpty() ? contentDividers.size() * contentDividers.get(0).prefWidth(-1) : 0;
 730         double minSize = 0;
 731         for (Content c: contentRegions) {
 732             if (horizontal) {
 733                 minSize += c.minWidth(-1);
 734             } else {
 735                 minSize += c.minHeight(-1);
 736             }



 737         }
 738         return minSize + dividerWidth;
 739     }
 740 
 741     private double getSize() {
 742         final SplitPane s = getSkinnable();
 743         double size = totalMinSize();
 744         if (horizontal) {
 745             if (s.getWidth() > size) {
 746                 size = s.getWidth() - snappedLeftInset() - snappedRightInset();
 747             }





 748         } else {
 749             if (s.getHeight() > size) {
 750                 size = s.getHeight() - snappedTopInset() - snappedBottomInset();
 751             }
 752         }
 753         return size;
 754     }
 755 
 756     // Evenly distribute the size to the available list.
 757     // size is the amount to distribute.
 758     private double distributeTo(List<Content> available, double size) {
 759         if (available.isEmpty()) {
 760             return size;






 761         }
 762                 
 763         size = snapSize(size);
 764         int portion = (int)(size)/available.size();
 765         int remainder;
 766 
 767         while (size > 0 && !available.isEmpty()) {
 768             Iterator<Content> i = available.iterator();
 769             while (i.hasNext()) {
 770                 Content c = i.next();
 771                 double max = Math.min((horizontal ? c.maxWidth(-1) : c.maxHeight(-1)), Double.MAX_VALUE);
 772                 double min = horizontal ? c.minWidth(-1) : c.minHeight(-1);
 773 
 774                 // We have too much space
 775                 if (c.getArea() >= max) {
 776                     c.setAvailable(c.getArea() - min);
 777                     i.remove();
 778                     continue;
 779                 }
 780                 // Not enough space
 781                 if (portion >= (max - c.getArea())) {
 782                     size -= (max - c.getArea());
 783                     c.setArea(max);
 784                     c.setAvailable(max - min);
 785                     i.remove();
 786                 } else {
 787                     // Enough space
 788                     c.setArea(c.getArea() + portion);
 789                     c.setAvailable(c.getArea() - min);
 790                     size -= portion;
 791                 }
 792                 if ((int)size == 0) {
 793                     return size;
 794                 }
 795             }
 796             if (available.isEmpty()) {
 797                 // We reached the max size for everything just return
 798                 return size;
 799             }
 800             portion = (int)(size)/available.size();
 801             remainder = (int)(size)%available.size();
 802             if (portion == 0 && remainder != 0) {
 803                 portion = remainder;
 804                 remainder = 0;
 805             }
 806         }
 807         return size;
 808     }
 809 
 810     // Evenly distribute the size from the available list.
 811     // size is the amount to distribute.
 812     private double distributeFrom(double size, List<Content> available) {
 813         if (available.isEmpty()) {
 814             return size;
 815         }
 816         
 817         size = snapSize(size);
 818         int portion = (int)(size)/available.size();
 819         int remainder;
 820 
 821         while (size > 0 && !available.isEmpty()) {
 822             Iterator<Content> i = available.iterator();
 823             while (i.hasNext()) {
 824                 Content c = i.next();
 825                 //not enough space taking available and setting min
 826                 if (portion >= c.getAvailable()) {
 827                     c.setArea(c.getArea() - c.getAvailable()); // Min size
 828                     size -= c.getAvailable();
 829                     c.setAvailable(0);
 830                     i.remove();
 831                 } else {
 832                     //enough space
 833                     c.setArea(c.getArea() - portion);
 834                     c.setAvailable(c.getAvailable() - portion);
 835                     size -= portion;
 836                 }
 837                 if ((int)size == 0) {
 838                     return size;
 839                 }


 840             }
 841             if (available.isEmpty()) {
 842                 // We reached the min size for everything just return
 843                 return size;
 844             }
 845             portion = (int)(size)/available.size();
 846             remainder = (int)(size)%available.size();
 847             if (portion == 0 && remainder != 0) {
 848                 portion = remainder;
 849                 remainder = 0;
 850             }
 851         }
 852         return size;



 853     }
 854 
 855     private void setupContentAndDividerForLayout() {
 856         // Set all the value to prepare for layout
 857         double dividerWidth = contentDividers.isEmpty() ? 0 : contentDividers.get(0).prefWidth(-1);
 858         double startX = 0;
 859         double startY = 0;
 860         for (Content c: contentRegions) {
 861             if (resize && !c.isResizableWithParent()) {
 862                 c.setArea(c.getResizableWithParentArea());



 863             }
 864             
 865             c.setX(startX);
 866             c.setY(startY);
 867             if (horizontal) {
 868                 startX += (c.getArea() + dividerWidth);
 869             } else {
 870                 startY += (c.getArea() + dividerWidth);
 871             }
 872         }
 873 
 874         startX = 0;
 875         startY = 0;
 876         // The dividers are already in the correct positions.  Disable
 877         // checking the divider positions.
 878         checkDividerPos = false;
 879         for (int i = 0; i < contentDividers.size(); i++) {
 880             ContentDivider d = contentDividers.get(i);



 881             if (horizontal) {
 882                 startX += getLeft(d).getArea() + (i == 0 ? 0 : dividerWidth);
 883             } else {
 884                 startY += getLeft(d).getArea() + (i == 0 ? 0 : dividerWidth);
 885             }
 886             d.setX(startX);
 887             d.setY(startY);              
 888             setAbsoluteDividerPos(d, (horizontal ? d.getX() : d.getY()));
 889             d.posExplicit = false;
 890         }
 891         checkDividerPos = true;
 892     }
 893 
 894     private void layoutDividersAndContent(double width, double height) {
 895         final double paddingX = snappedLeftInset();
 896         final double paddingY = snappedTopInset();
 897         final double dividerWidth = contentDividers.isEmpty() ? 0 : contentDividers.get(0).prefWidth(-1);
 898 
 899         for (Content c: contentRegions) {
 900 //            System.out.println("LAYOUT " + c.getId() + " PANELS X " + c.getX() + " Y " + c.getY() + " W " + (horizontal ? c.getArea() : width) + " H " + (horizontal ? height : c.getArea()));
 901             if (horizontal) {
 902                 c.setClipSize(c.getArea(), height);
 903                 layoutInArea(c, c.getX() + paddingX, c.getY() + paddingY, c.getArea(), height,
 904                     0/*baseline*/,HPos.CENTER, VPos.CENTER);
 905             } else {
 906                 c.setClipSize(width, c.getArea());
 907                 layoutInArea(c, c.getX() + paddingX, c.getY() + paddingY, width, c.getArea(),
 908                     0/*baseline*/,HPos.CENTER, VPos.CENTER);
 909             }


 910         }
 911         for (ContentDivider c: contentDividers) {
 912 //            System.out.println("LAYOUT DIVIDERS X " + c.getX() + " Y " + c.getY() + " W " + (horizontal ? dividerWidth : width) + " H " + (horizontal ? height : dividerWidth));
 913             if (horizontal) {
 914                 c.resize(dividerWidth, height);
 915                 positionInArea(c, c.getX() + paddingX, c.getY() + paddingY, dividerWidth, height,
 916                     /*baseline ignored*/0, HPos.CENTER, VPos.CENTER);
 917             } else {
 918                 c.resize(width, dividerWidth);                
 919                 positionInArea(c, c.getX() + paddingX, c.getY() + paddingY, width, dividerWidth,
 920                     /*baseline ignored*/0, HPos.CENTER, VPos.CENTER);
 921             }
 922         }
 923     }
 924 
 925     private double previousSize = -1;
 926     private int lastDividerUpdate = 0;
 927     private boolean resize = false;
 928     private boolean checkDividerPos = true;
 929 
 930     private void setAndCheckAbsoluteDividerPos(ContentDivider divider, double value) {
 931         double oldPos = divider.getDividerPos();
 932         setAbsoluteDividerPos(divider, value);
 933         checkDividerPosition(divider, value, oldPos);
 934     }
 935 
 936 
 937 
 938     /***************************************************************************
 939      *                                                                         *
 940      * Support classes                                                         *
 941      *                                                                         *
 942      **************************************************************************/
 943 
 944     // This listener is to be removed from 'removed' dividers and added to 'added' dividers
 945     class PosPropertyListener implements ChangeListener<Number> {
 946         ContentDivider divider;
 947 
 948         public PosPropertyListener(ContentDivider divider) {
 949             this.divider = divider;
 950         }
 951 
 952         @Override public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
 953             if (checkDividerPos) {
 954                 // When checking is enforced, we know that the position was set explicitly
 955                 divider.posExplicit = true;
 956             }
 957             getSkinnable().requestLayout();
 958         }
 959     }
 960 






















 961 
 962     class ContentDivider extends StackPane {
 963         private double initialPos;
 964         private double dividerPos;
 965         private double pressPos;
 966         private SplitPane.Divider d;
 967         private StackPane grabber;
 968         private double x;
 969         private double y;
 970         private boolean posExplicit;
 971         private ChangeListener<Number> listener;
 972 
 973         public ContentDivider(SplitPane.Divider d) {
 974             getStyleClass().setAll("split-pane-divider");
 975 
 976             this.d = d;
 977             this.initialPos = 0;
 978             this.dividerPos = 0;
 979             this.pressPos = 0;
 980