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; } }
/* * 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); } } } }