internal protected virtual void OnReplaceChild(TablePlacement placement, Widget oldWidget, Widget newWidget) { if (oldWidget != null) { OnRemove(oldWidget); } OnAdd(newWidget, placement); }
int GetEndAttach(TablePlacement tp, bool calcHeight) { if (calcHeight) { return(tp.Bottom); } else { return(tp.Right); } }
int GetEndAttach(TablePlacement tp, Orientation orientation) { if (orientation == Orientation.Vertical) { return(tp.Bottom); } else { return(tp.Right); } }
int GetStartAttach(TablePlacement tp, bool calcHeight) { if (calcHeight) { return(tp.Top); } else { return(tp.Left); } }
int GetStartAttach(TablePlacement tp, Orientation orientation) { if (orientation == Orientation.Vertical) { return(tp.Top); } else { return(tp.Left); } }
public void Attach(Widget widget, int left, int right, int top, int bottom, AttachOptions xOptions, AttachOptions yOptions) { var p = new TablePlacement((EventSink)WidgetEventSink, widget) { Left = left, Right = right, Top = top, Bottom = bottom, XOptions = xOptions, YOptions = yOptions }; children.Add(p); }
public void Add(Widget widget, int left, int top, int rowspan = 1, int colspan = 1, bool hexpand = false, bool vexpand = false, WidgetPlacement hpos = WidgetPlacement.Fill, WidgetPlacement vpos = WidgetPlacement.Fill, double marginLeft = -1, double marginTop = -1, double marginRight = -1, double marginBottom = -1, double margin = -1) { if (vpos != default(WidgetPlacement)) { widget.VerticalPlacement = vpos; } if (hpos != default(WidgetPlacement)) { widget.HorizontalPlacement = hpos; } widget.ExpandHorizontal = hexpand; widget.ExpandVertical = vexpand; if (margin != -1) { widget.Margin = margin; } if (marginLeft != -1) { widget.MarginLeft = marginLeft; } if (marginTop != -1) { widget.MarginTop = marginTop; } if (marginTop != -1) { widget.MarginRight = marginRight; } if (marginBottom != -1) { widget.MarginBottom = marginBottom; } var p = new TablePlacement((WidgetBackendHost)BackendHost, widget) { Left = left, Right = left + colspan, Top = top, Bottom = top + rowspan }; placements.Add(p); }
TablePlacement[] VisibleChildren() { TablePlacement[] result = new TablePlacement[children.Count]; int j = 0; for (int i = 0; i < children.Count; i++) { var item = children[i]; if (item.Child.Visible) { result[j] = item; j++; } } if (j != children.Count) { Array.Resize(ref result, j); } return(result); }
public void Attach(Widget widget, int left, int right, int top, int bottom, AttachOptions?xOptions = null, AttachOptions?yOptions = null) { if (xOptions != null) { widget.ExpandHorizontal = (xOptions.Value & AttachOptions.Expand) != 0; if ((xOptions.Value & AttachOptions.Fill) != 0) { widget.HorizontalPlacement = WidgetPlacement.Fill; } else { widget.HorizontalPlacement = WidgetPlacement.Center; } } if (yOptions != null) { widget.ExpandVertical = (yOptions.Value & AttachOptions.Expand) != 0; if ((yOptions.Value & AttachOptions.Fill) != 0) { widget.VerticalPlacement = WidgetPlacement.Fill; } else { widget.VerticalPlacement = WidgetPlacement.Center; } } var p = new TablePlacement((WidgetBackendHost)BackendHost, widget) { Left = left, Right = right, Top = top, Bottom = bottom }; placements.Add(p); }
void OnChildChanged(TablePlacement placement, object hint) { OnPreferredSizeChanged(); }
void OnAdd(Widget child, TablePlacement placement) { RegisterChild(child); Backend.Add((IWidgetBackend)GetBackend(child)); OnPreferredSizeChanged(); }
void CalcDefaultSizes(SizeRequestMode mode, bool calcHeights, out TablePlacement[] visibleChildren, out Dictionary <int, WidgetSize> fixedSizesByCell, out HashSet <int> cellsWithExpand, out WidgetSize[] sizes, out double spacing) { bool useLengthConstraint = mode == SizeRequestMode.HeightForWidth && calcHeights || mode == SizeRequestMode.WidthForHeight && !calcHeights; visibleChildren = children.Where(b => b.Child.Visible).ToArray(); int lastCell = 0; fixedSizesByCell = new Dictionary <int, WidgetSize> (); cellsWithExpand = new HashSet <int> (); HashSet <int> cellsWithWidget = new HashSet <int> (); sizes = new WidgetSize [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, calcHeights); int end = GetEndAttach(bp, calcHeights); if (end > lastCell) { lastCell = end; } // Check if the cell is expandable and store the value AttachOptions ops = calcHeights ? bp.YOptions : bp.XOptions; for (int i = start; i < end; i++) { cellsWithWidget.Add(i); if ((ops & AttachOptions.Expand) != 0) { cellsWithExpand.Add(i); } } WidgetSize s; if (useLengthConstraint) { s = GetPreferredLengthForSize(mode, bp.Child, calcHeights ? bp.NextWidth : bp.NextHeight); } else { s = GetPreferredSize(calcHeights, bp.Child); } sizes [n] = s; if (end == start + 1) { // The widget only takes one cell. Store its size if it is the biggest bool changed = false; WidgetSize fs; fixedSizesByCell.TryGetValue(start, out fs); if (s.MinSize > fs.MinSize) { fs.MinSize = s.MinSize; changed = true; } if (s.NaturalSize > fs.NaturalSize) { fs.NaturalSize = s.NaturalSize; 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, WidgetSize[]> growSizes = new Dictionary <TablePlacement, WidgetSize[]> (); for (int n = 0; n < visibleChildren.Length; n++) { var bp = visibleChildren[n]; int start = GetStartAttach(bp, calcHeights); int end = GetEndAttach(bp, calcHeights); if (end == start + 1) { continue; } widgetsToAdjust.Add(bp); WidgetSize fixedSize = new WidgetSize(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++) { WidgetSize fs; fixedSizesByCell.TryGetValue(c, out fs); fixedSize += fs; if (c != start && c != end) { spanSpacing += GetSpacing(c, calcHeights); } } // 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 WidgetSize sizeToGrow = sizes [n] - fixedSize - new WidgetSize(spanSpacing); WidgetSize sizeToGrowPart = new WidgetSize(sizeToGrow.MinSize / (end - start), sizeToGrow.NaturalSize / (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. WidgetSize[] widgetGrowSizes = new WidgetSize [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) { // Get a list of all widgets that cover this cell var colCells = widgetsToAdjust.Where(bp => GetStartAttach(bp, calcHeights) <= n && GetEndAttach(bp, calcHeights) > n).ToArray(); WidgetSize maxv = new WidgetSize(0); TablePlacement maxtMin = null; TablePlacement maxtNatural = null; // Find the widget that requires the maximum size for this cell foreach (var bp in colCells) { WidgetSize cv = growSizes[bp][n - GetStartAttach(bp, calcHeights)]; if (cv.MinSize > maxv.MinSize) { maxv.MinSize = cv.MinSize; maxtMin = bp; } if (cv.NaturalSize > maxv.NaturalSize) { maxv.NaturalSize = cv.NaturalSize; maxtNatural = bp; } } // Adjust the required size of all widgets of the cell (excluding the widget with the max size) foreach (var bp in colCells) { WidgetSize[] widgetGrows = growSizes[bp]; int cellIndex = n - GetStartAttach(bp, calcHeights); if (bp != maxtMin) { double cv = widgetGrows[cellIndex].MinSize; // splitExtraSpace is the additional space that the widget can take from this cell (because there is a widget // that is requiring more space), split among all other cells of the widget double splitExtraSpace = (maxv.MinSize - cv) / (widgetGrows.Length - 1); for (int i = 0; i < widgetGrows.Length; i++) { widgetGrows[i].MinSize -= splitExtraSpace; } } if (bp != maxtNatural) { double cv = widgetGrows[cellIndex].NaturalSize; double splitExtraSpace = (maxv.NaturalSize - cv) / (widgetGrows.Length - 1); for (int i = 0; i < widgetGrows.Length; i++) { widgetGrows[i].NaturalSize -= splitExtraSpace; } } } } // Find the maximum size-to-grow for each cell Dictionary <int, WidgetSize> finalGrowTable = new Dictionary <int, WidgetSize> (); foreach (var bp in widgetsToAdjust) { int start = GetStartAttach(bp, calcHeights); int end = GetEndAttach(bp, calcHeights); WidgetSize[] widgetGrows = growSizes[bp]; for (int n = start; n < end; n++) { WidgetSize curGrow; finalGrowTable.TryGetValue(n, out curGrow); var val = widgetGrows [n - start]; if (val.MinSize > curGrow.MinSize) { curGrow.MinSize = val.MinSize; } if (val.NaturalSize > curGrow.NaturalSize) { curGrow.NaturalSize = val.NaturalSize; } finalGrowTable [n] = curGrow; } } // Add the final size-to-grow to the fixed sizes calculated at the begining foreach (var it in finalGrowTable) { WidgetSize 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, calcHeights); } } }
/// <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.Child.ExpandsForOrientation(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 }); }