/// <summary> /// Calculates grid width and individual column widths. /// </summary> public Tuple <nfloat, nfloat[]> CalculateAbsoluteColumnWidth(Grid grid) { var absoluteColumnWidth = new nfloat[this.ColumnDefinitions.Count()]; var visibleCols = Cells.Where(c => c.IncludeInAutoWidthSizeCalcs).Select(c => c.Position.Column).Distinct(); int numVisibleCols = visibleCols.Count(); // Calculate full height of grid nfloat totalWidth; var minColWidth = new nfloat[this.ColumnDefinitions.Count()]; if (grid.AutoWidth) { LogLine($"CalculateAbsoluteColumnWidth AutoWidth"); var maxColWidth = new nfloat[this.ColumnDefinitions.Count()]; int maxColSpan = grid.CurrentLayout.cells.Max(c => c.Position.ColumnSpan); for (int colSpan = 1; colSpan <= maxColSpan; colSpan++) { for (int row = 0; row < this.RowDefinitions.Count(); row++) { for (int col = 0; col < this.ColumnDefinitions.Count(); col++) { var colDef = this.ColumnDefinitions[col]; var cell = FindCell(col, row); if (cell == null) { continue; } if (cell.IncludeInAutoWidthSizeCalcs && cell.Position.ColumnSpan == colSpan) { nfloat cellWidth = colDef.SizeType == SizeType.Auto && cell.Position.Horizontal == Alignment.Stretched ? 0f : colDef.SizeType == SizeType.Fixed ? colDef.Size : cell.AutoSizeSize.Width; cellWidth += cell.Position.Margin.Width(); LogLine($" {cell.Position} cellWidth={cellWidth}"); if (colSpan == 1) { maxColWidth[col] = NMath.Max(maxColWidth[col], cellWidth); } else { // Need to ensure the other columns we span have enough // width to account for this spanned column. nfloat spanWidth = 0; for (int col2 = cell.Position.Column; col2 < cell.Position.Column + colSpan; col2++) { spanWidth += maxColWidth[col2]; } if (cellWidth > spanWidth) { bool hasZeroColWidth = false; for (int col2 = cell.Position.Column; col2 < cell.Position.Column + colSpan; col2++) { if (maxColWidth[col2] == 0) { hasZeroColWidth = true; } } if (hasZeroColWidth) { // Cop out: just size equally for (int col2 = cell.Position.Column; col2 < cell.Position.Column + colSpan; col2++) { minColWidth[col2] = maxColWidth[col2] = cellWidth / colSpan; } } else { // Increase size of columns proportionatly nfloat colWidthUpsizeRation = cellWidth / spanWidth; for (int col2 = cell.Position.Column; col2 < cell.Position.Column + colSpan; col2++) { maxColWidth[col2] *= colWidthUpsizeRation; minColWidth[col2] = maxColWidth[col2]; } } } } } } } } #if ENABLE_DEBUG_LOG LogLine("minColWidth:"); for (int col = 0; col < this.ColumnDefinitions.Count(); col++) { LogLine($" col {col}: {minColWidth[col]}"); } #endif totalWidth = maxColWidth.Sum(w => (float)w); int numCols = maxColWidth.Count(w => w > 0); totalWidth += numVisibleCols * Spacing + Padding.Left + Padding.Right; } else { totalWidth = grid.Frame.Width; } // Track available col width available for percent-based sized roiws // Add width of fixed size cols nfloat fixedWidth = this.ColumnDefinitions.Where((d) => d.Size > 1).Select(d => d.Size).Sum(); nfloat remaining = totalWidth - fixedWidth; // Add width of auto sized columns for (int column = 0; column < this.ColumnDefinitions.Count(); column++) { var definition = this.ColumnDefinitions.ElementAt(column); if (definition.Size == -1) { var autoSizedCells = Cells.Where(c => c.Position.Column == column && c.Position.ColumnSpan == 1 && c.IncludeInAutoWidthSizeCalcs && c.Position.Horizontal != Alignment.Stretched); if (autoSizedCells.Any()) { nfloat colWidth = autoSizedCells.Max(c => c.AutoSizeSize.Width + c.Position.Margin.Width()); colWidth = NMath.Max(minColWidth[column], colWidth); absoluteColumnWidth[column] = colWidth; remaining -= colWidth; } } } // Add size of padding and spacing remaining -= this.Padding.Left + this.Padding.Right; remaining -= (numVisibleCols - 1) * this.Spacing; remaining = (nfloat)Math.Max(0, remaining); for (int column = 0; column < this.ColumnDefinitions.Count(); column++) { var definition = this.ColumnDefinitions.ElementAt(column); if (definition.Size != -1) { bool includeCol = Cells.Where(c => c.Position.Column == column).Any(c => c.IncludeInAutoHeightSizeCalcs); nfloat colWidth = !includeCol ? 0 : definition.Size > 1 ? definition.Size : definition.Size * remaining; absoluteColumnWidth[column] = NMath.Max(minColWidth[column], colWidth); } } // In auto width mode, can now take into account absolute and auto sized // columns if (grid.AutoWidth) { totalWidth = (nfloat)( absoluteColumnWidth.Sum(w => w) + Padding.Left + this.Padding.Right + (numVisibleCols - 1) * this.Spacing ); } LogLine($"totalWidth={totalWidth}"); return(new Tuple <nfloat, nfloat[]>(totalWidth, absoluteColumnWidth)); }
/// <summary> /// Calculates grid height and individual row heights. /// </summary> public Tuple <nfloat, nfloat[]> CalculateAbsoluteRowHeight(Grid grid) { var absoluteRowHeight = new nfloat[this.RowDefinitions.Count()]; var visibleRows = Cells.Where(c => c.IncludeInAutoHeightSizeCalcs).Select(c => c.Position.Row).Distinct(); int numVisibleRows = visibleRows.Count(); // Calculate full height of grid nfloat totalHeight; var minRowHeight = new nfloat[this.RowDefinitions.Count()]; if (grid.AutoHeight) { LogLine($"CalculateAbsoluteRowHeight AutoHeight"); var maxRowHeight = new nfloat[this.RowDefinitions.Count()]; int maxRowSpan = grid.CurrentLayout.cells.Max(c => c.Position.RowSpan); for (int rowSpan = 1; rowSpan <= maxRowSpan; rowSpan++) { for (int col = 0; col < this.ColumnDefinitions.Count(); col++) { for (int row = 0; row < this.RowDefinitions.Count(); row++) { var rowDef = this.RowDefinitions[row]; var cell = FindCell(col, row); if (cell == null) { continue; } if (cell.IncludeInAutoHeightSizeCalcs && cell.Position.RowSpan == rowSpan) { nfloat cellHeight = rowDef.SizeType == SizeType.Auto && cell.Position.Vertical == Alignment.Stretched ? 0f : rowDef.SizeType == SizeType.Fixed ? rowDef.Size : cell.AutoSizeSize.Height; cellHeight += cell.Position.Margin.Height(); LogLine($" {cell.Position} cellHeight={cellHeight}"); if (rowSpan == 1) { maxRowHeight[row] = NMath.Max(maxRowHeight[row], cellHeight); } else { // Need to ensure the other rows we span have enough // Height to account for this spanned column. nfloat spanHeight = 0; for (int row2 = cell.Position.Row; row2 < cell.Position.Row + rowSpan; row2++) { spanHeight += maxRowHeight[row2]; } if (cellHeight > spanHeight) { bool hasZeroRowHeight = false; for (int row2 = cell.Position.Row; row2 < cell.Position.Row + rowSpan; row2++) { if (maxRowHeight[row2] == 0) { hasZeroRowHeight = true; } } if (hasZeroRowHeight) { // Cop out: just size equally for (int row2 = cell.Position.Row; row2 < cell.Position.Row + rowSpan; row2++) { minRowHeight[row2] = maxRowHeight[row2] = cellHeight / rowSpan; } } else { // Increase size of columns proportionatly nfloat colHeightUpsizeRation = cellHeight / spanHeight; for (int row2 = cell.Position.Row; row2 < cell.Position.Row + rowSpan; row2++) { maxRowHeight[row2] *= colHeightUpsizeRation; minRowHeight[row2] = maxRowHeight[row2]; } } } } } } } } #if ENABLE_DEBUG_LOG LogLine("minRowHeight:"); for (int row = 0; row < this.RowDefinitions.Count(); row++) { LogLine($" row {row}: {minRowHeight[row]}"); } #endif totalHeight = maxRowHeight.Sum(h => (float)h); int numRows = maxRowHeight.Count(h => h > 0); totalHeight += (numVisibleRows - 1) * Spacing + Padding.Top + Padding.Bottom; } else { totalHeight = grid.Frame.Height; } // Track available row Height available for percent-based sized roiws // Add Height of fixed size cols nfloat fixedHeight = this.RowDefinitions.Where((d) => d.Size > 1).Select(d => d.Size).Sum(); nfloat remaining = totalHeight - fixedHeight; // Add Height of auto sized rows for (int row = 0; row < this.RowDefinitions.Count(); row++) { var definition = this.RowDefinitions.ElementAt(row); if (definition.Size == -1) { var autoSizedCells = Cells.Where(c => c.Position.Row == row && c.Position.RowSpan == 1 && c.IncludeInAutoHeightSizeCalcs && c.Position.Vertical != Alignment.Stretched); if (autoSizedCells.Any()) { nfloat rowHeight = autoSizedCells.Max(c => c.AutoSizeSize.Height + c.Position.Margin.Height()); rowHeight = NMath.Max(minRowHeight[row], rowHeight); absoluteRowHeight[row] = rowHeight; remaining -= rowHeight; } } } // Add size of padding and spacing remaining -= this.Padding.Top + this.Padding.Bottom; remaining -= (numVisibleRows - 1) * this.Spacing; remaining = (nfloat)Math.Max(0, remaining); for (int row = 0; row < this.RowDefinitions.Count(); row++) { var definition = this.RowDefinitions.ElementAt(row); if (definition.Size != -1) { bool includeRow = Cells.Where(c => c.Position.Row == row).Any(c => c.IncludeInAutoHeightSizeCalcs); nfloat rowHeight = !includeRow ? 0 : definition.Size > 1 ? definition.Size : definition.Size * remaining; absoluteRowHeight[row] = NMath.Max(minRowHeight[row], rowHeight); } } // In auto height mode, can now take into account absolute and auto sized // rows if (grid.AutoHeight) { totalHeight = (nfloat)( absoluteRowHeight.Sum(h => h) + Padding.Top + this.Padding.Bottom + (numVisibleRows - 1) * this.Spacing ); } LogLine($"totalHeight={totalHeight}"); return(new Tuple <nfloat, nfloat[]>(totalHeight, absoluteRowHeight)); }
private nfloat[] CalculateColumnWidths(CGSize availableSpace) { var widthsByRow = new List<nfloat[]>(); foreach (var row in rows) { for (var i = 0; i < widths.Length; i++) { var width = widths[i]; var sizeRow = new nfloat[widths.Length]; switch (width.Style) { case TableWidthStyle.SizeToFit: sizeRow[i] = (nfloat)row[i].SizeThatFits(new CGSize(nfloat.MaxValue, nfloat.MaxValue)).Width; break; case TableWidthStyle.Fixed: sizeRow[i] = width.Value; break; case TableWidthStyle.Percentage: case TableWidthStyle.Weight: sizeRow[i] = 0; break; } widthsByRow.Add(sizeRow); } } var calculatedWidths = new nfloat[widths.Length]; for (var i = 0; i < widths.Length; i++) { calculatedWidths[i] = (nfloat)widthsByRow.Max(x => x[i]); } var bespokeWidth = calculatedWidths.Sum(x => x) + GetPaddingAndSpacingWidth(); var remainingWidth = availableSpace.Width - bespokeWidth; var weightWidth = (nfloat)(remainingWidth - (nfloat)((nfloat)remainingWidth * widths.Where(x => x.Style == TableWidthStyle.Percentage).Sum(x => x.Value) / 100)); var totalWeight = widths.Where(x => x.Style == TableWidthStyle.Weight).Sum(x => x.Value); var individualWeightWidth = weightWidth / totalWeight; var oddManOutWeightWidth = weightWidth - individualWeightWidth * totalWeight; var result = new nfloat[widths.Length]; for (var i = 0; i < result.Length; i++) { var width = widths[i]; switch (width.Style) { case TableWidthStyle.Fixed: case TableWidthStyle.SizeToFit: result[i] = calculatedWidths[i]; break; case TableWidthStyle.Percentage: result[i] = (nfloat)((nfloat)width.Value / 100 * remainingWidth); break; case TableWidthStyle.Weight: result[i] = individualWeightWidth * width.Value + oddManOutWeightWidth; oddManOutWeightWidth = 0; break; } } return result; }