379 @Override public Orientation getContentBias() { 380 if (biasDirty) { 381 bias = null; 382 final List<Node> children = getManagedChildren(); 383 for (Node child : children) { 384 Orientation contentBias = child.getContentBias(); 385 if (contentBias != null) { 386 bias = contentBias; 387 if (contentBias == Orientation.HORIZONTAL) { 388 break; 389 } 390 } 391 } 392 biasDirty = false; 393 } 394 return bias; 395 } 396 397 @Override protected double computeMinWidth(double height) { 398 Insets insets = getInsets(); 399 return snapSpace(insets.getLeft()) + 400 computeContentWidth(getManagedChildren(), height, true) + 401 snapSpace(insets.getRight()); 402 } 403 404 @Override protected double computeMinHeight(double width) { 405 Insets insets = getInsets(); 406 List<Node>managed = getManagedChildren(); 407 double contentHeight = 0; 408 if (width != -1 && getContentBias() != null) { 409 double prefWidths[][] = getAreaWidths(managed, -1, false); 410 adjustAreaWidths(managed, prefWidths, width, -1); 411 contentHeight = computeMaxMinAreaHeight(managed, marginAccessor, prefWidths[0], getAlignmentInternal().getVpos()); 412 } else { 413 contentHeight = computeMaxMinAreaHeight(managed, marginAccessor, getAlignmentInternal().getVpos()); 414 } 415 return snapSpace(insets.getTop()) + 416 contentHeight + 417 snapSpace(insets.getBottom()); 418 } 419 420 @Override protected double computePrefWidth(double height) { 421 Insets insets = getInsets(); 422 return snapSpace(insets.getLeft()) + 423 computeContentWidth(getManagedChildren(), height, false) + 424 snapSpace(insets.getRight()); 425 } 426 427 @Override protected double computePrefHeight(double width) { 428 Insets insets = getInsets(); 429 List<Node>managed = getManagedChildren(); 430 double contentHeight = 0; 431 if (width != -1 && getContentBias() != null) { 432 double prefWidths[][] = getAreaWidths(managed, -1, false); 433 adjustAreaWidths(managed, prefWidths, width, -1); 434 contentHeight = computeMaxPrefAreaHeight(managed, marginAccessor, prefWidths[0], getAlignmentInternal().getVpos()); 435 } else { 436 contentHeight = computeMaxPrefAreaHeight(managed, marginAccessor, getAlignmentInternal().getVpos()); 437 } 438 return snapSpace(insets.getTop()) + 439 contentHeight + 440 snapSpace(insets.getBottom()); 441 } 442 443 private double[][] getAreaWidths(List<Node>managed, double height, boolean minimum) { 444 // height could be -1 445 double[][] temp = getTempArray(managed.size()); 446 final double insideHeight = height == -1? -1 : height - 447 snapSpace(getInsets().getTop()) - snapSpace(getInsets().getBottom()); 448 final boolean shouldFillHeight = shouldFillHeight(); 449 for (int i = 0, size = managed.size(); i < size; i++) { 450 Node child = managed.get(i); 451 Insets margin = getMargin(child); 452 if (minimum) { 453 temp[0][i] = computeChildMinAreaWidth(child, getMinBaselineComplement(), margin, insideHeight, shouldFillHeight); 454 } else { 455 temp[0][i] = computeChildPrefAreaWidth(child, getPrefBaselineComplement(), margin, insideHeight, shouldFillHeight); 456 } 457 } 458 return temp; 459 } 460 461 private double adjustAreaWidths(List<Node>managed, double areaWidths[][], double width, double height) { 462 Insets insets = getInsets(); 463 double top = snapSpace(insets.getTop()); 464 double bottom = snapSpace(insets.getBottom()); 465 466 double contentWidth = sum(areaWidths[0], managed.size()) + (managed.size()-1)*snapSpace(getSpacing()); 467 double extraWidth = width - 468 snapSpace(insets.getLeft()) - snapSpace(insets.getRight()) - contentWidth; 469 470 if (extraWidth != 0) { 471 final double refHeight = shouldFillHeight() && height != -1? height - top - bottom : -1; 472 double remaining = growOrShrinkAreaWidths(managed, areaWidths, Priority.ALWAYS, extraWidth, refHeight); 473 remaining = growOrShrinkAreaWidths(managed, areaWidths, Priority.SOMETIMES, remaining, refHeight); 474 contentWidth += (extraWidth - remaining); 475 } 476 return contentWidth; 477 } 478 479 private double growOrShrinkAreaWidths(List<Node>managed, double areaWidths[][], Priority priority, double extraWidth, double height) { 480 final boolean shrinking = extraWidth < 0; 481 int adjustingNumber = 0; 482 483 double[] usedWidths = areaWidths[0]; 484 double[] temp = areaWidths[1]; 485 final boolean shouldFillHeight = shouldFillHeight(); 486 487 if (shrinking) { 488 adjustingNumber = managed.size(); 489 for (int i = 0, size = managed.size(); i < size; i++) { 490 final Node child = managed.get(i); 491 temp[i] = computeChildMinAreaWidth(child, getMinBaselineComplement(), getMargin(child), height, shouldFillHeight); 492 } 493 } else { 494 for (int i = 0, size = managed.size(); i < size; i++) { 495 final Node child = managed.get(i); 496 if (getHgrow(child) == priority) { 497 temp[i] = computeChildMaxAreaWidth(child, getMinBaselineComplement(), getMargin(child), height, shouldFillHeight); 498 adjustingNumber++; 499 } else { 500 temp[i] = -1; 501 } 502 } 503 } 504 505 double available = extraWidth; // will be negative in shrinking case 506 outer:while (Math.abs(available) > 1 && adjustingNumber > 0) { 507 final double portion = snapPortion(available / adjustingNumber); // negative in shrinking case 508 for (int i = 0, size = managed.size(); i < size; i++) { 509 if (temp[i] == -1) { 510 continue; 511 } 512 final double limit = temp[i] - usedWidths[i]; // negative in shrinking case 513 final double change = Math.abs(limit) <= Math.abs(portion)? limit : portion; 514 usedWidths[i] += change; 515 available -= change; 516 if (Math.abs(available) < 1) { 517 break outer; 518 } 519 if (Math.abs(change) < Math.abs(portion)) { 520 temp[i] = -1; 521 adjustingNumber--; 522 } 523 } 524 } 525 526 return available; // might be negative in shrinking case 527 } 528 529 private double computeContentWidth(List<Node> managedChildren, double height, boolean minimum) { 530 return sum(getAreaWidths(managedChildren, height, minimum)[0], managedChildren.size()) 531 + (managedChildren.size()-1)*snapSpace(getSpacing()); 532 } 533 534 private static double sum(double[] array, int size) { 535 int i = 0; 536 double res = 0; 537 while (i != size) { 538 res += array[i++]; 539 } 540 return res; 541 } 542 543 @Override public void requestLayout() { 544 if (performingLayout) { 545 return; 546 } 547 biasDirty = true; 548 bias = null; 549 minBaselineComplement = Double.NaN; 550 prefBaselineComplement = Double.NaN; 551 baselineOffset = Double.NaN; 598 max = Math.max(max, top + child.getLayoutBounds().getMinY() + offset); 599 } 600 } 601 baselineOffset = max + snappedTopInset(); 602 } else { 603 baselineOffset = BASELINE_OFFSET_SAME_AS_HEIGHT; 604 } 605 } 606 return baselineOffset; 607 } 608 609 @Override protected void layoutChildren() { 610 performingLayout = true; 611 List<Node> managed = getManagedChildren(); 612 Insets insets = getInsets(); 613 Pos align = getAlignmentInternal(); 614 HPos alignHpos = align.getHpos(); 615 VPos alignVpos = align.getVpos(); 616 double width = getWidth(); 617 double height = getHeight(); 618 double top = snapSpace(insets.getTop()); 619 double left = snapSpace(insets.getLeft()); 620 double bottom = snapSpace(insets.getBottom()); 621 double right = snapSpace(insets.getRight()); 622 double space = snapSpace(getSpacing()); 623 boolean shouldFillHeight = shouldFillHeight(); 624 625 final double[][] actualAreaWidths = getAreaWidths(managed, height, false); 626 double contentWidth = adjustAreaWidths(managed, actualAreaWidths, width, height); 627 double contentHeight = height - top - bottom; 628 629 double x = left + computeXOffset(width - left - right, contentWidth, align.getHpos()); 630 double y = top; 631 double baselineOffset = -1; 632 if (alignVpos == VPos.BASELINE) { 633 double baselineComplement = getMinBaselineComplement(); 634 baselineOffset = getAreaBaselineOffset(managed, marginAccessor, i -> actualAreaWidths[0][i], 635 contentHeight, shouldFillHeight, baselineComplement); 636 } 637 638 for (int i = 0, size = managed.size(); i < size; i++) { 639 Node child = managed.get(i); 640 Insets margin = getMargin(child); 641 layoutInArea(child, x, y, actualAreaWidths[0][i], contentHeight, 642 baselineOffset, margin, true, shouldFillHeight, | 379 @Override public Orientation getContentBias() { 380 if (biasDirty) { 381 bias = null; 382 final List<Node> children = getManagedChildren(); 383 for (Node child : children) { 384 Orientation contentBias = child.getContentBias(); 385 if (contentBias != null) { 386 bias = contentBias; 387 if (contentBias == Orientation.HORIZONTAL) { 388 break; 389 } 390 } 391 } 392 biasDirty = false; 393 } 394 return bias; 395 } 396 397 @Override protected double computeMinWidth(double height) { 398 Insets insets = getInsets(); 399 return snapSpaceX(insets.getLeft()) + 400 computeContentWidth(getManagedChildren(), height, true) + 401 snapSpaceX(insets.getRight()); 402 } 403 404 @Override protected double computeMinHeight(double width) { 405 Insets insets = getInsets(); 406 List<Node>managed = getManagedChildren(); 407 double contentHeight = 0; 408 if (width != -1 && getContentBias() != null) { 409 double prefWidths[][] = getAreaWidths(managed, -1, false); 410 adjustAreaWidths(managed, prefWidths, width, -1); 411 contentHeight = computeMaxMinAreaHeight(managed, marginAccessor, prefWidths[0], getAlignmentInternal().getVpos()); 412 } else { 413 contentHeight = computeMaxMinAreaHeight(managed, marginAccessor, getAlignmentInternal().getVpos()); 414 } 415 return snapSpaceY(insets.getTop()) + 416 contentHeight + 417 snapSpaceY(insets.getBottom()); 418 } 419 420 @Override protected double computePrefWidth(double height) { 421 Insets insets = getInsets(); 422 return snapSpaceX(insets.getLeft()) + 423 computeContentWidth(getManagedChildren(), height, false) + 424 snapSpaceX(insets.getRight()); 425 } 426 427 @Override protected double computePrefHeight(double width) { 428 Insets insets = getInsets(); 429 List<Node>managed = getManagedChildren(); 430 double contentHeight = 0; 431 if (width != -1 && getContentBias() != null) { 432 double prefWidths[][] = getAreaWidths(managed, -1, false); 433 adjustAreaWidths(managed, prefWidths, width, -1); 434 contentHeight = computeMaxPrefAreaHeight(managed, marginAccessor, prefWidths[0], getAlignmentInternal().getVpos()); 435 } else { 436 contentHeight = computeMaxPrefAreaHeight(managed, marginAccessor, getAlignmentInternal().getVpos()); 437 } 438 return snapSpaceY(insets.getTop()) + 439 contentHeight + 440 snapSpaceY(insets.getBottom()); 441 } 442 443 private double[][] getAreaWidths(List<Node>managed, double height, boolean minimum) { 444 // height could be -1 445 double[][] temp = getTempArray(managed.size()); 446 final double insideHeight = height == -1? -1 : height - 447 snapSpaceY(getInsets().getTop()) - snapSpaceY(getInsets().getBottom()); 448 final boolean shouldFillHeight = shouldFillHeight(); 449 for (int i = 0, size = managed.size(); i < size; i++) { 450 Node child = managed.get(i); 451 Insets margin = getMargin(child); 452 if (minimum) { 453 temp[0][i] = computeChildMinAreaWidth(child, getMinBaselineComplement(), margin, insideHeight, shouldFillHeight); 454 } else { 455 temp[0][i] = computeChildPrefAreaWidth(child, getPrefBaselineComplement(), margin, insideHeight, shouldFillHeight); 456 } 457 } 458 return temp; 459 } 460 461 private double adjustAreaWidths(List<Node>managed, double areaWidths[][], double width, double height) { 462 Insets insets = getInsets(); 463 double top = snapSpaceY(insets.getTop()); 464 double bottom = snapSpaceY(insets.getBottom()); 465 466 double contentWidth = sum(areaWidths[0], managed.size()) + (managed.size()-1)*snapSpaceX(getSpacing()); 467 double extraWidth = width - 468 snapSpaceX(insets.getLeft()) - snapSpaceX(insets.getRight()) - contentWidth; 469 470 if (extraWidth != 0) { 471 final double refHeight = shouldFillHeight() && height != -1? height - top - bottom : -1; 472 double remaining = growOrShrinkAreaWidths(managed, areaWidths, Priority.ALWAYS, extraWidth, refHeight); 473 remaining = growOrShrinkAreaWidths(managed, areaWidths, Priority.SOMETIMES, remaining, refHeight); 474 contentWidth += (extraWidth - remaining); 475 } 476 return contentWidth; 477 } 478 479 private double growOrShrinkAreaWidths(List<Node>managed, double areaWidths[][], Priority priority, double extraWidth, double height) { 480 final boolean shrinking = extraWidth < 0; 481 int adjustingNumber = 0; 482 483 double[] usedWidths = areaWidths[0]; 484 double[] temp = areaWidths[1]; 485 final boolean shouldFillHeight = shouldFillHeight(); 486 487 if (shrinking) { 488 adjustingNumber = managed.size(); 489 for (int i = 0, size = managed.size(); i < size; i++) { 490 final Node child = managed.get(i); 491 temp[i] = computeChildMinAreaWidth(child, getMinBaselineComplement(), getMargin(child), height, shouldFillHeight); 492 } 493 } else { 494 for (int i = 0, size = managed.size(); i < size; i++) { 495 final Node child = managed.get(i); 496 if (getHgrow(child) == priority) { 497 temp[i] = computeChildMaxAreaWidth(child, getMinBaselineComplement(), getMargin(child), height, shouldFillHeight); 498 adjustingNumber++; 499 } else { 500 temp[i] = -1; 501 } 502 } 503 } 504 505 double available = extraWidth; // will be negative in shrinking case 506 outer:while (Math.abs(available) > 1 && adjustingNumber > 0) { 507 final double portion = snapPortionX(available / adjustingNumber); // negative in shrinking case 508 for (int i = 0, size = managed.size(); i < size; i++) { 509 if (temp[i] == -1) { 510 continue; 511 } 512 final double limit = temp[i] - usedWidths[i]; // negative in shrinking case 513 final double change = Math.abs(limit) <= Math.abs(portion)? limit : portion; 514 usedWidths[i] += change; 515 available -= change; 516 if (Math.abs(available) < 1) { 517 break outer; 518 } 519 if (Math.abs(change) < Math.abs(portion)) { 520 temp[i] = -1; 521 adjustingNumber--; 522 } 523 } 524 } 525 526 return available; // might be negative in shrinking case 527 } 528 529 private double computeContentWidth(List<Node> managedChildren, double height, boolean minimum) { 530 return sum(getAreaWidths(managedChildren, height, minimum)[0], managedChildren.size()) 531 + (managedChildren.size()-1)*snapSpaceX(getSpacing()); 532 } 533 534 private static double sum(double[] array, int size) { 535 int i = 0; 536 double res = 0; 537 while (i != size) { 538 res += array[i++]; 539 } 540 return res; 541 } 542 543 @Override public void requestLayout() { 544 if (performingLayout) { 545 return; 546 } 547 biasDirty = true; 548 bias = null; 549 minBaselineComplement = Double.NaN; 550 prefBaselineComplement = Double.NaN; 551 baselineOffset = Double.NaN; 598 max = Math.max(max, top + child.getLayoutBounds().getMinY() + offset); 599 } 600 } 601 baselineOffset = max + snappedTopInset(); 602 } else { 603 baselineOffset = BASELINE_OFFSET_SAME_AS_HEIGHT; 604 } 605 } 606 return baselineOffset; 607 } 608 609 @Override protected void layoutChildren() { 610 performingLayout = true; 611 List<Node> managed = getManagedChildren(); 612 Insets insets = getInsets(); 613 Pos align = getAlignmentInternal(); 614 HPos alignHpos = align.getHpos(); 615 VPos alignVpos = align.getVpos(); 616 double width = getWidth(); 617 double height = getHeight(); 618 double top = snapSpaceY(insets.getTop()); 619 double left = snapSpaceX(insets.getLeft()); 620 double bottom = snapSpaceY(insets.getBottom()); 621 double right = snapSpaceX(insets.getRight()); 622 double space = snapSpaceX(getSpacing()); 623 boolean shouldFillHeight = shouldFillHeight(); 624 625 final double[][] actualAreaWidths = getAreaWidths(managed, height, false); 626 double contentWidth = adjustAreaWidths(managed, actualAreaWidths, width, height); 627 double contentHeight = height - top - bottom; 628 629 double x = left + computeXOffset(width - left - right, contentWidth, align.getHpos()); 630 double y = top; 631 double baselineOffset = -1; 632 if (alignVpos == VPos.BASELINE) { 633 double baselineComplement = getMinBaselineComplement(); 634 baselineOffset = getAreaBaselineOffset(managed, marginAccessor, i -> actualAreaWidths[0][i], 635 contentHeight, shouldFillHeight, baselineComplement); 636 } 637 638 for (int i = 0, size = managed.size(); i < size; i++) { 639 Node child = managed.get(i); 640 Insets margin = getMargin(child); 641 layoutInArea(child, x, y, actualAreaWidths[0][i], contentHeight, 642 baselineOffset, margin, true, shouldFillHeight, |