/// <summary> /// Applies layout changes to a given box and its children. /// </summary> public override void ApplyVerticalLayout([NotNull] LayoutState state, [NotNull] LayoutState.LayoutLevel level) { var node = level.BranchRoot; if (node.State.NumberOfSiblings <= MaxGroups * 2) { base.ApplyVerticalLayout(state, level); return; } if (node.Level == 0) { node.State.SiblingsRowV = new Dimensions(node.State.Top, node.State.Bottom); } if (node.AssistantsRoot != null) { // assistants root has to be initialized with main node's exterior node.AssistantsRoot.State.CopyExteriorFrom(node.State); LayoutAlgorithm.VerticalLayout(state, node.AssistantsRoot); } var adapter = new SingleFishboneLayoutAdapter(node); while (adapter.NextGroup()) { LayoutAlgorithm.VerticalLayout(state, adapter.SpecialRoot); } }
/// <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> /// Pops a box from current layout stack, thus getting higher out from layout hierarchy. /// Automatically merges popped <see cref="Boundary"/> into the current level. /// </summary> public void PopLayoutLevel() { var innerLevel = m_layoutStack.Pop(); BoundaryChanged?.Invoke(this, new BoundaryChangedEventArgs(innerLevel.Boundary, innerLevel, this)); // if this was not the root, merge boundaries into current level if (m_layoutStack.Count > 0) { var higherLevel = m_layoutStack.Peek(); switch (CurrentOperation) { case Operation.VerticalLayout: higherLevel.Boundary.VerticalMergeFrom(innerLevel.Boundary); higherLevel.BranchRoot.State.BranchExterior = higherLevel.Boundary.BoundingRect; break; case Operation.HorizontalLayout: { // do not apply overlap adjustment for assistant branch, they are always above regular children if (higherLevel.BranchRoot.AssistantsRoot != innerLevel.BranchRoot) { var strategy = higherLevel.BranchRoot.State.RequireLayoutStrategy(); var overlap = higherLevel.Boundary.ComputeOverlap( innerLevel.Boundary, strategy.SiblingSpacing, Diagram.LayoutSettings.BranchSpacing); if (overlap > 0) { LayoutAlgorithm.MoveBranch(this, innerLevel, overlap); BoundaryChanged?.Invoke(this, new BoundaryChangedEventArgs(innerLevel.Boundary, innerLevel, this)); } } higherLevel.Boundary.MergeFrom(innerLevel.Boundary); // Do not update branch vertical measurements from the boundary, because boundary adds children one-by-one. // If we take it from boundary, then branch vertical measurement will be incorrect until all children are laid out horizontally, // and this temporarily incorrect state will break those algorithms that need to know combined branch height. higherLevel.BranchRoot.State.BranchExterior = new Rect( higherLevel.Boundary.BoundingRect.Left, higherLevel.BranchRoot.State.BranchExterior.Top, higherLevel.Boundary.BoundingRect.Size.Width, higherLevel.BranchRoot.State.BranchExterior.Size.Height); } break; default: throw new InvalidOperationException( "This operation can only be invoked when performing vertical or horizontal layouts"); } BoundaryChanged?.Invoke(this, new BoundaryChangedEventArgs(higherLevel.Boundary, higherLevel, this)); } // return boundary to the pool m_pooledBoundaries.Push(innerLevel.Boundary); }
/// <summary> /// Applies layout changes to a given box and its children. /// </summary> public override void ApplyVerticalLayout([NotNull] LayoutState state, [NotNull] LayoutState.LayoutLevel level) { var node = level.BranchRoot; if (node.Level == 0) { throw new InvalidOperationException("Should never be invoked on root node"); } var prevRowBottom = node.State.SiblingsRowV.To; var maxOnLeft = MaxOnLeft(node); for (var i = 0; i < maxOnLeft; i++) { var spacing = i == 0 ? ParentChildSpacing : SiblingSpacing; var child = node.Children[i]; var frame = child.State; frame.MoveTo(frame.Left, prevRowBottom + spacing); var rowExterior = new Dimensions(frame.Top, frame.Bottom); var i2 = i + maxOnLeft; if (i2 < node.State.NumberOfSiblings) { var child2 = node.Children[i2]; var frame2 = child2.State; frame2.MoveTo(frame2.Left, prevRowBottom + spacing); if (frame2.Bottom > frame.Bottom) { frame.MoveTo(frame.Left, frame2.CenterV - frame.Size.Height / 2); } else if (frame2.Bottom < frame.Bottom) { frame2.MoveTo(frame2.Left, frame.CenterV - frame2.Size.Height / 2); } frame2.BranchExterior = new Rect(frame2.TopLeft, frame2.Size); rowExterior += new Dimensions(frame2.Top, frame2.Bottom); frame2.SiblingsRowV = rowExterior; LayoutAlgorithm.VerticalLayout(state, child2); prevRowBottom = frame2.BranchExterior.Bottom; } frame.BranchExterior = new Rect(frame.TopLeft, frame.Size); frame.SiblingsRowV = rowExterior; LayoutAlgorithm.VerticalLayout(state, child); prevRowBottom = Math.Max(prevRowBottom, frame.BranchExterior.Bottom); } }
/// <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 ApplyVerticalLayout([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); } if (node.AssistantsRoot != null) { // assistants root has to be initialized with main node's exterior node.AssistantsRoot.State.CopyExteriorFrom(node.State); LayoutAlgorithm.VerticalLayout(state, node.AssistantsRoot); } if (node.State.NumberOfSiblings == 0) { return; } var siblingsRowExterior = Dimensions.MinMax(); var top = node.AssistantsRoot == null ? node.State.SiblingsRowV.To + ParentChildSpacing : node.State.BranchExterior.Bottom + ParentChildSpacing; for (var i = 0; i < node.State.NumberOfSiblings; i++) { var child = node.Children[i]; var rect = child.State; child.State.MoveTo(0, top); child.State.BranchExterior = new Rect(child.State.TopLeft, child.State.Size); siblingsRowExterior += new Dimensions(top, top + rect.Size.Height); } siblingsRowExterior = new Dimensions(siblingsRowExterior.From, siblingsRowExterior.To); for (var i = 0; i < node.State.NumberOfSiblings; i++) { var child = node.Children[i]; child.State.SiblingsRowV = siblingsRowExterior; // re-enter layout algorithm for child branch LayoutAlgorithm.VerticalLayout(state, child); } }
public override void ApplyHorizontalLayout(LayoutState state, [NotNull] LayoutState.LayoutLevel level) { if (level.BranchRoot != SpecialRoot) { throw new InvalidOperationException("Wrong root node received"); } var left = true; var countOnThisSide = 0; for (var i = 0; i < Iterator.Count; i++) { var child = SpecialRoot.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 == Iterator.MaxOnLeft) { if (left) { // horizontally align children in left pillar LayoutAlgorithm.AlignHorizontalCenters(state, level, EnumerateSiblings(0, Iterator.MaxOnLeft)); left = false; countOnThisSide = 0; var rightmost = double.MinValue; for (var k = 0; k < i; k++) { rightmost = Math.Max(rightmost, SpecialRoot.Children[k].State.BranchExterior.Right); } rightmost = Math.Max(rightmost, child.State.Right); // integrate protector for group's vertical carrier var spacer = SpecialRoot.Children[SpecialRoot.State.NumberOfSiblings]; spacer.State.AdjustSpacer( rightmost, SpecialRoot.Children[0].State.SiblingsRowV.From, SiblingSpacing, child.State.SiblingsRowV.To - SpecialRoot.Children[0].State.SiblingsRowV.From); level.Boundary.MergeFrom(spacer); } } } // horizontally align children in right pillar LayoutAlgorithm.AlignHorizontalCenters(state, level, EnumerateSiblings(Iterator.MaxOnLeft, Iterator.Count)); }
public override void ApplyVerticalLayout(LayoutState state, [NotNull] LayoutState.LayoutLevel level) { var prevRowBottom = RealRoot.AssistantsRoot?.State.BranchExterior.Bottom ?? SpecialRoot.State.SiblingsRowV.To; for (var i = 0; i < Iterator.MaxOnLeft; i++) { var spacing = i == 0 ? ParentChildSpacing : SiblingSpacing; var child = SpecialRoot.Children[i]; var frame = child.State; frame.MoveTo(frame.Left, prevRowBottom + spacing); var rowExterior = new Dimensions(frame.Top, frame.Bottom); var i2 = i + Iterator.MaxOnLeft; if (i2 < Iterator.Count) { var child2 = SpecialRoot.Children[i2]; var frame2 = child2.State; frame2.MoveTo(frame2.Left, prevRowBottom + spacing); if (frame2.Bottom > frame.Bottom) { frame.MoveTo(frame.Left, frame2.CenterV - frame.Size.Height / 2); } else if (frame2.Bottom < frame.Bottom) { frame2.MoveTo(frame2.Left, frame.CenterV - frame2.Size.Height / 2); } frame2.BranchExterior = new Rect(frame2.TopLeft, frame2.Size); rowExterior += new Dimensions(frame2.Top, frame2.Bottom); frame2.SiblingsRowV = rowExterior; LayoutAlgorithm.VerticalLayout(state, child2); prevRowBottom = frame2.BranchExterior.Bottom; } frame.BranchExterior = new Rect(frame.TopLeft, frame.Size); frame.SiblingsRowV = rowExterior; LayoutAlgorithm.VerticalLayout(state, child); prevRowBottom = Math.Max(prevRowBottom, frame.BranchExterior.Bottom); } }
/// <summary> /// Applies layout changes to a given box and its children. /// </summary> public override void ApplyVerticalLayout([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); } if (node.AssistantsRoot != null) { // assistants root has to be initialized with main node's exterior node.AssistantsRoot.State.CopyExteriorFrom(node.State); LayoutAlgorithm.VerticalLayout(state, node.AssistantsRoot); } var prevRowExterior = new Dimensions( node.State.SiblingsRowV.From, node.AssistantsRoot == null ? node.State.SiblingsRowV.To : node.State.BranchExterior.Bottom); for (var row = 0; row < node.State.NumberOfSiblings; row++) { // first, compute var child = node.Children[row]; var rect = child.State; var top = prevRowExterior.To + (row == 0 ? ParentChildSpacing : SiblingSpacing); child.State.MoveTo(rect.Left, top); child.State.BranchExterior = new Rect(child.State.TopLeft, child.State.Size); var rowExterior = new Dimensions(top, top + rect.Size.Height); child = node.Children[row]; child.State.SiblingsRowV = rowExterior; // re-enter layout algorithm for child branch LayoutAlgorithm.VerticalLayout(state, child); var childBranchBottom = child.State.BranchExterior.Bottom; prevRowExterior = new Dimensions(rowExterior.From, Math.Max(childBranchBottom, rowExterior.To)); } }
/// <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 ApplyVerticalLayout([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.ApplyVerticalLayout(state, level); return; } if (node.Level == 0) { node.State.SiblingsRowV = new Dimensions(node.State.Top, node.State.Bottom); } if (node.AssistantsRoot != null) { // assistants root has to be initialized with main node's exterior node.AssistantsRoot.State.CopyExteriorFrom(node.State); LayoutAlgorithm.VerticalLayout(state, node.AssistantsRoot); } var prevRowExterior = new Dimensions( node.State.SiblingsRowV.From, node.AssistantsRoot == null ? node.State.SiblingsRowV.To : node.State.BranchExterior.Bottom); for (var row = 0; row < node.State.NumberOfSiblingRows; row++) { var siblingsRowExterior = Dimensions.MinMax(); var spacing = row == 0 ? ParentChildSpacing : SiblingSpacing; // first, compute var from = row * node.State.NumberOfSiblingColumns; var to = Math.Min(from + node.State.NumberOfSiblingColumns, node.State.NumberOfSiblings); for (var i = from; i < to; i++) { var child = node.Children[i]; if (child.Element.IsSpecial) { // skip vertical spacers for now continue; } var rect = child.State; var top = prevRowExterior.To + spacing; child.State.MoveTo(rect.Left, top); child.State.BranchExterior = new Rect(child.State.TopLeft, child.State.Size); siblingsRowExterior += new Dimensions(top, top + rect.Size.Height); } siblingsRowExterior = new Dimensions(siblingsRowExterior.From, siblingsRowExterior.To); var siblingsBottom = double.MinValue; for (var i = from; i < to; i++) { var child = node.Children[i]; child.State.SiblingsRowV = siblingsRowExterior; // re-enter layout algorithm for child branch LayoutAlgorithm.VerticalLayout(state, child); siblingsBottom = Math.Max(siblingsBottom, child.State.BranchExterior.Bottom); } prevRowExterior = new Dimensions(siblingsRowExterior.From, Math.Max(siblingsBottom, siblingsRowExterior.To)); // now assign size to the vertical spacer, if any var spacerIndex = from + node.State.NumberOfSiblingColumns / 2; if (spacerIndex < node.State.NumberOfSiblings) { // in the last row, spacer should only extend to the siblings row bottom, // because main vertical carrier does not go below last row // and thus cannot conflict with branches of children of the last row var spacerBottom = row == node.State.NumberOfSiblingRows - 1 ? node.Children[spacerIndex - 1].State.SiblingsRowV.To : prevRowExterior.To; var spacer = node.Children[spacerIndex].State; spacer.AdjustSpacer( 0, prevRowExterior.From, ParentConnectorShield, spacerBottom - prevRowExterior.From); } } }
/// <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 ApplyVerticalLayout([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); } if (node.State.NumberOfSiblings == 0) { return; } var siblingsRowExterior = Dimensions.MinMax(); if (Orientation == StackOrientation.SingleRowHorizontal) { var top = node.AssistantsRoot == null ? node.State.SiblingsRowV.To + ParentChildSpacing : node.State.BranchExterior.Bottom + ParentChildSpacing; for (var i = 0; i < node.State.NumberOfSiblings; i++) { var child = node.Children[i]; var rect = child.State; child.State.MoveTo(0, top); child.State.BranchExterior = new Rect(child.State.TopLeft, child.State.Size); siblingsRowExterior += new Dimensions(top, top + rect.Size.Height); } siblingsRowExterior = new Dimensions(siblingsRowExterior.From, siblingsRowExterior.To); for (var i = 0; i < node.State.NumberOfSiblings; i++) { var child = node.Children[i]; child.State.SiblingsRowV = siblingsRowExterior; // re-enter layout algorithm for child branch LayoutAlgorithm.VerticalLayout(state, child); } } else if (Orientation == StackOrientation.SingleColumnVertical) { var prevRowExterior = new Dimensions( node.State.SiblingsRowV.From, node.State.SiblingsRowV.To); for (var row = 0; row < node.State.NumberOfSiblings; row++) { // first, compute var child = node.Children[row]; var rect = child.State; var top = prevRowExterior.To + (row == 0 ? ParentChildSpacing : SiblingSpacing); child.State.MoveTo(rect.Left, top); child.State.BranchExterior = new Rect(child.State.TopLeft, child.State.Size); var rowExterior = new Dimensions(top, top + rect.Size.Height); child = node.Children[row]; child.State.SiblingsRowV = rowExterior; // re-enter layout algorithm for child branch LayoutAlgorithm.VerticalLayout(state, child); var childBranchBottom = child.State.BranchExterior.Bottom; prevRowExterior = new Dimensions(rowExterior.From, Math.Max(childBranchBottom, rowExterior.To)); } } }
/// <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); } } }