TablePlacement[] VisibleChildren() { TablePlacement[] result = new TablePlacement[placements.Count]; int j = 0; for (int i = 0; i < placements.Count; i++) { var item = placements[i]; if (item.Child.Visible) { result[j] = item; j++; } } if (j != placements.Count) { Array.Resize (ref result, j); } return result; }
int GetStartAttach(TablePlacement tp, Orientation orientation) { if (orientation == Orientation.Vertical) return tp.Top; else return tp.Left; }
int GetEndAttach(TablePlacement tp, Orientation orientation) { if (orientation == Orientation.Vertical) return tp.Bottom; else return tp.Right; }
// Get the preferred size of each child widget, including the margins Size[] GetPreferredChildrenSizes(TablePlacement[] visibleChildren, bool useWidthConstraint, bool useHeightConstraint) { var sizes = new Size [visibleChildren.Length]; for (int n=0; n<visibleChildren.Length; n++) { var bp = visibleChildren[n]; Size s; if (useWidthConstraint) s = bp.GetPreferredSize (bp.NextWidth - bp.LeftMargin - bp.RightMargin, double.PositiveInfinity); else if (useHeightConstraint) s = bp.GetPreferredSize (double.PositiveInfinity, bp.NextHeight - bp.TopMargin - bp.BottomMargin); else s = bp.GetPreferredSize (double.PositiveInfinity, double.PositiveInfinity); s.Width += bp.LeftMargin + bp.RightMargin; s.Height += bp.TopMargin + bp.BottomMargin; sizes [n] = s; } return sizes; }
/// <summary> /// Calculates the preferred size of each cell (either height or width, depending on the provided orientation) /// </summary> /// <param name="mode">Mode.</param> /// <param name="orientation">Wether we are calculating the vertical size or the horizontal size</param> /// <param name="visibleChildren">List of children that are visible, and for which the size is being calculated.</param> /// <param name="fixedSizesByCell">Cells which have a fixed size</param> /// <param name="cellsWithExpand">Cells which are expandable.</param> /// <param name="sizes">Calculated size of each cell</param> /// <param name="spacing">Spacing to use for each cell</param> CellSizeVector CalcPreferredCellSizes(TablePlacement[] visibleChildren, Size[] childrenSizes, Orientation orientation) { Dictionary<int,double> fixedSizesByCell; HashSet<int> cellsWithExpand; double[] sizes; double spacing; int lastCell = 0; fixedSizesByCell = new Dictionary<int, double> (); cellsWithExpand = new HashSet<int> (); HashSet<int> cellsWithWidget = new HashSet<int> (); sizes = new double [visibleChildren.Length]; // Get the size of each widget and store the fixed sizes for widgets which don't span more than one cell for (int n=0; n<visibleChildren.Length; n++) { var bp = visibleChildren[n]; int start = GetStartAttach (bp, orientation); int end = GetEndAttach (bp, orientation); if (end > lastCell) lastCell = end; // Check if the cell is expandable and store the value bool expand = bp.GetExpandsForOrientation (orientation); for (int i=start; i < end; i++) { cellsWithWidget.Add (i); if (expand) cellsWithExpand.Add (i); } double s = orientation == Orientation.Vertical ? childrenSizes[n].Height : childrenSizes[n].Width; sizes [n] = s; if (end == start + 1) { // The widget only takes one cell. Store its size if it is the biggest bool changed = false; double fs; if (!fixedSizesByCell.TryGetValue (start, out fs)) changed = true; if (s > fs) { fs = s; changed = true; } if (changed) fixedSizesByCell [start] = fs; } } // For widgets that span more than one cell, calculate the floating size, that is, the size // which is not taken by other fixed size widgets List<TablePlacement> widgetsToAdjust = new List<TablePlacement> (); Dictionary<TablePlacement,double[]> growSizes = new Dictionary<TablePlacement, double[]> (); for (int n=0; n<visibleChildren.Length; n++) { var bp = visibleChildren[n]; int start = GetStartAttach (bp, orientation); int end = GetEndAttach (bp, orientation); if (end == start + 1) continue; widgetsToAdjust.Add (bp); double fixedSize = 0; // We are going to calculate the spacing included in the widget's span of cells // (there is spacing between each cell) double spanSpacing = 0; for (int c = start; c < end; c++) { double fs; fixedSizesByCell.TryGetValue (c, out fs); fixedSize += fs; if (c != start && c != end) spanSpacing += GetSpacing (c, orientation); } // sizeToGrow is the size that the whole cell span has to grow in order to fit // this widget. We substract the spacing between cells because that space will // be used by the widget, so we don't need to allocate more size for it double sizeToGrow = sizes [n] - fixedSize - spanSpacing; double sizeToGrowPart = sizeToGrow / (end - start); // Split the size to grow between the cells of the widget. We need to know how much size the widget // requires for each cell it covers. double[] widgetGrowSizes = new double [end - start]; for (int i=0; i<widgetGrowSizes.Length; i++) widgetGrowSizes [i] = sizeToGrowPart; growSizes[bp] = widgetGrowSizes; } // Now size-to-grow values have to be adjusted. For example, let's say widget A requires 100px for column 1 and 100px for column 2, and widget B requires // 60px for column 2 and 60px for column 3. So the widgets are overlapping at column 2. Since A requires at least 100px in column 2, it means that B can assume // that it will have 100px available in column 2, which means 40px more than it requested. Those extra 40px can then be substracted from the 60px that // it required for column 3. foreach (var n in cellsWithWidget) { double maxv = 0; TablePlacement maxtNatural = null; // Find the widget that requires the maximum size for this cell foreach (var bp in widgetsToAdjust) { // could be expressed as where clause, but this is faster and performance matters here if (GetStartAttach (bp, orientation) <= n && GetEndAttach (bp, orientation) > n) { double cv = growSizes[bp][n - GetStartAttach (bp, orientation)]; if (cv > maxv) { maxv = cv; maxtNatural = bp; } } } // Adjust the required size of all widgets of the cell (excluding the widget with the max size) foreach (var bp in widgetsToAdjust) { if (GetStartAttach (bp, orientation) <= n && GetEndAttach (bp, orientation) > n) { double[] widgetGrows = growSizes[bp]; int cellIndex = n - GetStartAttach (bp, orientation); if (bp != maxtNatural) { double cv = widgetGrows[cellIndex]; double splitExtraSpace = (maxv - cv) / (widgetGrows.Length - 1); for (int i=0; i<widgetGrows.Length; i++) widgetGrows[i] -= splitExtraSpace; } } } } // Find the maximum size-to-grow for each cell Dictionary<int,double> finalGrowTable = new Dictionary<int, double> (); foreach (var bp in widgetsToAdjust) { int start = GetStartAttach (bp, orientation); int end = GetEndAttach (bp, orientation); double[] widgetGrows = growSizes[bp]; for (int n=start; n<end; n++) { double curGrow; finalGrowTable.TryGetValue (n, out curGrow); var val = widgetGrows [n - start]; if (val > curGrow) curGrow = val; finalGrowTable [n] = curGrow; } } // Add the final size-to-grow to the fixed sizes calculated at the begining foreach (var it in finalGrowTable) { double ws; fixedSizesByCell.TryGetValue (it.Key, out ws); fixedSizesByCell [it.Key] = it.Value + ws; } spacing = 0; for (int n=1; n<lastCell; n++) { if (cellsWithWidget.Contains (n)) spacing += GetSpacing (n, orientation); } return new CellSizeVector () { visibleChildren = visibleChildren, fixedSizesByCell = fixedSizesByCell, cellsWithExpand = cellsWithExpand, sizes = sizes, spacing = spacing, orientation = orientation }; }