/// <summary> /// Push a new box onto the layout stack, thus getting deeper into layout hierarchy. /// Automatically allocates a Bondary object from pool. /// </summary> public LayoutLevel PushLayoutLevel([NotNull] BoxTree.Node node) { if (m_pooledBoundaries.Count == 0) { m_pooledBoundaries.Push(new Boundary()); } var boundary = m_pooledBoundaries.Pop(); switch (CurrentOperation) { case Operation.VerticalLayout: boundary.Prepare(node); break; case Operation.HorizontalLayout: boundary.PrepareForHorizontalLayout(node); break; default: throw new InvalidOperationException("This operation can only be invoked when performing vertical or horizontal layouts"); } var result = new LayoutLevel(node, boundary); m_layoutStack.Push(result); BoundaryChanged?.Invoke(this, new BoundaryChangedEventArgs(boundary, result, this)); return(result); }
/// <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> /// Merges a provided spacer box into the current branch boundary. /// </summary> public void MergeSpacer([NotNull] BoxTree.Node spacer) { if (CurrentOperation != Operation.HorizontalLayout) { throw new InvalidOperationException("Spacers can only be merged during horizontal layout"); } if (m_layoutStack.Count == 0) { throw new InvalidOperationException("Cannot merge spacers at top nesting level"); } var level = m_layoutStack.Peek(); level.Boundary.MergeFrom(spacer); BoundaryChanged?.Invoke(this, new BoundaryChangedEventArgs(level.Boundary, level, this)); }