modules/graphics/src/main/java/javafx/scene/layout/GridPane.java

Print this page




1187             for (int i = 0; i < managed.size(); ++i) {
1188                 final Orientation b = managed.get(i).getContentBias();
1189                 if (b != null) {
1190                     bias = b;
1191                     if (b == Orientation.HORIZONTAL) {
1192                         break;
1193                     }
1194                 }
1195             }
1196 
1197             metricsDirty = false;
1198         }
1199     }
1200 
1201     @Override protected double computeMinWidth(double height) {
1202         computeGridMetrics();
1203         performingLayout = true;
1204         try {
1205             final double[] heights = height == -1 ? null : computeHeightsToFit(height).asArray();
1206 
1207             return snapSpace(getInsets().getLeft()) +
1208                     computeMinWidths(heights).computeTotalWithMultiSize() +
1209                     snapSpace(getInsets().getRight());
1210         } finally {
1211             performingLayout = false;
1212         }
1213 
1214     }
1215 
1216     @Override protected double computeMinHeight(double width) {
1217         computeGridMetrics();
1218         performingLayout = true;
1219         try {
1220             final double[] widths = width == -1 ? null : computeWidthsToFit(width).asArray();
1221 
1222             return snapSpace(getInsets().getTop()) +
1223                     computeMinHeights(widths).computeTotalWithMultiSize() +
1224                     snapSpace(getInsets().getBottom());
1225         } finally {
1226             performingLayout = false;
1227         }
1228     }
1229 
1230     @Override protected double computePrefWidth(double height) {
1231         computeGridMetrics();
1232         performingLayout = true;
1233         try {
1234             final double[] heights = height == -1 ? null : computeHeightsToFit(height).asArray();
1235 
1236             return snapSpace(getInsets().getLeft()) +
1237                     computePrefWidths(heights).computeTotalWithMultiSize() +
1238                     snapSpace(getInsets().getRight());
1239         } finally {
1240             performingLayout = false;
1241         }
1242     }
1243 
1244     @Override protected double computePrefHeight(double width) {
1245         computeGridMetrics();
1246         performingLayout = true;
1247         try {
1248             final double[] widths = width == -1 ? null : computeWidthsToFit(width).asArray();
1249 
1250             return snapSpace(getInsets().getTop()) +
1251                     computePrefHeights(widths).computeTotalWithMultiSize() +
1252                     snapSpace(getInsets().getBottom());
1253         } finally {
1254             performingLayout = false;
1255         }
1256     }
1257 
1258     private VPos getRowValignment(int rowIndex) {
1259         if (rowIndex < getRowConstraints().size()) {
1260             RowConstraints constraints = getRowConstraints().get(rowIndex);
1261             if (constraints.getValignment() != null) {
1262                 return constraints.getValignment();
1263             }
1264         }
1265         return VPos.CENTER;
1266     }
1267 
1268     private HPos getColumnHalignment(int columnIndex) {
1269         if (columnIndex < getColumnConstraints().size()) {
1270             ColumnConstraints constraints = getColumnConstraints().get(columnIndex);
1271             if (constraints.getHalignment() != null) {
1272                 return constraints.getHalignment();


1345         if (getNodeColumnSpan(child) == 1) {
1346             return widths[getNodeColumnIndex(child)];
1347         } else {
1348             double total = 0;
1349             for (int i = getNodeColumnIndex(child), last = getNodeColumnEndConvertRemaining(child); i <= last; ++i) {
1350                 total += widths[i];
1351             }
1352             return total;
1353         }
1354     }
1355 
1356     private CompositeSize computeMaxHeights() {
1357         if (rowMaxHeight == null) {
1358             rowMaxHeight = createCompositeRows(Double.MAX_VALUE); // Do not restrict the row (to allow grow). The
1359                                                                   // Nodes will be restricted to their computed size
1360                                                                   // in Region.layoutInArea call
1361             final ObservableList<RowConstraints> rowConstr = getRowConstraints();
1362             CompositeSize prefHeights = null;
1363             for (int i = 0; i < rowConstr.size(); ++i) {
1364                 final RowConstraints curConstraint = rowConstr.get(i);
1365                 double maxRowHeight = snapSize(curConstraint.getMaxHeight());
1366                 if (maxRowHeight == USE_PREF_SIZE) {
1367                     if (prefHeights == null) {
1368                         prefHeights = computePrefHeights(null);
1369                     }
1370                     rowMaxHeight.setPresetSize(i, prefHeights.getSize(i));
1371                 } else if (maxRowHeight != USE_COMPUTED_SIZE) {
1372                     final double min = snapSize(curConstraint.getMinHeight());
1373                     if (min >= 0 ) {
1374                         rowMaxHeight.setPresetSize(i, boundedSize(min, maxRowHeight, maxRowHeight));
1375                     } else {
1376                         rowMaxHeight.setPresetSize(i, maxRowHeight);
1377                     }
1378                 }
1379             }
1380         }
1381         return rowMaxHeight;
1382     }
1383 
1384     private CompositeSize computePrefHeights(double[] widths) {
1385         CompositeSize result;
1386         if (widths == null) {
1387             if (rowPrefHeight != null) {
1388                 return rowPrefHeight;
1389             }
1390             rowPrefHeight = createCompositeRows(0);
1391             result = rowPrefHeight;
1392         } else {
1393             result = createCompositeRows(0);
1394         }
1395 
1396         final ObservableList<RowConstraints> rowConstr = getRowConstraints();
1397         for (int i = 0; i < rowConstr.size(); ++i) {
1398             final RowConstraints curConstraint = rowConstr.get(i);
1399             double prefRowHeight = snapSize(curConstraint.getPrefHeight());
1400             final double min = snapSize(curConstraint.getMinHeight());
1401             if (prefRowHeight != USE_COMPUTED_SIZE) {
1402                 final double max = snapSize(curConstraint.getMaxHeight());
1403                 if (min >= 0 || max >= 0) {
1404                     result.setPresetSize(i, boundedSize(min < 0 ? 0 : min,
1405                             prefRowHeight,
1406                             max < 0 ? Double.POSITIVE_INFINITY : max));
1407                 } else {
1408                     result.setPresetSize(i, prefRowHeight);
1409                 }
1410             } else if (min > 0){
1411                 result.setSize(i, min);
1412             }
1413         }
1414         List<Node> managed = getManagedChildren();
1415         for (int i = 0, size = managed.size(); i < size; i++) {
1416             Node child = managed.get(i);
1417             int start = getNodeRowIndex(child);
1418             int end = getNodeRowEndConvertRemaining(child);
1419             double childPrefAreaHeight = computeChildPrefAreaHeight(child, isNodePositionedByBaseline(child) ? rowPrefBaselineComplement[start] : -1, getMargin(child),
1420                     widths == null ? -1 : getTotalWidthOfNodeColumns(child, widths));
1421             if (start == end && !result.isPreset(start)) {
1422                 double min = getRowMinHeight(start);


1427             }
1428         }
1429         return result;
1430     }
1431 
1432     private CompositeSize computeMinHeights(double[] widths) {
1433         CompositeSize result;
1434         if (widths == null) {
1435             if (rowMinHeight != null) {
1436                 return rowMinHeight;
1437             }
1438             rowMinHeight = createCompositeRows(0);
1439             result = rowMinHeight;
1440         } else {
1441             result = createCompositeRows(0);
1442         }
1443 
1444         final ObservableList<RowConstraints> rowConstr = getRowConstraints();
1445         CompositeSize prefHeights = null;
1446         for (int i = 0; i < rowConstr.size(); ++i) {
1447             double minRowHeight = snapSize(rowConstr.get(i).getMinHeight());
1448             if (minRowHeight == USE_PREF_SIZE) {
1449                 if (prefHeights == null) {
1450                     prefHeights = computePrefHeights(widths);
1451                 }
1452                 result.setPresetSize(i, prefHeights.getSize(i));
1453             } else if (minRowHeight != USE_COMPUTED_SIZE) {
1454                 result.setPresetSize(i, minRowHeight);
1455             }
1456         }
1457         List<Node> managed = getManagedChildren();
1458         for (int i = 0, size = managed.size(); i < size; i++) {
1459             Node child = managed.get(i);
1460             int start = getNodeRowIndex(child);
1461             int end = getNodeRowEndConvertRemaining(child);
1462             double childMinAreaHeight = computeChildMinAreaHeight(child, isNodePositionedByBaseline(child) ? rowMinBaselineComplement[start] : -1, getMargin(child),
1463                              widths == null ? -1 : getTotalWidthOfNodeColumns(child, widths));
1464             if (start == end && !result.isPreset(start)) {
1465                 result.setMaxSize(start, childMinAreaHeight);
1466             } else if (start != end){
1467                 result.setMaxMultiSize(start, end + 1, childMinAreaHeight);


1474         if (getNodeRowSpan(child) == 1) {
1475             return heights[getNodeRowIndex(child)];
1476         } else {
1477             double total = 0;
1478             for (int i = getNodeRowIndex(child), last = getNodeRowEndConvertRemaining(child); i <= last; ++i) {
1479                 total += heights[i];
1480             }
1481             return total;
1482         }
1483     }
1484 
1485     private CompositeSize computeMaxWidths() {
1486         if (columnMaxWidth == null) {
1487             columnMaxWidth = createCompositeColumns(Double.MAX_VALUE);// Do not restrict the column (to allow grow). The
1488                                                                       // Nodes will be restricted to their computed size
1489                                                                       // in Region.layoutInArea call
1490             final ObservableList<ColumnConstraints> columnConstr = getColumnConstraints();
1491             CompositeSize prefWidths = null;
1492             for (int i = 0; i < columnConstr.size(); ++i) {
1493                 final ColumnConstraints curConstraint = columnConstr.get(i);
1494                 double maxColumnWidth = snapSize(curConstraint.getMaxWidth());
1495                 if (maxColumnWidth == USE_PREF_SIZE) {
1496                     if (prefWidths == null) {
1497                         prefWidths = computePrefWidths(null);
1498                     }
1499                     columnMaxWidth.setPresetSize(i, prefWidths.getSize(i));
1500                 } else if (maxColumnWidth != USE_COMPUTED_SIZE) {
1501                     final double min = snapSize(curConstraint.getMinWidth());
1502                     if (min >= 0) {
1503                         columnMaxWidth.setPresetSize(i, boundedSize(min, maxColumnWidth, maxColumnWidth));
1504                     } else {
1505                         columnMaxWidth.setPresetSize(i, maxColumnWidth);
1506                     }
1507                 }
1508             }
1509         }
1510         return columnMaxWidth;
1511     }
1512 
1513     private CompositeSize computePrefWidths(double[] heights) {
1514         CompositeSize result;
1515         if (heights == null) {
1516             if (columnPrefWidth != null) {
1517                 return columnPrefWidth;
1518             }
1519             columnPrefWidth = createCompositeColumns(0);
1520             result = columnPrefWidth;
1521         } else {
1522             result = createCompositeColumns(0);
1523         }
1524 
1525         final ObservableList<ColumnConstraints> columnConstr = getColumnConstraints();
1526         for (int i = 0; i < columnConstr.size(); ++i) {
1527             final ColumnConstraints curConstraint = columnConstr.get(i);
1528             double prefColumnWidth = snapSize(curConstraint.getPrefWidth());
1529             final double min = snapSize(curConstraint.getMinWidth());
1530             if (prefColumnWidth != USE_COMPUTED_SIZE) {
1531                 final double max = snapSize(curConstraint.getMaxWidth());
1532                 if (min >= 0 || max >= 0) {
1533                     result.setPresetSize(i, boundedSize(min < 0 ? 0 : min,
1534                             prefColumnWidth,
1535                             max < 0 ? Double.POSITIVE_INFINITY : max));
1536                 } else {
1537                     result.setPresetSize(i, prefColumnWidth);
1538                 }
1539             } else if (min > 0){
1540                 result.setSize(i, min);
1541             }
1542         }
1543         List<Node> managed = getManagedChildren();
1544         for (int i = 0, size = managed.size(); i < size; i++) {
1545             Node child = managed.get(i);
1546             int start = getNodeColumnIndex(child);
1547             int end = getNodeColumnEndConvertRemaining(child);
1548             if (start == end && !result.isPreset(start)) {
1549                 double min = getColumnMinWidth(start);
1550                 double max = getColumnMaxWidth(start);
1551                 result.setMaxSize(start, boundedSize(min < 0 ? 0 : min, computeChildPrefAreaWidth(child,


1559             }
1560         }
1561         return result;
1562     }
1563 
1564     private CompositeSize computeMinWidths(double[] heights) {
1565         CompositeSize result;
1566         if (heights == null) {
1567             if (columnMinWidth != null) {
1568                 return columnMinWidth;
1569             }
1570             columnMinWidth = createCompositeColumns(0);
1571             result = columnMinWidth;
1572         } else {
1573             result = createCompositeColumns(0);
1574         }
1575 
1576         final ObservableList<ColumnConstraints> columnConstr = getColumnConstraints();
1577         CompositeSize prefWidths = null;
1578         for (int i = 0; i < columnConstr.size(); ++i) {
1579             double minColumnWidth = snapSize(columnConstr.get(i).getMinWidth());
1580             if (minColumnWidth == USE_PREF_SIZE) {
1581                 if (prefWidths == null) {
1582                     prefWidths = computePrefWidths(heights);
1583                 }
1584                 result.setPresetSize(i, prefWidths.getSize(i));
1585             } else if (minColumnWidth != USE_COMPUTED_SIZE) {
1586                 result.setPresetSize(i, minColumnWidth);
1587             }
1588         }
1589         List<Node> managed = getManagedChildren();
1590         for (int i = 0, size = managed.size(); i < size; i++) {
1591             Node child = managed.get(i);
1592             int start = getNodeColumnIndex(child);
1593             int end = getNodeColumnEndConvertRemaining(child);
1594             if (start == end && !result.isPreset(start)) {
1595                 result.setMaxSize(start, computeChildMinAreaWidth(child, getBaselineComplementForChild(child),
1596                         getMargin(child),
1597                         heights == null ? -1 : getTotalHeightOfNodeRows(child, heights),false));
1598             } else if (start != end){
1599                 result.setMaxMultiSize(start, end + 1, computeChildMinAreaWidth(child, getBaselineComplementForChild(child),


1646         // next layout pass.
1647         if (performingLayout) {
1648             return;
1649         } else if (metricsDirty) {
1650             super.requestLayout();
1651             return;
1652         }
1653         metricsDirty = true;
1654         bias = null;
1655         rowGrow = null;
1656         rowMinHeight = rowPrefHeight = rowMaxHeight = null;
1657         columnGrow = null;
1658         columnMinWidth = columnPrefWidth = columnMaxWidth = null;
1659         rowMinBaselineComplement = rowPrefBaselineComplement = rowMaxBaselineComplement = null;
1660         super.requestLayout();
1661     }
1662 
1663     @Override protected void layoutChildren() {
1664         performingLayout = true;
1665         try {
1666             final double snaphgap = snapSpace(getHgap());
1667             final double snapvgap = snapSpace(getVgap());
1668             final double top = snapSpace(getInsets().getTop());
1669             final double bottom = snapSpace(getInsets().getBottom());
1670             final double left = snapSpace(getInsets().getLeft());
1671             final double right = snapSpace(getInsets().getRight());
1672 
1673             final double width = getWidth();
1674             final double height = getHeight();
1675             final double contentHeight = height - top - bottom;
1676             final double contentWidth = width - left - right;
1677             double columnTotal;
1678             double rowTotal;
1679             computeGridMetrics();
1680 
1681             Orientation contentBias = getContentBias();
1682             CompositeSize heights;
1683             final CompositeSize widths;
1684             if (contentBias == null) {
1685                 heights = (CompositeSize) computePrefHeights(null).clone();
1686                 widths = (CompositeSize) computePrefWidths(null).clone();
1687                 rowTotal = adjustRowHeights(heights, height);
1688                 columnTotal = adjustColumnWidths(widths, width);
1689             } else if (contentBias == Orientation.HORIZONTAL) {
1690                 widths = (CompositeSize) computePrefWidths(null).clone();
1691                 columnTotal = adjustColumnWidths(widths, width);


1782                     baselineOffset = baselineOffsets[rowIndex];
1783                 }
1784 
1785                 Insets margin = getMargin(child);
1786                 layoutInArea(child, areaX, areaY, areaW, areaH,
1787                         baselineOffset,
1788                         margin,
1789                         fillWidth, fillHeight,
1790                         halign, valign);
1791             }
1792             layoutGridLines(widths, heights, x, y, rowTotal, columnTotal);
1793             currentHeights = heights;
1794             currentWidths = widths;
1795         } finally {
1796             performingLayout = false;
1797         }
1798     }
1799 
1800     private double adjustRowHeights(final CompositeSize heights, double height) {
1801         assert(height != -1);
1802         final double snapvgap = snapSpace(getVgap());
1803         final double top = snapSpace(getInsets().getTop());
1804         final double bottom = snapSpace(getInsets().getBottom());
1805         final double vgaps = snapvgap * (getNumberOfRows() - 1);
1806         final double contentHeight = height - top - bottom;
1807 
1808         // if there are percentage rows, give them their percentages first
1809         if (rowPercentTotal > 0) {
1810             double remainder = 0;
1811             for (int i = 0; i < rowPercentHeight.length; i++) {
1812                 if (rowPercentHeight[i] >= 0) {
1813                     double size = (contentHeight - vgaps) * (rowPercentHeight[i]/100);
1814                     double floor = Math.floor(size);
1815                     remainder += size - floor;
1816 
1817                     // snap size to integer boundary based on the computed remainder as we loop through the rows.
1818                     size = floor;
1819                     if (remainder >= 0.5) {
1820                         size++;
1821                         remainder = (-1.0) + remainder;
1822                     }
1823                     heights.setSize(i, size);
1824                 }


1987         double available = extraHeight; // will be negative in shrinking case
1988         boolean handleRemainder = false;
1989         double portion = 0;
1990 
1991         // RT-25684: We have to be careful that when subtracting change
1992         // that we don't jump right past 0 - this leads to an infinite
1993         // loop
1994         final boolean wasPositive = available >= 0.0;
1995         boolean isPositive = wasPositive;
1996 
1997         CompositeSize limitSize = shrinking? computeMinHeights(null) :
1998                             computeMaxHeights();
1999         while (available != 0 && wasPositive == isPositive && adjusting.size() > 0) {
2000             if (!handleRemainder) {
2001                 portion = available > 0 ? Math.floor(available / adjusting.size()) :
2002                         Math.ceil(available / adjusting.size()); // negative in shrinking case
2003             }
2004             if (portion != 0) {
2005                 for (Iterator<Integer> i = adjusting.iterator(); i.hasNext();) {
2006                     final int index = i.next();
2007                     double limit = snapSpace(limitSize.getProportionalMinOrMaxSize(index, shrinking))
2008                             - heights.getSize(index); // negative in shrinking case
2009                     if (shrinking && limit > 0
2010                             || !shrinking && limit < 0) { // Limit completely if current size
2011                                                  // (originally based on preferred) already passed the computed limit
2012                         limit = 0;
2013                     }
2014                     final double change = Math.abs(limit) <= Math.abs(portion)? limit : portion;
2015                     heights.addSize(index, change);
2016                     available -= change;
2017                     isPositive = available >= 0.0;
2018                     if (Math.abs(change) < Math.abs(portion)) {
2019                         i.remove();
2020                     }
2021                     if (available == 0) {
2022                         break;
2023                     }
2024                 }
2025              } else {
2026                 // Handle the remainder
2027                 portion = (int)(available) % adjusting.size();
2028                 if (portion == 0) {
2029                     break;
2030                 } else {
2031                     // We have a remainder evenly distribute it.
2032                     portion = shrinking ? -1 : 1;
2033                     handleRemainder = true;
2034                 }
2035             }
2036         }
2037 
2038         return available; // might be negative in shrinking case
2039     }
2040 
2041     private double adjustColumnWidths(final CompositeSize widths, double width) {
2042         assert(width != -1);
2043         final double snaphgap = snapSpace(getHgap());
2044         final double left = snapSpace(getInsets().getLeft());
2045         final double right = snapSpace(getInsets().getRight());
2046         final double hgaps = snaphgap * (getNumberOfColumns() - 1);
2047         final double contentWidth = width - left - right;
2048 
2049         // if there are percentage rows, give them their percentages first
2050         if (columnPercentTotal > 0) {
2051             double remainder = 0;
2052             for (int i = 0; i < columnPercentWidth.length; i++) {
2053                 if (columnPercentWidth[i] >= 0) {
2054                     double size = (contentWidth - hgaps) * (columnPercentWidth[i]/100);
2055                     double floor = Math.floor(size);
2056                     remainder += size - floor;
2057 
2058                     // snap size to integer boundary based on the computed remainder as we loop through the columns.
2059                     size = floor;
2060                     if (remainder >= 0.5) {
2061                         size++;
2062                         remainder = (-1.0) + remainder;
2063                     }
2064                     widths.setSize(i, size);
2065                 }


2231         double available = extraWidth; // will be negative in shrinking case
2232         boolean handleRemainder = false;
2233         double portion = 0;
2234 
2235         // RT-25684: We have to be careful that when subtracting change
2236         // that we don't jump right past 0 - this leads to an infinite
2237         // loop
2238         final boolean wasPositive = available >= 0.0;
2239         boolean isPositive = wasPositive;
2240 
2241         CompositeSize limitSize = shrinking? computeMinWidths(null) :
2242                             computeMaxWidths();
2243         while (available != 0 && wasPositive == isPositive && adjusting.size() > 0) {
2244             if (!handleRemainder) {
2245                 portion = available > 0 ? Math.floor(available / adjusting.size()) :
2246                         Math.ceil(available / adjusting.size()); // negative in shrinking case
2247             }
2248             if (portion != 0) {
2249                 for (Iterator<Integer> i = adjusting.iterator(); i.hasNext();) {
2250                     final int index = i.next();
2251                     double limit = snapSpace(limitSize.getProportionalMinOrMaxSize(index, shrinking))
2252                             - widths.getSize(index); // negative in shrinking case
2253                     if (shrinking && limit > 0
2254                             || !shrinking && limit < 0) { // Limit completely if current size
2255                                                  // (originally based on preferred) already passed the computed limit
2256                         limit = 0;
2257                     }
2258                     final double change = Math.abs(limit) <= Math.abs(portion)? limit : portion;
2259                     widths.addSize(index, change);
2260                     available -= change;
2261                     isPositive = available >= 0.0;
2262                     if (Math.abs(change) < Math.abs(portion)) {
2263                         i.remove();
2264                     }
2265                     if (available == 0) {
2266                         break;
2267                     }
2268                 }
2269             } else {
2270                 // Handle the remainder
2271                 portion = (int)(available) % adjusting.size();
2272                 if (portion == 0) {
2273                     break;
2274                 } else {
2275                     // We have a remainder evenly distribute it.
2276                     portion = shrinking ? -1 : 1;
2277                     handleRemainder = true;
2278                 }
2279             }
2280         }
2281 
2282         return available; // might be negative in shrinking case
2283     }
2284 
2285     private void layoutGridLines(CompositeSize columnWidths, CompositeSize rowHeights, double x, double y, double columnHeight, double rowWidth) {
2286         if (!isGridLinesVisible()) {
2287             return;
2288         }
2289         if (!gridLines.getChildren().isEmpty()) {
2290             gridLines.getChildren().clear();
2291         }
2292         double hgap = snapSpace(getHgap());
2293         double vgap = snapSpace(getVgap());
2294 
2295         // create vertical lines
2296         double linex = x;
2297         double liney = y;
2298         for (int i = 0; i <= columnWidths.getLength(); i++) {
2299              gridLines.getChildren().add(createGridLine(linex, liney, linex, liney + columnHeight));
2300              if (i > 0 && i < columnWidths.getLength() && getHgap() != 0) {
2301                  linex += getHgap();
2302                  gridLines.getChildren().add(createGridLine(linex, liney, linex, liney + columnHeight));
2303              }
2304              if (i < columnWidths.getLength()) {
2305                  linex += columnWidths.getSize(i);
2306              }
2307         }
2308         // create horizontal lines
2309         linex = x;
2310         for (int i = 0; i <= rowHeights.getLength(); i++) {
2311             gridLines.getChildren().add(createGridLine(linex, liney, linex + rowWidth, liney));
2312             if (i > 0 && i < rowHeights.getLength() && getVgap() != 0) {
2313                 liney += getVgap();


2324          line.setStartX(startX);
2325          line.setStartY(startY);
2326          line.setEndX(endX);
2327          line.setEndY(endY);
2328          line.setStroke(GRID_LINE_COLOR);
2329          line.setStrokeDashOffset(GRID_LINE_DASH);
2330 
2331          return line;
2332     }
2333 
2334     /**
2335      * Returns a string representation of this {@code GridPane} object.
2336      * @return a string representation of this {@code GridPane} object.
2337      */
2338     @Override public String toString() {
2339         return "Grid hgap="+getHgap()+", vgap="+getVgap()+", alignment="+getAlignment();
2340     }
2341 
2342     private CompositeSize createCompositeRows(double initSize) {
2343         return new CompositeSize(getNumberOfRows(), rowPercentHeight, rowPercentTotal,
2344                 snapSpace(getVgap()), initSize);
2345     }
2346 
2347     private CompositeSize createCompositeColumns(double initSize) {
2348         return new CompositeSize(getNumberOfColumns(), columnPercentWidth, columnPercentTotal,
2349                 snapSpace(getHgap()), initSize);
2350     }
2351 
2352     private int getNodeRowEndConvertRemaining(Node child) {
2353         int rowSpan = getNodeRowSpan(child);
2354         return rowSpan != REMAINING? getNodeRowIndex(child) + rowSpan - 1 : getNumberOfRows() - 1;
2355     }
2356 
2357     private int getNodeColumnEndConvertRemaining(Node child) {
2358         int columnSpan = getNodeColumnSpan(child);
2359         return columnSpan != REMAINING? getNodeColumnIndex(child) + columnSpan - 1 : getNumberOfColumns() - 1;
2360     }
2361 
2362 
2363     // This methods are inteded to be used by GridPaneDesignInfo
2364     private CompositeSize currentHeights;
2365     private CompositeSize currentWidths;
2366 
2367     double[][] getGrid() {
2368         if (currentHeights == null || currentWidths == null) {
2369             return null;


2723         int nColumns = this.getColumnConstraints().size();
2724         for (int i = 0; i < this.getChildren().size(); i++) {
2725             Node child = this.getChildren().get(i);
2726             if (child.isManaged()) {
2727                 int columnIndex = GridPane.getNodeColumnIndex(child);
2728                 int columnEnd = GridPane.getNodeColumnEnd(child);
2729                 nColumns = Math.max(nColumns, (columnEnd != GridPane.REMAINING? columnEnd : columnIndex) + 1);
2730             }
2731         }
2732         return nColumns;
2733     }
2734 
2735     /**
2736      * Copied from GridPaneDesignInfo for SceneBuilder.
2737      *
2738      * @treatAsPrivate implementation detail
2739      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
2740      */
2741     @Deprecated // SB-dependency: RT-33381 has been filed to track this
2742     public final Bounds impl_getCellBounds(int columnIndex, int rowIndex) {
2743         final double snaphgap = this.snapSpace(this.getHgap());
2744         final double snapvgap = this.snapSpace(this.getVgap());
2745         final double top = this.snapSpace(this.getInsets().getTop());
2746         final double right = this.snapSpace(this.getInsets().getRight());
2747         final double bottom = this.snapSpace(this.getInsets().getBottom());
2748         final double left = this.snapSpace(this.getInsets().getLeft());
2749         final double gridPaneHeight = this.snapSize(this.getHeight()) - (top + bottom);
2750         final double gridPaneWidth = this.snapSize(this.getWidth()) - (left + right);
2751 
2752         // Compute grid. Result contains two double arrays, first for columns, second for rows
2753         double[] columnWidths;
2754         double[] rowHeights;
2755 
2756         double[][] grid = this.getGrid();
2757         if (grid == null) {
2758             rowHeights = new double[] {0};
2759             rowIndex = 0;
2760             columnWidths = new double[] {0};
2761             columnIndex = 0;
2762         } else {
2763             columnWidths = grid[0];
2764             rowHeights = grid[1];
2765         }
2766 
2767         // Compute the total row height
2768         double rowTotal = 0;
2769         for (int i = 0; i < rowHeights.length; i++) {
2770             rowTotal += rowHeights[i];




1187             for (int i = 0; i < managed.size(); ++i) {
1188                 final Orientation b = managed.get(i).getContentBias();
1189                 if (b != null) {
1190                     bias = b;
1191                     if (b == Orientation.HORIZONTAL) {
1192                         break;
1193                     }
1194                 }
1195             }
1196 
1197             metricsDirty = false;
1198         }
1199     }
1200 
1201     @Override protected double computeMinWidth(double height) {
1202         computeGridMetrics();
1203         performingLayout = true;
1204         try {
1205             final double[] heights = height == -1 ? null : computeHeightsToFit(height).asArray();
1206 
1207             return snapSpaceX(getInsets().getLeft()) +
1208                     computeMinWidths(heights).computeTotalWithMultiSize() +
1209                     snapSpaceX(getInsets().getRight());
1210         } finally {
1211             performingLayout = false;
1212         }
1213 
1214     }
1215 
1216     @Override protected double computeMinHeight(double width) {
1217         computeGridMetrics();
1218         performingLayout = true;
1219         try {
1220             final double[] widths = width == -1 ? null : computeWidthsToFit(width).asArray();
1221 
1222             return snapSpaceY(getInsets().getTop()) +
1223                     computeMinHeights(widths).computeTotalWithMultiSize() +
1224                     snapSpaceY(getInsets().getBottom());
1225         } finally {
1226             performingLayout = false;
1227         }
1228     }
1229 
1230     @Override protected double computePrefWidth(double height) {
1231         computeGridMetrics();
1232         performingLayout = true;
1233         try {
1234             final double[] heights = height == -1 ? null : computeHeightsToFit(height).asArray();
1235 
1236             return snapSpaceX(getInsets().getLeft()) +
1237                     computePrefWidths(heights).computeTotalWithMultiSize() +
1238                     snapSpaceX(getInsets().getRight());
1239         } finally {
1240             performingLayout = false;
1241         }
1242     }
1243 
1244     @Override protected double computePrefHeight(double width) {
1245         computeGridMetrics();
1246         performingLayout = true;
1247         try {
1248             final double[] widths = width == -1 ? null : computeWidthsToFit(width).asArray();
1249 
1250             return snapSpaceY(getInsets().getTop()) +
1251                     computePrefHeights(widths).computeTotalWithMultiSize() +
1252                     snapSpaceY(getInsets().getBottom());
1253         } finally {
1254             performingLayout = false;
1255         }
1256     }
1257 
1258     private VPos getRowValignment(int rowIndex) {
1259         if (rowIndex < getRowConstraints().size()) {
1260             RowConstraints constraints = getRowConstraints().get(rowIndex);
1261             if (constraints.getValignment() != null) {
1262                 return constraints.getValignment();
1263             }
1264         }
1265         return VPos.CENTER;
1266     }
1267 
1268     private HPos getColumnHalignment(int columnIndex) {
1269         if (columnIndex < getColumnConstraints().size()) {
1270             ColumnConstraints constraints = getColumnConstraints().get(columnIndex);
1271             if (constraints.getHalignment() != null) {
1272                 return constraints.getHalignment();


1345         if (getNodeColumnSpan(child) == 1) {
1346             return widths[getNodeColumnIndex(child)];
1347         } else {
1348             double total = 0;
1349             for (int i = getNodeColumnIndex(child), last = getNodeColumnEndConvertRemaining(child); i <= last; ++i) {
1350                 total += widths[i];
1351             }
1352             return total;
1353         }
1354     }
1355 
1356     private CompositeSize computeMaxHeights() {
1357         if (rowMaxHeight == null) {
1358             rowMaxHeight = createCompositeRows(Double.MAX_VALUE); // Do not restrict the row (to allow grow). The
1359                                                                   // Nodes will be restricted to their computed size
1360                                                                   // in Region.layoutInArea call
1361             final ObservableList<RowConstraints> rowConstr = getRowConstraints();
1362             CompositeSize prefHeights = null;
1363             for (int i = 0; i < rowConstr.size(); ++i) {
1364                 final RowConstraints curConstraint = rowConstr.get(i);
1365                 double maxRowHeight = snapSizeY(curConstraint.getMaxHeight());
1366                 if (maxRowHeight == USE_PREF_SIZE) {
1367                     if (prefHeights == null) {
1368                         prefHeights = computePrefHeights(null);
1369                     }
1370                     rowMaxHeight.setPresetSize(i, prefHeights.getSize(i));
1371                 } else if (maxRowHeight != USE_COMPUTED_SIZE) {
1372                     final double min = snapSizeY(curConstraint.getMinHeight());
1373                     if (min >= 0 ) {
1374                         rowMaxHeight.setPresetSize(i, boundedSize(min, maxRowHeight, maxRowHeight));
1375                     } else {
1376                         rowMaxHeight.setPresetSize(i, maxRowHeight);
1377                     }
1378                 }
1379             }
1380         }
1381         return rowMaxHeight;
1382     }
1383 
1384     private CompositeSize computePrefHeights(double[] widths) {
1385         CompositeSize result;
1386         if (widths == null) {
1387             if (rowPrefHeight != null) {
1388                 return rowPrefHeight;
1389             }
1390             rowPrefHeight = createCompositeRows(0);
1391             result = rowPrefHeight;
1392         } else {
1393             result = createCompositeRows(0);
1394         }
1395 
1396         final ObservableList<RowConstraints> rowConstr = getRowConstraints();
1397         for (int i = 0; i < rowConstr.size(); ++i) {
1398             final RowConstraints curConstraint = rowConstr.get(i);
1399             double prefRowHeight = snapSizeY(curConstraint.getPrefHeight());
1400             final double min = snapSizeY(curConstraint.getMinHeight());
1401             if (prefRowHeight != USE_COMPUTED_SIZE) {
1402                 final double max = snapSizeY(curConstraint.getMaxHeight());
1403                 if (min >= 0 || max >= 0) {
1404                     result.setPresetSize(i, boundedSize(min < 0 ? 0 : min,
1405                             prefRowHeight,
1406                             max < 0 ? Double.POSITIVE_INFINITY : max));
1407                 } else {
1408                     result.setPresetSize(i, prefRowHeight);
1409                 }
1410             } else if (min > 0){
1411                 result.setSize(i, min);
1412             }
1413         }
1414         List<Node> managed = getManagedChildren();
1415         for (int i = 0, size = managed.size(); i < size; i++) {
1416             Node child = managed.get(i);
1417             int start = getNodeRowIndex(child);
1418             int end = getNodeRowEndConvertRemaining(child);
1419             double childPrefAreaHeight = computeChildPrefAreaHeight(child, isNodePositionedByBaseline(child) ? rowPrefBaselineComplement[start] : -1, getMargin(child),
1420                     widths == null ? -1 : getTotalWidthOfNodeColumns(child, widths));
1421             if (start == end && !result.isPreset(start)) {
1422                 double min = getRowMinHeight(start);


1427             }
1428         }
1429         return result;
1430     }
1431 
1432     private CompositeSize computeMinHeights(double[] widths) {
1433         CompositeSize result;
1434         if (widths == null) {
1435             if (rowMinHeight != null) {
1436                 return rowMinHeight;
1437             }
1438             rowMinHeight = createCompositeRows(0);
1439             result = rowMinHeight;
1440         } else {
1441             result = createCompositeRows(0);
1442         }
1443 
1444         final ObservableList<RowConstraints> rowConstr = getRowConstraints();
1445         CompositeSize prefHeights = null;
1446         for (int i = 0; i < rowConstr.size(); ++i) {
1447             double minRowHeight = snapSizeY(rowConstr.get(i).getMinHeight());
1448             if (minRowHeight == USE_PREF_SIZE) {
1449                 if (prefHeights == null) {
1450                     prefHeights = computePrefHeights(widths);
1451                 }
1452                 result.setPresetSize(i, prefHeights.getSize(i));
1453             } else if (minRowHeight != USE_COMPUTED_SIZE) {
1454                 result.setPresetSize(i, minRowHeight);
1455             }
1456         }
1457         List<Node> managed = getManagedChildren();
1458         for (int i = 0, size = managed.size(); i < size; i++) {
1459             Node child = managed.get(i);
1460             int start = getNodeRowIndex(child);
1461             int end = getNodeRowEndConvertRemaining(child);
1462             double childMinAreaHeight = computeChildMinAreaHeight(child, isNodePositionedByBaseline(child) ? rowMinBaselineComplement[start] : -1, getMargin(child),
1463                              widths == null ? -1 : getTotalWidthOfNodeColumns(child, widths));
1464             if (start == end && !result.isPreset(start)) {
1465                 result.setMaxSize(start, childMinAreaHeight);
1466             } else if (start != end){
1467                 result.setMaxMultiSize(start, end + 1, childMinAreaHeight);


1474         if (getNodeRowSpan(child) == 1) {
1475             return heights[getNodeRowIndex(child)];
1476         } else {
1477             double total = 0;
1478             for (int i = getNodeRowIndex(child), last = getNodeRowEndConvertRemaining(child); i <= last; ++i) {
1479                 total += heights[i];
1480             }
1481             return total;
1482         }
1483     }
1484 
1485     private CompositeSize computeMaxWidths() {
1486         if (columnMaxWidth == null) {
1487             columnMaxWidth = createCompositeColumns(Double.MAX_VALUE);// Do not restrict the column (to allow grow). The
1488                                                                       // Nodes will be restricted to their computed size
1489                                                                       // in Region.layoutInArea call
1490             final ObservableList<ColumnConstraints> columnConstr = getColumnConstraints();
1491             CompositeSize prefWidths = null;
1492             for (int i = 0; i < columnConstr.size(); ++i) {
1493                 final ColumnConstraints curConstraint = columnConstr.get(i);
1494                 double maxColumnWidth = snapSizeX(curConstraint.getMaxWidth());
1495                 if (maxColumnWidth == USE_PREF_SIZE) {
1496                     if (prefWidths == null) {
1497                         prefWidths = computePrefWidths(null);
1498                     }
1499                     columnMaxWidth.setPresetSize(i, prefWidths.getSize(i));
1500                 } else if (maxColumnWidth != USE_COMPUTED_SIZE) {
1501                     final double min = snapSizeX(curConstraint.getMinWidth());
1502                     if (min >= 0) {
1503                         columnMaxWidth.setPresetSize(i, boundedSize(min, maxColumnWidth, maxColumnWidth));
1504                     } else {
1505                         columnMaxWidth.setPresetSize(i, maxColumnWidth);
1506                     }
1507                 }
1508             }
1509         }
1510         return columnMaxWidth;
1511     }
1512 
1513     private CompositeSize computePrefWidths(double[] heights) {
1514         CompositeSize result;
1515         if (heights == null) {
1516             if (columnPrefWidth != null) {
1517                 return columnPrefWidth;
1518             }
1519             columnPrefWidth = createCompositeColumns(0);
1520             result = columnPrefWidth;
1521         } else {
1522             result = createCompositeColumns(0);
1523         }
1524 
1525         final ObservableList<ColumnConstraints> columnConstr = getColumnConstraints();
1526         for (int i = 0; i < columnConstr.size(); ++i) {
1527             final ColumnConstraints curConstraint = columnConstr.get(i);
1528             double prefColumnWidth = snapSizeX(curConstraint.getPrefWidth());
1529             final double min = snapSizeX(curConstraint.getMinWidth());
1530             if (prefColumnWidth != USE_COMPUTED_SIZE) {
1531                 final double max = snapSizeX(curConstraint.getMaxWidth());
1532                 if (min >= 0 || max >= 0) {
1533                     result.setPresetSize(i, boundedSize(min < 0 ? 0 : min,
1534                             prefColumnWidth,
1535                             max < 0 ? Double.POSITIVE_INFINITY : max));
1536                 } else {
1537                     result.setPresetSize(i, prefColumnWidth);
1538                 }
1539             } else if (min > 0){
1540                 result.setSize(i, min);
1541             }
1542         }
1543         List<Node> managed = getManagedChildren();
1544         for (int i = 0, size = managed.size(); i < size; i++) {
1545             Node child = managed.get(i);
1546             int start = getNodeColumnIndex(child);
1547             int end = getNodeColumnEndConvertRemaining(child);
1548             if (start == end && !result.isPreset(start)) {
1549                 double min = getColumnMinWidth(start);
1550                 double max = getColumnMaxWidth(start);
1551                 result.setMaxSize(start, boundedSize(min < 0 ? 0 : min, computeChildPrefAreaWidth(child,


1559             }
1560         }
1561         return result;
1562     }
1563 
1564     private CompositeSize computeMinWidths(double[] heights) {
1565         CompositeSize result;
1566         if (heights == null) {
1567             if (columnMinWidth != null) {
1568                 return columnMinWidth;
1569             }
1570             columnMinWidth = createCompositeColumns(0);
1571             result = columnMinWidth;
1572         } else {
1573             result = createCompositeColumns(0);
1574         }
1575 
1576         final ObservableList<ColumnConstraints> columnConstr = getColumnConstraints();
1577         CompositeSize prefWidths = null;
1578         for (int i = 0; i < columnConstr.size(); ++i) {
1579             double minColumnWidth = snapSizeX(columnConstr.get(i).getMinWidth());
1580             if (minColumnWidth == USE_PREF_SIZE) {
1581                 if (prefWidths == null) {
1582                     prefWidths = computePrefWidths(heights);
1583                 }
1584                 result.setPresetSize(i, prefWidths.getSize(i));
1585             } else if (minColumnWidth != USE_COMPUTED_SIZE) {
1586                 result.setPresetSize(i, minColumnWidth);
1587             }
1588         }
1589         List<Node> managed = getManagedChildren();
1590         for (int i = 0, size = managed.size(); i < size; i++) {
1591             Node child = managed.get(i);
1592             int start = getNodeColumnIndex(child);
1593             int end = getNodeColumnEndConvertRemaining(child);
1594             if (start == end && !result.isPreset(start)) {
1595                 result.setMaxSize(start, computeChildMinAreaWidth(child, getBaselineComplementForChild(child),
1596                         getMargin(child),
1597                         heights == null ? -1 : getTotalHeightOfNodeRows(child, heights),false));
1598             } else if (start != end){
1599                 result.setMaxMultiSize(start, end + 1, computeChildMinAreaWidth(child, getBaselineComplementForChild(child),


1646         // next layout pass.
1647         if (performingLayout) {
1648             return;
1649         } else if (metricsDirty) {
1650             super.requestLayout();
1651             return;
1652         }
1653         metricsDirty = true;
1654         bias = null;
1655         rowGrow = null;
1656         rowMinHeight = rowPrefHeight = rowMaxHeight = null;
1657         columnGrow = null;
1658         columnMinWidth = columnPrefWidth = columnMaxWidth = null;
1659         rowMinBaselineComplement = rowPrefBaselineComplement = rowMaxBaselineComplement = null;
1660         super.requestLayout();
1661     }
1662 
1663     @Override protected void layoutChildren() {
1664         performingLayout = true;
1665         try {
1666             final double snaphgap = snapSpaceX(getHgap());
1667             final double snapvgap = snapSpaceY(getVgap());
1668             final double top = snapSpaceY(getInsets().getTop());
1669             final double bottom = snapSpaceY(getInsets().getBottom());
1670             final double left = snapSpaceX(getInsets().getLeft());
1671             final double right = snapSpaceX(getInsets().getRight());
1672 
1673             final double width = getWidth();
1674             final double height = getHeight();
1675             final double contentHeight = height - top - bottom;
1676             final double contentWidth = width - left - right;
1677             double columnTotal;
1678             double rowTotal;
1679             computeGridMetrics();
1680 
1681             Orientation contentBias = getContentBias();
1682             CompositeSize heights;
1683             final CompositeSize widths;
1684             if (contentBias == null) {
1685                 heights = (CompositeSize) computePrefHeights(null).clone();
1686                 widths = (CompositeSize) computePrefWidths(null).clone();
1687                 rowTotal = adjustRowHeights(heights, height);
1688                 columnTotal = adjustColumnWidths(widths, width);
1689             } else if (contentBias == Orientation.HORIZONTAL) {
1690                 widths = (CompositeSize) computePrefWidths(null).clone();
1691                 columnTotal = adjustColumnWidths(widths, width);


1782                     baselineOffset = baselineOffsets[rowIndex];
1783                 }
1784 
1785                 Insets margin = getMargin(child);
1786                 layoutInArea(child, areaX, areaY, areaW, areaH,
1787                         baselineOffset,
1788                         margin,
1789                         fillWidth, fillHeight,
1790                         halign, valign);
1791             }
1792             layoutGridLines(widths, heights, x, y, rowTotal, columnTotal);
1793             currentHeights = heights;
1794             currentWidths = widths;
1795         } finally {
1796             performingLayout = false;
1797         }
1798     }
1799 
1800     private double adjustRowHeights(final CompositeSize heights, double height) {
1801         assert(height != -1);
1802         final double snapvgap = snapSpaceY(getVgap());
1803         final double top = snapSpaceY(getInsets().getTop());
1804         final double bottom = snapSpaceY(getInsets().getBottom());
1805         final double vgaps = snapvgap * (getNumberOfRows() - 1);
1806         final double contentHeight = height - top - bottom;
1807 
1808         // if there are percentage rows, give them their percentages first
1809         if (rowPercentTotal > 0) {
1810             double remainder = 0;
1811             for (int i = 0; i < rowPercentHeight.length; i++) {
1812                 if (rowPercentHeight[i] >= 0) {
1813                     double size = (contentHeight - vgaps) * (rowPercentHeight[i]/100);
1814                     double floor = Math.floor(size);
1815                     remainder += size - floor;
1816 
1817                     // snap size to integer boundary based on the computed remainder as we loop through the rows.
1818                     size = floor;
1819                     if (remainder >= 0.5) {
1820                         size++;
1821                         remainder = (-1.0) + remainder;
1822                     }
1823                     heights.setSize(i, size);
1824                 }


1987         double available = extraHeight; // will be negative in shrinking case
1988         boolean handleRemainder = false;
1989         double portion = 0;
1990 
1991         // RT-25684: We have to be careful that when subtracting change
1992         // that we don't jump right past 0 - this leads to an infinite
1993         // loop
1994         final boolean wasPositive = available >= 0.0;
1995         boolean isPositive = wasPositive;
1996 
1997         CompositeSize limitSize = shrinking? computeMinHeights(null) :
1998                             computeMaxHeights();
1999         while (available != 0 && wasPositive == isPositive && adjusting.size() > 0) {
2000             if (!handleRemainder) {
2001                 portion = available > 0 ? Math.floor(available / adjusting.size()) :
2002                         Math.ceil(available / adjusting.size()); // negative in shrinking case
2003             }
2004             if (portion != 0) {
2005                 for (Iterator<Integer> i = adjusting.iterator(); i.hasNext();) {
2006                     final int index = i.next();
2007                     double limit = snapSpaceY(limitSize.getProportionalMinOrMaxSize(index, shrinking))
2008                             - heights.getSize(index); // negative in shrinking case
2009                     if (shrinking && limit > 0
2010                             || !shrinking && limit < 0) { // Limit completely if current size
2011                                                  // (originally based on preferred) already passed the computed limit
2012                         limit = 0;
2013                     }
2014                     final double change = Math.abs(limit) <= Math.abs(portion)? limit : portion;
2015                     heights.addSize(index, change);
2016                     available -= change;
2017                     isPositive = available >= 0.0;
2018                     if (Math.abs(change) < Math.abs(portion)) {
2019                         i.remove();
2020                     }
2021                     if (available == 0) {
2022                         break;
2023                     }
2024                 }
2025              } else {
2026                 // Handle the remainder
2027                 portion = (int)(available) % adjusting.size();
2028                 if (portion == 0) {
2029                     break;
2030                 } else {
2031                     // We have a remainder evenly distribute it.
2032                     portion = shrinking ? -1 : 1;
2033                     handleRemainder = true;
2034                 }
2035             }
2036         }
2037 
2038         return available; // might be negative in shrinking case
2039     }
2040 
2041     private double adjustColumnWidths(final CompositeSize widths, double width) {
2042         assert(width != -1);
2043         final double snaphgap = snapSpaceX(getHgap());
2044         final double left = snapSpaceX(getInsets().getLeft());
2045         final double right = snapSpaceX(getInsets().getRight());
2046         final double hgaps = snaphgap * (getNumberOfColumns() - 1);
2047         final double contentWidth = width - left - right;
2048 
2049         // if there are percentage rows, give them their percentages first
2050         if (columnPercentTotal > 0) {
2051             double remainder = 0;
2052             for (int i = 0; i < columnPercentWidth.length; i++) {
2053                 if (columnPercentWidth[i] >= 0) {
2054                     double size = (contentWidth - hgaps) * (columnPercentWidth[i]/100);
2055                     double floor = Math.floor(size);
2056                     remainder += size - floor;
2057 
2058                     // snap size to integer boundary based on the computed remainder as we loop through the columns.
2059                     size = floor;
2060                     if (remainder >= 0.5) {
2061                         size++;
2062                         remainder = (-1.0) + remainder;
2063                     }
2064                     widths.setSize(i, size);
2065                 }


2231         double available = extraWidth; // will be negative in shrinking case
2232         boolean handleRemainder = false;
2233         double portion = 0;
2234 
2235         // RT-25684: We have to be careful that when subtracting change
2236         // that we don't jump right past 0 - this leads to an infinite
2237         // loop
2238         final boolean wasPositive = available >= 0.0;
2239         boolean isPositive = wasPositive;
2240 
2241         CompositeSize limitSize = shrinking? computeMinWidths(null) :
2242                             computeMaxWidths();
2243         while (available != 0 && wasPositive == isPositive && adjusting.size() > 0) {
2244             if (!handleRemainder) {
2245                 portion = available > 0 ? Math.floor(available / adjusting.size()) :
2246                         Math.ceil(available / adjusting.size()); // negative in shrinking case
2247             }
2248             if (portion != 0) {
2249                 for (Iterator<Integer> i = adjusting.iterator(); i.hasNext();) {
2250                     final int index = i.next();
2251                     double limit = snapSpaceX(limitSize.getProportionalMinOrMaxSize(index, shrinking))
2252                             - widths.getSize(index); // negative in shrinking case
2253                     if (shrinking && limit > 0
2254                             || !shrinking && limit < 0) { // Limit completely if current size
2255                                                  // (originally based on preferred) already passed the computed limit
2256                         limit = 0;
2257                     }
2258                     final double change = Math.abs(limit) <= Math.abs(portion)? limit : portion;
2259                     widths.addSize(index, change);
2260                     available -= change;
2261                     isPositive = available >= 0.0;
2262                     if (Math.abs(change) < Math.abs(portion)) {
2263                         i.remove();
2264                     }
2265                     if (available == 0) {
2266                         break;
2267                     }
2268                 }
2269             } else {
2270                 // Handle the remainder
2271                 portion = (int)(available) % adjusting.size();
2272                 if (portion == 0) {
2273                     break;
2274                 } else {
2275                     // We have a remainder evenly distribute it.
2276                     portion = shrinking ? -1 : 1;
2277                     handleRemainder = true;
2278                 }
2279             }
2280         }
2281 
2282         return available; // might be negative in shrinking case
2283     }
2284 
2285     private void layoutGridLines(CompositeSize columnWidths, CompositeSize rowHeights, double x, double y, double columnHeight, double rowWidth) {
2286         if (!isGridLinesVisible()) {
2287             return;
2288         }
2289         if (!gridLines.getChildren().isEmpty()) {
2290             gridLines.getChildren().clear();
2291         }
2292         double hgap = snapSpaceX(getHgap());
2293         double vgap = snapSpaceY(getVgap());
2294 
2295         // create vertical lines
2296         double linex = x;
2297         double liney = y;
2298         for (int i = 0; i <= columnWidths.getLength(); i++) {
2299              gridLines.getChildren().add(createGridLine(linex, liney, linex, liney + columnHeight));
2300              if (i > 0 && i < columnWidths.getLength() && getHgap() != 0) {
2301                  linex += getHgap();
2302                  gridLines.getChildren().add(createGridLine(linex, liney, linex, liney + columnHeight));
2303              }
2304              if (i < columnWidths.getLength()) {
2305                  linex += columnWidths.getSize(i);
2306              }
2307         }
2308         // create horizontal lines
2309         linex = x;
2310         for (int i = 0; i <= rowHeights.getLength(); i++) {
2311             gridLines.getChildren().add(createGridLine(linex, liney, linex + rowWidth, liney));
2312             if (i > 0 && i < rowHeights.getLength() && getVgap() != 0) {
2313                 liney += getVgap();


2324          line.setStartX(startX);
2325          line.setStartY(startY);
2326          line.setEndX(endX);
2327          line.setEndY(endY);
2328          line.setStroke(GRID_LINE_COLOR);
2329          line.setStrokeDashOffset(GRID_LINE_DASH);
2330 
2331          return line;
2332     }
2333 
2334     /**
2335      * Returns a string representation of this {@code GridPane} object.
2336      * @return a string representation of this {@code GridPane} object.
2337      */
2338     @Override public String toString() {
2339         return "Grid hgap="+getHgap()+", vgap="+getVgap()+", alignment="+getAlignment();
2340     }
2341 
2342     private CompositeSize createCompositeRows(double initSize) {
2343         return new CompositeSize(getNumberOfRows(), rowPercentHeight, rowPercentTotal,
2344                 snapSpaceY(getVgap()), initSize);
2345     }
2346 
2347     private CompositeSize createCompositeColumns(double initSize) {
2348         return new CompositeSize(getNumberOfColumns(), columnPercentWidth, columnPercentTotal,
2349                 snapSpaceX(getHgap()), initSize);
2350     }
2351 
2352     private int getNodeRowEndConvertRemaining(Node child) {
2353         int rowSpan = getNodeRowSpan(child);
2354         return rowSpan != REMAINING? getNodeRowIndex(child) + rowSpan - 1 : getNumberOfRows() - 1;
2355     }
2356 
2357     private int getNodeColumnEndConvertRemaining(Node child) {
2358         int columnSpan = getNodeColumnSpan(child);
2359         return columnSpan != REMAINING? getNodeColumnIndex(child) + columnSpan - 1 : getNumberOfColumns() - 1;
2360     }
2361 
2362 
2363     // This methods are inteded to be used by GridPaneDesignInfo
2364     private CompositeSize currentHeights;
2365     private CompositeSize currentWidths;
2366 
2367     double[][] getGrid() {
2368         if (currentHeights == null || currentWidths == null) {
2369             return null;


2723         int nColumns = this.getColumnConstraints().size();
2724         for (int i = 0; i < this.getChildren().size(); i++) {
2725             Node child = this.getChildren().get(i);
2726             if (child.isManaged()) {
2727                 int columnIndex = GridPane.getNodeColumnIndex(child);
2728                 int columnEnd = GridPane.getNodeColumnEnd(child);
2729                 nColumns = Math.max(nColumns, (columnEnd != GridPane.REMAINING? columnEnd : columnIndex) + 1);
2730             }
2731         }
2732         return nColumns;
2733     }
2734 
2735     /**
2736      * Copied from GridPaneDesignInfo for SceneBuilder.
2737      *
2738      * @treatAsPrivate implementation detail
2739      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
2740      */
2741     @Deprecated // SB-dependency: RT-33381 has been filed to track this
2742     public final Bounds impl_getCellBounds(int columnIndex, int rowIndex) {
2743         final double snaphgap = this.snapSpaceX(this.getHgap());
2744         final double snapvgap = this.snapSpaceY(this.getVgap());
2745         final double top = this.snapSpaceY(this.getInsets().getTop());
2746         final double right = this.snapSpaceX(this.getInsets().getRight());
2747         final double bottom = this.snapSpaceY(this.getInsets().getBottom());
2748         final double left = this.snapSpaceX(this.getInsets().getLeft());
2749         final double gridPaneHeight = this.snapSizeY(this.getHeight()) - (top + bottom);
2750         final double gridPaneWidth = this.snapSizeX(this.getWidth()) - (left + right);
2751 
2752         // Compute grid. Result contains two double arrays, first for columns, second for rows
2753         double[] columnWidths;
2754         double[] rowHeights;
2755 
2756         double[][] grid = this.getGrid();
2757         if (grid == null) {
2758             rowHeights = new double[] {0};
2759             rowIndex = 0;
2760             columnWidths = new double[] {0};
2761             columnIndex = 0;
2762         } else {
2763             columnWidths = grid[0];
2764             rowHeights = grid[1];
2765         }
2766 
2767         // Compute the total row height
2768         double rowTotal = 0;
2769         for (int i = 0; i < rowHeights.length; i++) {
2770             rowTotal += rowHeights[i];