void CalcDefaultSizes(double width, double height) { bool vertical = direction == Orientation.Vertical; int nexpands = 0; double requiredSize = 0; double availableSize = vertical ? height : width; var widthConstraint = vertical ? SizeConstraint.WithSize(width) : SizeConstraint.Unconstrained; var heightConstraint = vertical ? SizeConstraint.Unconstrained : SizeConstraint.WithSize(height); var visibleChildren = children.Where(b => b.Child.Visible).ToArray(); var sizes = new Dictionary <BoxPlacement, double> (); // Get the natural size of each child foreach (var bp in visibleChildren) { Size s; s = bp.Child.Surface.GetPreferredSize(widthConstraint, heightConstraint, true); bp.NextSize = vertical ? s.Height : s.Width; sizes [bp] = bp.NextSize; requiredSize += bp.NextSize; if (bp.Child.ExpandsForOrientation(direction)) { nexpands++; } } double remaining = availableSize - requiredSize - (spacing * (double)(visibleChildren.Length - 1)); if (remaining < 0) { // The box is not big enough to fit the widgets using its natural size. // We have to shrink the widgets. // The total amount we have to shrink double shrinkSize = -remaining; var sizePart = new SizeSplitter(shrinkSize, visibleChildren.Length); foreach (var bp in visibleChildren) { bp.NextSize -= sizePart.NextSizePart(); } } else { var expandRemaining = new SizeSplitter(remaining, nexpands); foreach (var bp in visibleChildren) { if (bp.Child.ExpandsForOrientation(direction)) { bp.NextSize += expandRemaining.NextSizePart(); } } } }
void CalcDefaultSizes(SizeRequestMode mode, double totalSize, double lengthConstraint) { bool calcHeights = direction == Orientation.Vertical; bool useLengthConstraint = mode == SizeRequestMode.HeightForWidth && calcHeights || mode == SizeRequestMode.WidthForHeight && !calcHeights; int nexpands = 0; double naturalSize = 0; var visibleChildren = children.Where(b => b.Child.Visible).ToArray(); var sizes = new Dictionary <BoxPlacement, WidgetSize> (); // Get the natural size of each child foreach (var bp in visibleChildren) { WidgetSize s; if (useLengthConstraint) { s = GetPreferredLengthForSize(mode, bp.Child, lengthConstraint); } else { s = GetPreferredSize(calcHeights, bp.Child); } sizes [bp] = s; naturalSize += s.NaturalSize; bp.NextSize = s.NaturalSize; if ((bp.BoxMode & BoxMode.Expand) != 0) { nexpands++; } } double remaining = totalSize - naturalSize - (spacing * (double)(visibleChildren.Length - 1)); if (remaining < 0) { // The box is not big enough to fit the widgets using its natural size. // We have to shrink the widgets. // List of widgets that we have to shrink var toShrink = new List <BoxPlacement> (visibleChildren); // The total amount we have to shrink double shrinkSize = -remaining; while (toShrink.Count > 0 && shrinkSize > 0) { SizeSplitter sizePart = new SizeSplitter(shrinkSize, toShrink.Count); shrinkSize = 0; for (int i = 0; i < toShrink.Count; i++) { var bp = toShrink[i]; bp.NextSize -= sizePart.NextSizePart(); WidgetSize size = sizes [bp]; if (bp.NextSize < size.MinSize) { // If the widget can't be shrinked anymore, we remove it from the shrink list // and increment the remaining shrink size. We'll loop again and this size will be // substracted from the cells which can still be reduced shrinkSize += (size.MinSize - bp.NextSize); bp.NextSize = size.MinSize; toShrink.RemoveAt(i); i--; } } } } else { var expandRemaining = new SizeSplitter(remaining, nexpands); foreach (var bp in visibleChildren) { if ((bp.BoxMode & BoxMode.Expand) != 0) { bp.NextSize += expandRemaining.NextSizePart(); } } } }
void CalcDefaultSizes(SizeRequestMode mode, double totalSize, double lengthConstraint) { bool calcHeights = direction == Orientation.Vertical; bool useLengthConstraint = mode == SizeRequestMode.HeightForWidth && calcHeights || mode == SizeRequestMode.WidthForHeight && !calcHeights; int nexpands = 0; double naturalSize = 0; var visibleChildren = children.Where(b => b.Child.Visible); int childrenCount = 0; // Get the natural size of each child foreach (var bp in visibleChildren) { childrenCount++; WidgetSize s; if (useLengthConstraint) { s = GetPreferredLengthForSize(mode, bp.Child, lengthConstraint); } else { s = GetPreferredSize(calcHeights, bp.Child); } naturalSize += s.NaturalSize; bp.NextSize = s.NaturalSize; if ((bp.BoxMode & BoxMode.Expand) != 0) { nexpands++; } } double remaining = totalSize - naturalSize - (spacing * (double)(childrenCount - 1)); if (remaining < 0) { // The box is not big enough to fit the widgets using its natural size. // We have to shrink the widgets. var sizePart = new SizeSplitter(-remaining, childrenCount); var toAdjust = new List <BoxPlacement> (); double adjustSize = 0; foreach (var bp in visibleChildren) { WidgetSize s; if (useLengthConstraint) { s = GetPreferredLengthForSize(mode, bp.Child, lengthConstraint); } else { s = GetPreferredSize(calcHeights, bp.Child); } bp.NextSize = s.NaturalSize - sizePart.NextSizePart(); if (bp.NextSize < s.MinSize) { adjustSize += (s.MinSize - bp.NextSize); bp.NextSize = s.MinSize; } else { toAdjust.Add(bp); } } sizePart = new SizeSplitter(adjustSize, toAdjust.Count); foreach (var bp in toAdjust) { bp.NextSize += sizePart.NextSizePart(); } } else { var expandRemaining = new SizeSplitter(remaining, nexpands); foreach (var bp in visibleChildren) { if ((bp.BoxMode & BoxMode.Expand) != 0) { bp.NextSize += expandRemaining.NextSizePart(); } } } }
void CalcDefaultSizes(SizeRequestMode mode, double totalSize, bool calcHeights, bool calcOffsets) { TablePlacement[] visibleChildren; Dictionary <int, WidgetSize> fixedSizesByCell; HashSet <int> cellsWithExpand; WidgetSize[] sizes; double spacing; CalcDefaultSizes(mode, calcHeights, out visibleChildren, out fixedSizesByCell, out cellsWithExpand, out sizes, out spacing); double naturalSize = 0; // Get the total natural size foreach (var ws in fixedSizesByCell.Values) { naturalSize += ws.NaturalSize; } double remaining = totalSize - naturalSize - spacing; if (remaining < 0) { // The box is not big enough to fit the widgets using its natural size. // We have to shrink the cells // List of cell indexes that we have to shrink var toShrink = new List <int> (fixedSizesByCell.Keys); // The total amount we have to shrink double shrinkSize = -remaining; while (toShrink.Count > 0 && shrinkSize > 0) { SizeSplitter sizePart = new SizeSplitter(shrinkSize, toShrink.Count); shrinkSize = 0; for (int i = 0; i < toShrink.Count; i++) { int n = toShrink[i]; double reduction = sizePart.NextSizePart(); WidgetSize size; fixedSizesByCell.TryGetValue(n, out size); size.NaturalSize -= reduction; if (size.NaturalSize < size.MinSize) { // If the widget can't be shrinked anymore, we remove it from the shrink list // and increment the remaining shrink size. We'll loop again and this size will be // substracted from the cells which can still be reduced shrinkSize += (size.MinSize - size.NaturalSize); size.NaturalSize = size.MinSize; toShrink.RemoveAt(i); i--; } fixedSizesByCell [n] = size; } } } else { int nexpands = cellsWithExpand.Count; var expandRemaining = new SizeSplitter(remaining, nexpands); foreach (var c in cellsWithExpand) { WidgetSize ws; fixedSizesByCell.TryGetValue(c, out ws); ws.NaturalSize += expandRemaining.NextSizePart(); fixedSizesByCell [c] = ws; } } for (int n = 0; n < visibleChildren.Length; n++) { var bp = visibleChildren[n]; double allocatedSize = 0; double cellOffset = 0; AttachOptions ops = calcHeights ? bp.YOptions : bp.XOptions; int start = GetStartAttach(bp, calcHeights); int end = GetEndAttach(bp, calcHeights); for (int i = start; i < end; i++) { WidgetSize ws; fixedSizesByCell.TryGetValue(i, out ws); allocatedSize += ws.NaturalSize; if (i != start) { allocatedSize += GetSpacing(i, calcHeights); } } if ((ops & AttachOptions.Fill) == 0) { double s = sizes[n].NaturalSize; if (s < allocatedSize) { cellOffset = (allocatedSize - s) / 2; allocatedSize = s; } } // cellOffset is the offset of the widget inside the cell. We store it in NextX/Y, and // will be used below to calculate the total offset of the widget if (calcHeights) { bp.NextHeight = allocatedSize; bp.NextY = cellOffset; } else { bp.NextWidth = allocatedSize; bp.NextX = cellOffset; } } if (calcOffsets) { var sortedChildren = visibleChildren.OrderBy(c => GetStartAttach(c, calcHeights)).ToArray(); var cells = fixedSizesByCell.OrderBy(c => c.Key); double offset = 0; int n = 0; foreach (var c in cells) { if (c.Key > 0) { offset += GetSpacing(c.Key, calcHeights); } while (n < sortedChildren.Length && GetStartAttach(sortedChildren[n], calcHeights) == c.Key) { // In the loop above we store the offset of the widget inside the cell in the NextX/Y field // so now we have to add (not just assign) the offset of the cell to NextX/Y if (calcHeights) { sortedChildren[n].NextY += offset; } else { sortedChildren[n].NextX += offset; } n++; } offset += c.Value.NaturalSize; } } }
/// <summary> /// Calculates size of each cell, taking into account their preferred size, expansion/fill requests, and the available space. /// Calculation is done only for the provided orientation (either height or width). /// </summary> /// <param name="mode">Mode.</param> /// <param name="availableSize">Total size available</param> /// <param name="calcOffsets"></param> void CalcCellSizes(CellSizeVector cellSizes, double availableSize, bool calcOffsets) { TablePlacement[] visibleChildren = cellSizes.visibleChildren; Dictionary <int, double> fixedSizesByCell = cellSizes.fixedSizesByCell; double[] sizes = cellSizes.sizes; double spacing = cellSizes.spacing; Orientation orientation = cellSizes.orientation; // Get the total natural size double naturalSize = fixedSizesByCell.Values.Sum(); double remaining = availableSize - naturalSize - spacing; if (availableSize - spacing <= 0) { foreach (var i in fixedSizesByCell.Keys.ToArray()) { fixedSizesByCell [i] = 0; } } else if (remaining < 0) { // The box is not big enough to fit the widgets using its natural size. // We have to shrink the cells. We do a proportional reduction // List of cell indexes that we have to shrink var toShrink = new List <int> (fixedSizesByCell.Keys); // The total amount we have to shrink double splitSize = (availableSize - spacing) / toShrink.Count; // We have to reduce all cells proportionally, but if a cell is much bigger that // its proportionally allocated space, then we reduce this one before the others var smallCells = fixedSizesByCell.Where(c => c.Value < splitSize); var belowSplitSize = smallCells.Sum(c => splitSize - c.Value); var bigCells = fixedSizesByCell.Where(c => c.Value > splitSize); var overSplitSize = bigCells.Sum(c => c.Value - splitSize); ReduceProportional(fixedSizesByCell, bigCells.Select(c => c.Key), overSplitSize - belowSplitSize); var newNatural = fixedSizesByCell.Sum(c => c.Value); ReduceProportional(fixedSizesByCell, fixedSizesByCell.Keys, (availableSize - spacing) - newNatural); RoundSizes(fixedSizesByCell); } else { // Distribute remaining space among the extensible widgets HashSet <int> cellsWithExpand = cellSizes.cellsWithExpand; int nexpands = cellsWithExpand.Count; var expandRemaining = new SizeSplitter(remaining, nexpands); foreach (var c in cellsWithExpand) { double ws; fixedSizesByCell.TryGetValue(c, out ws); ws += expandRemaining.NextSizePart(); fixedSizesByCell [c] = ws; } } // Calculate the offset of each widget, relative to the cell (so 0 means at the left/top of the cell). for (int n = 0; n < visibleChildren.Length; n++) { var bp = visibleChildren[n]; double allocatedSize = 0; double cellOffset = 0; int start = GetStartAttach(bp, orientation); int end = GetEndAttach(bp, orientation); for (int i = start; i < end; i++) { double ws; fixedSizesByCell.TryGetValue(i, out ws); allocatedSize += ws; if (i != start) { allocatedSize += GetSpacing(i, orientation); } } var al = bp.Child.AlignmentForOrientation(orientation); if (al != WidgetPlacement.Fill) { double s = sizes[n]; if (s < allocatedSize) { cellOffset = (allocatedSize - s) * al.GetValue(); allocatedSize = s; } } // cellOffset is the offset of the widget inside the cell. We store it in NextX/Y, and // will be used below to calculate the total offset of the widget if (orientation == Orientation.Vertical) { bp.NextHeight = allocatedSize; bp.NextY = cellOffset; } else { bp.NextWidth = allocatedSize; bp.NextX = cellOffset; } } if (calcOffsets) { // Calculate the final offset of each widget, relative to the table origin var sortedChildren = visibleChildren.OrderBy(c => GetStartAttach(c, orientation)).ToArray(); var cells = fixedSizesByCell.OrderBy(c => c.Key); double offset = 0; int n = 0; foreach (var c in cells) { if (c.Key > 0) { offset += GetSpacing(c.Key, orientation); } while (n < sortedChildren.Length && GetStartAttach(sortedChildren[n], orientation) == c.Key) { // In the loop above we store the offset of the widget inside the cell in the NextX/Y field // so now we have to add (not just assign) the offset of the cell to NextX/Y if (orientation == Orientation.Vertical) { sortedChildren[n].NextY += offset; } else { sortedChildren[n].NextX += offset; } n++; } offset += c.Value; } } }