private IEnumerable <BoxTree.Node> EnumerateColumn(BoxTree.Node branchRoot)
 {
     for (var i = 0; i < branchRoot.State.NumberOfSiblings; i++)
     {
         yield return(branchRoot.Children[i]);
     }
 }
示例#2
0
 /// <summary>
 /// Ctr.
 /// </summary>
 public Step([NotNull] BoxTree.Node node, double x, double top, double bottom)
 {
     Node   = node;
     X      = x;
     Top    = top;
     Bottom = bottom;
 }
示例#3
0
        /// <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>
        /// Allocates and routes connectors.
        /// </summary>
        public override void RouteConnectors([NotNull] LayoutState state, [NotNull] BoxTree.Node node)
        {
            if (node.ChildCount == 0)
            {
                return;
            }

            // one parent connector (also serves as mid-sibling carrier) and horizontal carriers
            var count = 1 + node.State.NumberOfSiblings;

            var segments = new Edge[count];

            var rootRect = node.State;
            var center   = rootRect.CenterH;

            var verticalCarrierHeight = node.Children[node.State.NumberOfSiblings - 1].State.CenterV - node.State.Bottom;

            // big vertical connector, from parent to last row
            segments[0] = new Edge(new Point(center, rootRect.Bottom), new Point(center, rootRect.Bottom + verticalCarrierHeight));

            for (var ix = 0; ix < node.State.NumberOfSiblings; ix++)
            {
                var rect        = node.Children[ix].State;
                var destination = ParentAlignment == BranchParentAlignment.Left ? rect.Left : rect.Right;
                segments[1 + ix] = new Edge(
                    new Point(center, rect.CenterV),
                    new Point(destination, rect.CenterV));
            }

            node.State.Connector = new Connector(segments);
        }
 private IEnumerable <BoxTree.Node> EnumerateSiblings(BoxTree.Node node, int from, int to)
 {
     for (var i = from; i < to; i++)
     {
         yield return(node.Children[i]);
     }
 }
示例#6
0
        /// <summary>
        /// Re-initializes left and right edges based on actual coordinates of boxes.
        /// </summary>
        public void ReloadFromBranch(BoxTree.Node branchRoot)
        {
            var leftmost  = double.MaxValue;
            var rightmost = double.MinValue;

            for (var i = 0; i < Left.Count; i++)
            {
                var left    = Left[i];
                var newLeft = left.Node.State.Left;
                Left[i]  = left.ChangeX(newLeft);
                leftmost = Math.Min(leftmost, newLeft);
            }

            for (var i = 0; i < Right.Count; i++)
            {
                var right    = Right[i];
                var newRight = right.Node.State.Right;
                Right[i]  = right.ChangeX(newRight);
                rightmost = Math.Max(rightmost, newRight);
            }

            leftmost  = Math.Min(branchRoot.State.Left, leftmost);
            rightmost = Math.Max(branchRoot.State.Right, rightmost);

            BoundingRect = new Rect(new Point(leftmost, BoundingRect.Top),
                                    new Size(rightmost - leftmost, BoundingRect.Size.Height));
        }
        /// <summary>
        /// A chance for layout strategy to append special auto-generated boxes into the visual tree.
        /// </summary>
        public override void PreProcessThisNode([NotNull] LayoutState state, [NotNull] BoxTree.Node node)
        {
            if (MaxSiblingsPerRow <= 0 || MaxSiblingsPerRow % 2 != 0)
            {
                throw new InvalidOperationException(nameof(MaxSiblingsPerRow) + " must be a positive even value");
            }

            if (node.ChildCount <= MaxSiblingsPerRow)
            {
                // fall back to linear layout, only have one row of boxes
                base.PreProcessThisNode(state, node);
                return;
            }

            node.State.NumberOfSiblings = node.ChildCount;

            // only add spacers for non-collapsed boxes
            if (node.State.NumberOfSiblings > 0)
            {
                var lastRowBoxCount = node.ChildCount % MaxSiblingsPerRow;

                // add one (for vertical spacer) into the count of layout columns
                node.State.NumberOfSiblingColumns = 1 + MaxSiblingsPerRow;

                node.State.NumberOfSiblingRows = node.ChildCount / MaxSiblingsPerRow;
                if (lastRowBoxCount != 0)
                {
                    node.State.NumberOfSiblingRows++;
                }

                // include vertical spacers into the count of layout siblings
                node.State.NumberOfSiblings = node.ChildCount + node.State.NumberOfSiblingRows;
                if (lastRowBoxCount > 0 && lastRowBoxCount <= MaxSiblingsPerRow / 2)
                {
                    // don't need the last spacer, last row is half-full or even less
                    node.State.NumberOfSiblings--;
                }

                // sibling middle-spacers have to be inserted between siblings
                var ix = MaxSiblingsPerRow / 2;
                while (ix < node.State.NumberOfSiblings)
                {
                    var siblingSpacer = Box.Special(Box.None, node.Element.Id, false);
                    node.InsertRegularChild(ix, siblingSpacer);
                    ix += node.State.NumberOfSiblingColumns;
                }

                // add parent vertical spacer to the end
                var verticalSpacer = Box.Special(Box.None, node.Element.Id, false);
                node.AddRegularChild(verticalSpacer);

                // add horizontal spacers to the end
                for (var i = 0; i < node.State.NumberOfSiblingRows; i++)
                {
                    var horizontalSpacer = Box.Special(Box.None, node.Element.Id, false);
                    node.AddRegularChild(horizontalSpacer);
                }
            }
        }
示例#8
0
        /// <summary>
        /// Resets the edges, use when re-using this object from pool.
        /// </summary>
        public void Prepare([NotNull] BoxTree.Node node)
        {
            Left.Clear();
            Right.Clear();

            // adjust the top edge to fit the logical grid
            BoundingRect = new Rect(node.State.TopLeft, node.State.Size);
        }
示例#9
0
        /// <summary>
        /// Allocates and routes connectors.
        /// </summary>
        public override void RouteConnectors([NotNull] LayoutState state, [NotNull] BoxTree.Node node)
        {
            var normalChildCount = node.State.NumberOfSiblings;

            var count = normalChildCount == 0
                ? 0                         // no visible children = no edges
                : normalChildCount == 1
                    ? 1                     // one child = one direct edge between parent and child
                    : 1                     // one downward edge for parent
                        + 1                 // one for horizontal carrier
                        + normalChildCount; // one upward edge for each child

            if (count == 0)
            {
                node.State.Connector = null;
                return;
            }

            var segments = new Edge[count];

            var rootRect = node.State;
            var center   = rootRect.CenterH;

            if (node.Children == null)
            {
                throw new Exception("State is present, but children not set");
            }

            if (count == 1)
            {
                segments[0] = new Edge(new Point(center, rootRect.Bottom),
                                       new Point(center, node.Children[0].State.Top));
            }
            else
            {
                var space = node.Children[0].State.SiblingsRowV.From - rootRect.Bottom;

                segments[0] = new Edge(new Point(center, rootRect.Bottom),
                                       new Point(center, rootRect.Bottom + space - ChildConnectorHookLength));

                for (var i = 0; i < normalChildCount; i++)
                {
                    var childRect   = node.Children[i].State;
                    var childCenter = childRect.CenterH;
                    segments[1 + i] = new Edge(new Point(childCenter, childRect.Top),
                                               new Point(childCenter, childRect.Top - ChildConnectorHookLength));
                }

                segments[count - 1] = new Edge(
                    new Point(segments[1].To.X, segments[1].To.Y),
                    new Point(segments[count - 2].To.X, segments[1].To.Y));
            }

            node.State.Connector = new Connector(segments);
        }
示例#10
0
 /// <summary>
 /// Moves a given branch horizontally, except its root box.
 /// Also updates branch exterior rects.
 /// Unlike <see cref="MoveChildrenOnly"/> and <see cref="MoveBranch"/>, does NOT update the boundary.
 /// </summary>
 /// <remarks>DOES NOT update branch boundary! Must call <see cref="Boundary.ReloadFromBranch"/> after batch of updates is complete</remarks>
 private static void MoveOneChild([NotNull] LayoutState state, [NotNull] BoxTree.Node root, double offset)
 {
     root.IterateChildFirst(node =>
     {
         if (!node.State.IsHidden)
         {
             node.State.TopLeft        = node.State.TopLeft.MoveH(offset);
             node.State.BranchExterior = node.State.BranchExterior.MoveH(offset);
         }
         return(true);
     });
 }
        private IEnumerable <BoxTree.Node> EnumerateColumn(BoxTree.Node branchRoot, int col)
        {
            for (var row = 0; row < branchRoot.State.NumberOfSiblingRows; row++)
            {
                var ix = row * branchRoot.State.NumberOfSiblingColumns + col;
                if (ix >= branchRoot.State.NumberOfSiblings)
                {
                    yield break;
                }

                yield return(branchRoot.Children[ix]);
            }
        }
        /// <summary>
        /// Allocates and routes connectors.
        /// </summary>
        public override void RouteConnectors([NotNull] LayoutState state, [NotNull] BoxTree.Node node)
        {
            var count = node.State.NumberOfSiblings;

            if (count == 0)
            {
                return;
            }

            if (NeedCarrierProtector(node))
            {
                count++;
            }

            var segments = new Edge[count];

            var ix = 0;

            // one hook for each child
            var maxOnLeft = MaxOnLeft(node);
            var carrier   = node.Children[node.State.NumberOfSiblings].State;
            var from      = carrier.CenterH;

            var isLeft          = true;
            var countOnThisSide = 0;
            var bottomMost      = double.MinValue;

            for (var i = 0; i < node.State.NumberOfSiblings; i++)
            {
                var to = isLeft ? node.Children[i].State.Right : node.Children[i].State.Left;
                var y  = node.Children[i].State.CenterV;
                bottomMost     = Math.Max(bottomMost, y);
                segments[ix++] = new Edge(new Point(from, y), new Point(to, y));

                if (++countOnThisSide == maxOnLeft)
                {
                    countOnThisSide = 0;
                    isLeft          = !isLeft;
                }
            }

            if (NeedCarrierProtector(node))
            {
                // one for each vertical carrier
                segments[node.State.NumberOfSiblings] = new Edge(
                    new Point(carrier.CenterH, carrier.Top),
                    new Point(carrier.CenterH, bottomMost));
            }

            node.State.Connector = new Connector(segments);
        }
示例#13
0
        /// <summary>
        /// Resets the edges, use when re-using this object from pool.
        /// </summary>
        public void PrepareForHorizontalLayout([NotNull] BoxTree.Node node)
        {
            Prepare(node);

            if (node.Element.DisableCollisionDetection)
            {
                return;
            }

            var rect = node.State;

            Left.Add(new Step(node, rect.Left, rect.Top, rect.Bottom));
            Right.Add(new Step(node, rect.Right, rect.Top, rect.Bottom));
        }
示例#14
0
        /// <summary>
        /// Merges a box into this one, potentially pushing its edges out.
        /// </summary>
        public void MergeFrom([NotNull] BoxTree.Node node)
        {
            if (node.Element.DisableCollisionDetection)
            {
                return;
            }

            if (node.State.Size.Height.IsZero())
            {
                return;
            }

            m_spacerMerger.PrepareForHorizontalLayout(node);
            MergeFrom(m_spacerMerger);
        }
        /// <summary>
        /// A chance for layout strategy to append special auto-generated boxes into the visual tree.
        /// This strategy does not use connectors and spacers.
        /// </summary>
        public override void PreProcessThisNode([NotNull] LayoutState state, [NotNull] BoxTree.Node node)
        {
            node.State.NumberOfSiblings = node.Element.IsCollapsed ? 0 : node.ChildCount;

            if (node.State.NumberOfSiblings > 0)
            {
                // this strategy requires certain adjustments to be made to the box sizes
                // they will only affect corresponding Nodes, not the size on the box itself
                if (Orientation != StackOrientation.SingleRowHorizontal &&
                    Orientation != StackOrientation.SingleColumnVertical)
                {
                    throw new InvalidOperationException("Unsupported value for orientation: " + Orientation);
                }
            }
        }
示例#16
0
        /// <summary>
        /// A chance for layout strategy to append special auto-generated boxes into the visual tree.
        /// </summary>
        public override void PreProcessThisNode([NotNull] LayoutState state, [NotNull] BoxTree.Node node)
        {
            if (MaxGroups <= 0)
            {
                throw new InvalidOperationException(nameof(MaxGroups) + " must be a positive value");
            }

            if (node.ChildCount <= MaxGroups * 2)
            {
                base.PreProcessThisNode(state, node);
                return;
            }

            node.State.NumberOfSiblings = node.ChildCount;

            // only add spacers for non-collapsed boxes
            if (node.State.NumberOfSiblings > 0)
            {
                // using column == group here,
                // and each group consists of two vertical stretches of boxes with a vertical carrier in between
                node.State.NumberOfSiblingColumns = MaxGroups;
                node.State.NumberOfSiblingRows    = node.State.NumberOfSiblings / (MaxGroups * 2);
                if (node.State.NumberOfSiblings % (MaxGroups * 2) != 0)
                {
                    node.State.NumberOfSiblingRows++;
                }

                // a connector from parent to horizontal carrier
                var parentSpacer = Box.Special(Box.None, node.Element.Id, false);
                node.AddRegularChild(parentSpacer);

                // spacers for vertical carriers
                for (var i = 0; i < node.State.NumberOfSiblingColumns; i++)
                {
                    var verticalSpacer = Box.Special(Box.None, node.Element.Id, false);
                    node.AddRegularChild(verticalSpacer);
                }

                // if needed, horizontal carrier
                if (node.State.NumberOfSiblingColumns > 1)
                {
                    var horizontalSpacer = Box.Special(Box.None, node.Element.Id, false);
                    node.AddRegularChild(horizontalSpacer);
                }
            }
        }
示例#17
0
        /// <summary>
        /// A chance for layout strategy to append special auto-generated boxes into the visual tree.
        /// </summary>
        public override void PreProcessThisNode([NotNull] LayoutState state, [NotNull] BoxTree.Node node)
        {
            if (node.ChildCount > 0)
            {
                node.State.NumberOfSiblings = node.Element.IsCollapsed ? 0 : node.ChildCount;

                // only add spacers for non-collapsed boxes
                if (!node.Element.IsCollapsed)
                {
                    var verticalSpacer = Box.Special(Box.None, node.Element.Id, false);
                    node.AddRegularChild(verticalSpacer);

                    var horizontalSpacer = Box.Special(Box.None, node.Element.Id, false);
                    node.AddRegularChild(horizontalSpacer);
                }
            }
        }
示例#18
0
        /// <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));
        }
        /// <summary>
        /// A chance for layout strategy to append special auto-generated boxes into the visual tree.
        /// </summary>
        public override void PreProcessThisNode([NotNull] LayoutState state, [NotNull] BoxTree.Node node)
        {
            node.State.NumberOfSiblings = node.ChildCount;

            // only add spacers for non-collapsed boxes
            if (node.State.NumberOfSiblings > 0)
            {
                // using column == group here,
                // and each group consists of two vertical stretches of boxes with a vertical carrier in between
                node.State.NumberOfSiblingColumns = 1;
                node.State.NumberOfSiblingRows    = node.State.NumberOfSiblings / 2;
                if (node.State.NumberOfSiblings % 2 != 0)
                {
                    node.State.NumberOfSiblingRows++;
                }

                // a vertical carrier from parent
                var spacer = Box.Special(Box.None, node.Element.Id, false);
                node.AddRegularChild(spacer);
            }
        }
示例#20
0
            public SingleFishboneLayoutAdapter([NotNull] BoxTree.Node realRoot)
            {
                Iterator = new GroupIterator(realRoot.State.NumberOfSiblings, realRoot.State.NumberOfSiblingColumns);

                RealRoot    = realRoot;
                SpecialRoot = new TreeNodeView(Box.Special(Box.None, realRoot.Element.Id, true))
                {
                    Level      = RealRoot.Level,
                    ParentNode = RealRoot
                };

                SpecialRoot.State.EffectiveLayoutStrategy = this;

                var parentStrategy = (MultiLineFishboneLayoutStrategy)realRoot.State.RequireLayoutStrategy();

                SiblingSpacing           = parentStrategy.SiblingSpacing;
                ParentConnectorShield    = parentStrategy.ParentConnectorShield;
                ParentChildSpacing       = parentStrategy.ParentChildSpacing;
                ParentAlignment          = parentStrategy.ParentAlignment;
                ChildConnectorHookLength = parentStrategy.ChildConnectorHookLength;
            }
        /// <summary>
        /// A chance for layout strategy to append special auto-generated boxes into the visual tree.
        /// </summary>
        public override void PreProcessThisNode([NotNull] LayoutState state, [NotNull] BoxTree.Node node)
        {
            if (ParentAlignment != BranchParentAlignment.Left &&
                ParentAlignment != BranchParentAlignment.Right)
            {
                throw new InvalidOperationException("Unsupported value for " + nameof(ParentAlignment));
            }

            node.State.NumberOfSiblings = node.Element.IsCollapsed ? 0 : node.ChildCount;

            // only add spacers for non-collapsed boxes
            if (node.State.NumberOfSiblings > 0 && node.Level > 0)
            {
                // add one (for vertical spacer) into the count of layout columns
                node.State.NumberOfSiblingColumns = 1;
                node.State.NumberOfSiblingRows    = node.ChildCount;

                // add parent's vertical carrier to the end
                var verticalSpacer = Box.Special(Box.None, node.Element.Id, false);
                node.AddRegularChild(verticalSpacer);
            }
        }
示例#22
0
        /// <summary>
        /// Re-entrant layout algorithm.
        /// </summary>
        public static void VerticalLayout([NotNull] LayoutState state, [NotNull] BoxTree.Node branchRoot)
        {
            if (branchRoot.State.IsHidden)
            {
                throw new InvalidOperationException($"Branch root {branchRoot.Element.Id} does not affect layout");
            }

            var level = state.PushLayoutLevel(branchRoot);

            try
            {
                if (branchRoot.Level == 0 ||
                    (branchRoot.State.NumberOfSiblings > 0 || branchRoot.AssistantsRoot != null) &&
                    !branchRoot.Element.IsCollapsed)
                {
                    branchRoot.State.RequireLayoutStrategy().ApplyVerticalLayout(state, level);
                }
            }
            finally
            {
                state.PopLayoutLevel();
            }
        }
示例#23
0
 public override void RouteConnectors(LayoutState state, BoxTree.Node node)
 {
     throw new NotSupportedException();
 }
示例#24
0
 /// <summary>
 /// Returns a new <see cref="Step"/> whose <see cref="Node"/> property was set to <paramref name="newNode"/> and <see cref="X"/> to <paramref name="newX"/>.
 /// </summary>
 public Step ChangeOwner([NotNull] BoxTree.Node newNode, double newX)
 {
     return(new Step(newNode, newX, Top, Bottom));
 }
 /// <summary>
 /// Allocates and routes connectors.
 /// </summary>
 public override void RouteConnectors([NotNull] LayoutState state, [NotNull] BoxTree.Node node)
 {
     // this strategy does not use connectors
 }
 private bool NeedCarrierProtector(BoxTree.Node node) => node.ParentNode.ChildCount == 0;
 private int MaxOnLeft(BoxTree.Node node) => node.State.NumberOfSiblings / 2 + node.State.NumberOfSiblings % 2;
        /// <summary>
        /// Allocates and routes connectors.
        /// </summary>
        public override void RouteConnectors([NotNull] LayoutState state, [NotNull] BoxTree.Node node)
        {
            if (node.State.NumberOfSiblings <= MaxSiblingsPerRow)
            {
                // fall back to linear layout, only have one row of boxes
                base.RouteConnectors(state, node);
                return;
            }

            // one parent connector (also serves as mid-sibling carrier) and horizontal carriers
            var count = 1 + node.State.NumberOfSiblingRows;

            foreach (var child in node.Children)
            {
                // normal boxes get one upward hook
                if (!child.Element.IsSpecial)
                {
                    count++;
                }
            }

            var segments = new Edge[count];

            var rootRect = node.State;
            var center   = rootRect.CenterH;

            var verticalCarrierHeight = node.Children[node.State.NumberOfSiblings - 1].State.SiblingsRowV.From
                                        - ChildConnectorHookLength - rootRect.Bottom;

            // central mid-sibling vertical connector, from parent to last row
            segments[0] = new Edge(new Point(center, rootRect.Bottom), new Point(center, rootRect.Bottom + verticalCarrierHeight));

            // short hook for each child
            var ix = 1;

            for (var i = 0; i < node.State.NumberOfSiblings; i++)
            {
                var child = node.Children[i];
                if (!child.Element.IsSpecial)
                {
                    var childRect   = child.State;
                    var childCenter = childRect.CenterH;
                    segments[ix++] = new Edge(
                        new Point(childCenter, childRect.Top),
                        new Point(childCenter, childRect.Top - ChildConnectorHookLength));
                }
            }

            // horizontal carriers go from leftmost child hook to righmost child hook
            // for the last row which is just half or less full, it will only go to the central vertical carrier
            var lastChildHookIndex = count - node.State.NumberOfSiblingRows - 1;

            for (var firstInRowIndex = 1; firstInRowIndex < count - node.State.NumberOfSiblingRows; firstInRowIndex += MaxSiblingsPerRow)
            {
                var firstInRow = segments[firstInRowIndex];

                var lastInRow = segments[Math.Min(firstInRowIndex + MaxSiblingsPerRow - 1, lastChildHookIndex)];

                if (lastInRow.From.X < segments[0].From.X)
                {
                    segments[ix++] = new Edge(
                        new Point(firstInRow.To.X, firstInRow.To.Y),
                        new Point(segments[0].To.X, firstInRow.To.Y));
                }
                else
                {
                    segments[ix++] = new Edge(
                        new Point(firstInRow.To.X, firstInRow.To.Y),
                        new Point(lastInRow.To.X, firstInRow.To.Y));
                }
            }

            node.State.Connector = new Connector(segments);
        }
 /// <summary>
 /// A chance for layout strategy to insert special auto-generated boxes into the visual tree.
 /// </summary>
 public abstract void PreProcessThisNode([NotNull] LayoutState state, [NotNull] BoxTree.Node node);
 /// <summary>
 /// Allocates and routes connectors.
 /// </summary>
 public abstract void RouteConnectors([NotNull] LayoutState state, [NotNull] BoxTree.Node node);