Example #1
0
        public void Flow(CssBoxTreeNode Node)
        {
            if (Node is null)
            {
                throw new System.ArgumentNullException(nameof(Node));
            }
            Contract.EndContractBlock();

            CssBoxTreeNode Current = Node.firstChild;

            while (Current is object)
            {
                if (Current.previousSibling is null)
                {
                    Current.Position = Point2f.Zero;
                }
                else
                {
                    var prev      = Current.previousSibling;
                    var prev_pos  = prev.Position;
                    var prev_size = prev.Size;
                    Current.Position = new Point2f(prev_pos.X + prev_size.Width, prev_pos.Y + prev_size.Height);
                }

                Current = Current.nextSibling;
            }
        }
Example #2
0
        /*
         * Docs: https://www.w3.org/TR/css-display-3/#intro
         * Docs: https://www.w3.org/TR/CSS22/visuren.html#box-gen
         */

        /// <summary>
        /// Generates an appropriate CSS principal box object for the given element, populated with any appropriate child boxes
        /// </summary>
        /// <param name="E"></param>
        /// <returns></returns>
        public static void Generate_Tree(Node StartNode, Node EndNode = null)
        {
            if (StartNode is null)
            {
                throw new ArgumentNullException(nameof(StartNode));
            }

            if (!StartNode.GetFlag(ENodeFlags.NeedsBoxUpdate | ENodeFlags.ChildNeedsBoxUpdate))
            {
                throw new ArgumentException($"Neither {nameof(StartNode)} nor its children are flagged for box updates.");
            }

            Contract.EndContractBlock();

            /* This is the method we use to populate the tree from a given point onwards:
             * 1) While queue is not empty, Pop next node.
             * 2) If next node == End then break;
             * 3) Delete all box-nodes in chain to the nodes real principal-box parent.
             * 4) Generate a new principal-box or text-run.
             * 5) Insert the new box-node into tree.
             * 6) Queue all children of element.
             */

            var Queue = new Queue <Node>();

            Queue.Enqueue(StartNode);
            while (Queue.Count > 0)
            {
                Node node = Queue.Dequeue();
                if (ReferenceEquals(node, EndNode))
                {
                    break;
                }

                if (node.GetFlag(ENodeFlags.NeedsBoxUpdate))
                {
                    CssBoxTreeNode Box             = node.Box;
                    Element        nearestAncestor = Get_Closest_Box_Generating_Ancestor(node);

                    // 3) Delete the nodes current box
                    // 3) Unlink all box-nodes in the chain leading to the nodes real principal-box parent
                    // The current box might be wrapped in an anonymous box. in which case the index we WANT is actually THAT boxs'
                    int index = -1;
                    if (Box is object)
                    {
                        var ChainRoot = Box.Unlink(nearestAncestor.Box);
                        index = ChainRoot.index;
                    }

                    CssBoxTreeNode nextBox = null;
                    // 4) Generate a new principal-box or text-run
                    switch (node.nodeType)
                    {
                    case DOM.Enums.ENodeType.TEXT_NODE:
                    {
                        // Texts runs encompass all of the contiguous sibling text-nodes, skip those contiguous nodes in the queue
                        var TextNodes = new List <Text>(node.parentNode.childNodes.Count);
                        while (Queue.Peek().nodeType == DOM.Enums.ENodeType.TEXT_NODE && ReferenceEquals(Queue.Peek().parentNode, node.parentNode))
                        {
                            TextNodes.Add((Text)Queue.Dequeue());
                        }

                        nextBox = new CssTextRun(TextNodes.ToArray());
                    }
                    break;

                    case DOM.Enums.ENodeType.ELEMENT_NODE:
                    {
                        nextBox = Generate_Box((Element)node);
                    }
                    break;
                    }

                    // Transfer child nodes from old box to the new box (they will remove themselves if needed)
                    ITreeNode current = Box.firstChild;
                    while (current is object)
                    {
                        current.parentNode = null;
                        nextBox.childNodes.Add(current);
                        current = current.nextSibling;
                    }

                    // 5) Insert the new box-node into tree
                    if (index > -1)
                    {
                        nearestAncestor.Box.Insert(index, nextBox);
                    }
                    else
                    {
                        nearestAncestor.Box.Add(nextBox);
                    }

                    // Notify the tree that we need to be reflowed
                    node.Propagate_Flag(ENodeFlags.ChildNeedsReflow, exclude_self: true);
                }

                node.ClearFlag(ENodeFlags.NeedsBoxUpdate | ENodeFlags.ChildNeedsBoxUpdate);
                // 6) Queue all children of node.
                foreach (var n in node.childNodes)
                {
                    // Only add items which we KNOW will need an update
                    if (n.GetFlag(ENodeFlags.NeedsBoxUpdate | ENodeFlags.ChildNeedsBoxUpdate))
                    {
                        Queue.Enqueue(n);
                    }
                }
            }
        }