/// <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.GetAlignmentForOrientation (orientation); if (!double.IsNaN (al)) { double s = sizes[n]; if (s < allocatedSize) { cellOffset = (allocatedSize - s) * al; 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; } } }
/// <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; } } }