/// <summary> /// Applies layout changes to a given box and its children. /// </summary> public override void ApplyHorizontalLayout([NotNull] LayoutState state, [NotNull] LayoutState.LayoutLevel level) { var node = level.BranchRoot; var nodeState = node.State; if (node.AssistantsRoot != null) { LayoutAlgorithm.HorizontalLayout(state, node.AssistantsRoot); } // first, perform horizontal layout for every node in this column for (var row = 0; row < nodeState.NumberOfSiblings; row++) { var child = node.Children[row]; // re-enter layout algorithm for child branch // siblings are guaranteed not to offend each other LayoutAlgorithm.HorizontalLayout(state, child); } // now align the column var edges = LayoutAlgorithm.AlignHorizontalCenters(state, level, EnumerateColumn(node)); if (node.Level > 0 && node.ChildCount > 0) { var rect = node.State; double diff; if (ParentAlignment == BranchParentAlignment.Left) { var desiredLeft = rect.CenterH + ParentConnectorShield / 2; diff = desiredLeft - edges.From; } else if (ParentAlignment == BranchParentAlignment.Right) { var desiredRight = rect.CenterH - ParentConnectorShield / 2; diff = desiredRight - edges.To; } else { throw new InvalidOperationException("Invalid ParentAlignment setting"); } // vertical connector from parent LayoutAlgorithm.MoveChildrenOnly(state, level, diff); // spacer for the vertical carrier var verticalSpacer = node.Level > 0 ? node.Children[node.ChildCount - 1] : null; if (verticalSpacer != null) { var spacerTop = node.State.Bottom; var spacerBottom = node.Children[node.ChildCount - 2].State.Bottom; verticalSpacer.State.AdjustSpacer( rect.CenterH - ParentConnectorShield / 2, spacerTop, ParentConnectorShield, spacerBottom - spacerTop); state.MergeSpacer(verticalSpacer); } } }
/// <summary> /// Applies layout changes to a given box and its children. /// </summary> public override void ApplyHorizontalLayout([NotNull] LayoutState state, [NotNull] LayoutState.LayoutLevel level) { var node = level.BranchRoot; if (node.AssistantsRoot != null) { LayoutAlgorithm.HorizontalLayout(state, node.AssistantsRoot); } for (var i = 0; i < node.State.NumberOfSiblings; i++) { var child = node.Children[i]; // re-enter layout algorithm for child branch LayoutAlgorithm.HorizontalLayout(state, child); } if (node.Level > 0 && node.ChildCount > 0) { var rect = node.State; var leftmost = node.Children[0].State.CenterH; var rightmost = node.Children[node.State.NumberOfSiblings - 1].State.CenterH; var desiredCenter = node.State.NumberOfSiblings == 1 || ParentAlignment == BranchParentAlignment.Center ? leftmost + (rightmost - leftmost) / 2 : ParentAlignment == BranchParentAlignment.Left ? leftmost + ChildConnectorHookLength : rightmost - ChildConnectorHookLength; var center = rect.CenterH; var diff = center - desiredCenter; LayoutAlgorithm.MoveChildrenOnly(state, level, diff); // vertical connector from parent var verticalSpacer = node.Children[node.State.NumberOfSiblings]; verticalSpacer.State.AdjustSpacer( center - ParentConnectorShield / 2, rect.Bottom, ParentConnectorShield, node.Children[0].State.SiblingsRowV.From - rect.Bottom); state.MergeSpacer(verticalSpacer); // horizontal protector var firstInRow = node.Children[0].State; var horizontalSpacer = node.Children[node.State.NumberOfSiblings + 1]; horizontalSpacer.State.AdjustSpacer( firstInRow.Left, firstInRow.SiblingsRowV.From - ParentChildSpacing, node.Children[node.State.NumberOfSiblings - 1].State.Right - firstInRow.Left, ParentChildSpacing); state.MergeSpacer(horizontalSpacer); } }
/// <summary> /// Applies layout changes to a given box and its children. /// </summary> public override void ApplyHorizontalLayout([NotNull] LayoutState state, [NotNull] LayoutState.LayoutLevel level) { var node = level.BranchRoot; foreach (var child in node.Children) { // re-enter layout algorithm for child branch LayoutAlgorithm.HorizontalLayout(state, child); } if (node.ChildCount > 0) { if (Orientation == StackOrientation.SingleRowHorizontal) { // now auto-extend or contract the parent box var width = node.Children[node.State.NumberOfSiblings - 1].State.Right - node.Children[0].State.Left; node.State.Size = new Size(Math.Max(node.State.Size.Width, width), node.State.Size.Height); // now position children under the parent var center = (node.Children[0].State.Left + node.Children[node.ChildCount - 1].State.Right) / 2; var desiredCenter = node.State.CenterH; var diff = desiredCenter - center; LayoutAlgorithm.MoveChildrenOnly(state, level, diff); } else if (Orientation == StackOrientation.SingleColumnVertical) { LayoutAlgorithm.AlignHorizontalCenters(state, level, node.Children); // now position children under the parent var center = node.Children[0].State.CenterH; var desiredCenter = node.State.CenterH; var diff = desiredCenter - center; LayoutAlgorithm.MoveChildrenOnly(state, level, diff); } } }
/// <summary> /// Applies layout changes to a given box and its children. /// </summary> public override void ApplyHorizontalLayout([NotNull] LayoutState state, [NotNull] LayoutState.LayoutLevel level) { var node = level.BranchRoot; if (node.Level == 0) { node.State.SiblingsRowV = new Dimensions(node.State.Top, node.State.Bottom); } var left = true; var countOnThisSide = 0; var maxOnLeft = MaxOnLeft(node); for (var i = 0; i < node.State.NumberOfSiblings; i++) { var child = node.Children[i]; LayoutAlgorithm.HorizontalLayout(state, child); // we go top-bottom to layout left side of the group, // then add a carrier protector // then top-bottom to fill right side of the group if (++countOnThisSide == maxOnLeft) { if (left) { // horizontally align children in left pillar LayoutAlgorithm.AlignHorizontalCenters(state, level, EnumerateSiblings(node, 0, maxOnLeft)); left = false; countOnThisSide = 0; var rightmost = double.MinValue; for (var k = 0; k <= i; k++) { rightmost = Math.Max(rightmost, node.Children[k].State.BranchExterior.Right); } // vertical spacer does not have to be extended to the bottom of the lowest branch, // unless the lowest branch on the right side has some children and is expanded if (node.State.NumberOfSiblings % 2 != 0) { rightmost = Math.Max(rightmost, child.State.Right); } else { var opposite = node.Children[node.State.NumberOfSiblings - 1]; if (opposite.Element.IsCollapsed || opposite.ChildCount == 0) { rightmost = Math.Max(rightmost, child.State.Right); } else { rightmost = Math.Max(rightmost, child.State.BranchExterior.Right); } } // integrate protector for group's vertical carrier // it must prevent boxes on the right side from overlapping the middle vertical connector, // so protector's height must be set to height of this entire assistant branch var spacer = node.Children[node.State.NumberOfSiblings]; spacer.State.AdjustSpacer( rightmost, node.State.Bottom, ParentConnectorShield, node.State.BranchExterior.Bottom - node.State.Bottom ); level.Boundary.MergeFrom(spacer); } } } // horizontally align children in right pillar LayoutAlgorithm.AlignHorizontalCenters(state, level, EnumerateSiblings(node, maxOnLeft, node.State.NumberOfSiblings)); // align children under parent if (node.Level > 0 && node.State.NumberOfSiblings > 0) { double diff; var carrier = node.Children[node.State.NumberOfSiblings].State.CenterH; var desiredCenter = node.State.CenterH; diff = desiredCenter - carrier; LayoutAlgorithm.MoveChildrenOnly(state, level, diff); } }
/// <summary> /// Applies layout changes to a given box and its children. /// </summary> public override void ApplyHorizontalLayout([NotNull] LayoutState state, [NotNull] LayoutState.LayoutLevel level) { var node = level.BranchRoot; if (node.State.NumberOfSiblings <= MaxSiblingsPerRow) { // fall back to linear layout, only have one row of boxes base.ApplyHorizontalLayout(state, level); return; } if (node.AssistantsRoot != null) { LayoutAlgorithm.HorizontalLayout(state, node.AssistantsRoot); } for (var col = 0; col < node.State.NumberOfSiblingColumns; col++) { // first, perform horizontal layout for every node in this column for (var row = 0; row < node.State.NumberOfSiblingRows; row++) { var ix = row * node.State.NumberOfSiblingColumns + col; if (ix >= node.State.NumberOfSiblings) { break; } var child = node.Children[ix]; // re-enter layout algorithm for child branch LayoutAlgorithm.HorizontalLayout(state, child); } LayoutAlgorithm.AlignHorizontalCenters(state, level, EnumerateColumn(node, col)); } // now align children under parent var rect = node.State; var spacer = node.Children[node.State.NumberOfSiblingColumns / 2]; var desiredCenter = spacer.State.CenterH; var diff = rect.CenterH - desiredCenter; LayoutAlgorithm.MoveChildrenOnly(state, level, diff); // vertical connector from parent var verticalSpacer = node.Children[node.State.NumberOfSiblings]; verticalSpacer.State.AdjustSpacer( rect.CenterH - ParentConnectorShield / 2, rect.Bottom, ParentConnectorShield, node.Children[0].State.SiblingsRowV.From - rect.Bottom); state.MergeSpacer(verticalSpacer); // horizontal row carrier protectors var spacing = ParentChildSpacing; for (var firstInRowIndex = 0; firstInRowIndex < node.State.NumberOfSiblings; firstInRowIndex += node.State.NumberOfSiblingColumns) { var firstInRow = node.Children[firstInRowIndex].State; var lastInRow = node.Children[Math.Min(firstInRowIndex + node.State.NumberOfSiblingColumns - 1, node.State.NumberOfSiblings - 1)].State; var horizontalSpacer = node.Children[1 + node.State.NumberOfSiblings + firstInRowIndex / node.State.NumberOfSiblingColumns]; var width = lastInRow.Right >= verticalSpacer.State.Right ? lastInRow.Right - firstInRow.Left : // extend protector at least to the central carrier verticalSpacer.State.Right - firstInRow.Left; horizontalSpacer.State.AdjustSpacer( firstInRow.Left, firstInRow.SiblingsRowV.From - spacing, width, spacing); state.MergeSpacer(horizontalSpacer); spacing = SiblingSpacing; } }
/// <summary> /// Applies layout changes to a given box and its children. /// </summary> public override void ApplyHorizontalLayout([NotNull] LayoutState state, [NotNull] LayoutState.LayoutLevel level) { var node = level.BranchRoot; if (node.State.NumberOfSiblings <= MaxGroups * 2) { base.ApplyHorizontalLayout(state, level); return; } if (node.Level == 0) { node.State.SiblingsRowV = new Dimensions(node.State.Top, node.State.Bottom); } if (node.AssistantsRoot != null) { LayoutAlgorithm.HorizontalLayout(state, node.AssistantsRoot); } var adapter = new SingleFishboneLayoutAdapter(node); while (adapter.NextGroup()) { LayoutAlgorithm.HorizontalLayout(state, adapter.SpecialRoot); } var rect = node.State; // now align child nodes under the parent if (node.Level > 0) { double diff; if (node.State.NumberOfSiblingColumns > 1) { var leftCarrier = node.Children[node.State.NumberOfSiblings + 1].State.CenterH; var rightCarrier = node.Children[node.State.NumberOfSiblings + node.State.NumberOfSiblingColumns].State.CenterH; var desiredCenter = node.State.NumberOfSiblings == 1 || ParentAlignment == BranchParentAlignment.Center ? leftCarrier + (rightCarrier - leftCarrier) / 2 : ParentAlignment == BranchParentAlignment.Left ? leftCarrier + ChildConnectorHookLength : rightCarrier - ChildConnectorHookLength; //var desiredCenter = (leftCarrier + rightCarrier)/2.0; diff = rect.CenterH - desiredCenter; } else { var carrier = node.Children[1 + node.State.NumberOfSiblings].State.CenterH; var desiredCenter = rect.CenterH; diff = desiredCenter - carrier; } LayoutAlgorithm.MoveChildrenOnly(state, level, diff); } if (node.Level > 0) { // vertical connector from parent var ix = node.State.NumberOfSiblings; var verticalSpacer = node.Children[ix]; verticalSpacer.State.AdjustSpacer( rect.CenterH - ParentConnectorShield / 2, rect.Bottom, ParentConnectorShield, node.Children[0].State.SiblingsRowV.From - rect.Bottom); state.MergeSpacer(verticalSpacer); ix++; // vertical carriers already merged in ix += node.State.NumberOfSiblingColumns; if (node.State.NumberOfSiblingColumns > 1) { // have a horizontal carrier var horizontalSpacer = node.Children[ix]; var leftmost = node.Children[node.State.NumberOfSiblings + 1].State.TopLeft; var rightmost = node.Children[ix - 1].State.Right; horizontalSpacer.State.AdjustSpacer( leftmost.X, leftmost.Y - ParentChildSpacing, rightmost - leftmost.X, ParentChildSpacing); state.MergeSpacer(horizontalSpacer); } } }