private void InsertElement(RenderBatch batch, ContainerNode parent, int childIndex, ArraySegment <RenderTreeFrame> frames, RenderTreeFrame frame, int frameIndex) { // Note: we don't handle SVG here var newElement = new ElementNode(frame.ElementName); parent.InsertLogicalChild(newElement, childIndex); // Apply attributes for (var i = frameIndex + 1; i < frameIndex + frame.ElementSubtreeLength; i++) { var descendantFrame = batch.ReferenceFrames.Array[i]; if (descendantFrame.FrameType == RenderTreeFrameType.Attribute) { ApplyAttribute(batch, newElement, descendantFrame); } else { // As soon as we see a non-attribute child, all the subsequent child frames are // not attributes, so bail out and insert the remnants recursively InsertFrameRange(batch, newElement, 0, frames, i, frameIndex + frame.ElementSubtreeLength); break; } } }
private void ApplyEdits(RenderBatch batch, ContainerNode parent, int childIndex, ArrayBuilderSegment <RenderTreeEdit> edits) { var currentDepth = 0; var childIndexAtCurrentDepth = childIndex; var permutations = new List <PermutationListEntry>(); for (var editIndex = edits.Offset; editIndex < edits.Offset + edits.Count; editIndex++) { var edit = edits.Array[editIndex]; switch (edit.Type) { case RenderTreeEditType.PrependFrame: { var frame = batch.ReferenceFrames.Array[edit.ReferenceFrameIndex]; var siblingIndex = edit.SiblingIndex; InsertFrame(batch, parent, childIndexAtCurrentDepth + siblingIndex, batch.ReferenceFrames.Array, frame, edit.ReferenceFrameIndex); break; } case RenderTreeEditType.RemoveFrame: { var siblingIndex = edit.SiblingIndex; parent.RemoveLogicalChild(childIndexAtCurrentDepth + siblingIndex); break; } case RenderTreeEditType.SetAttribute: { var frame = batch.ReferenceFrames.Array[edit.ReferenceFrameIndex]; var siblingIndex = edit.SiblingIndex; var node = parent.Children[childIndexAtCurrentDepth + siblingIndex]; if (node is ElementNode element) { ApplyAttribute(batch, element, frame); } else { throw new Exception("Cannot set attribute on non-element child"); } break; } case RenderTreeEditType.RemoveAttribute: { // Note that we don't have to dispose the info we track about event handlers here, because the // disposed event handler IDs are delivered separately (in the 'disposedEventHandlerIds' array) var siblingIndex = edit.SiblingIndex; var node = parent.Children[childIndexAtCurrentDepth + siblingIndex]; if (node is ElementNode element) { var attributeName = edit.RemovedAttributeName; // First try to remove any special property we use for this attribute if (!TryApplySpecialProperty(batch, element, attributeName, default)) { // If that's not applicable, it's a regular DOM attribute so remove that element.RemoveAttribute(attributeName); } } else { throw new Exception("Cannot remove attribute from non-element child"); } break; } case RenderTreeEditType.UpdateText: { var frame = batch.ReferenceFrames.Array[edit.ReferenceFrameIndex]; var siblingIndex = edit.SiblingIndex; var node = parent.Children[childIndexAtCurrentDepth + siblingIndex]; if (node is TextNode textNode) { textNode.TextContent = frame.TextContent; } else { throw new Exception("Cannot set text content on non-text child"); } break; } case RenderTreeEditType.UpdateMarkup: { var frame = batch.ReferenceFrames.Array[edit.ReferenceFrameIndex]; var siblingIndex = edit.SiblingIndex; parent.RemoveLogicalChild(childIndexAtCurrentDepth + siblingIndex); InsertMarkup(parent, childIndexAtCurrentDepth + siblingIndex, frame); break; } case RenderTreeEditType.StepIn: { var siblingIndex = edit.SiblingIndex; parent = (ContainerNode)parent.Children[childIndexAtCurrentDepth + siblingIndex]; currentDepth++; childIndexAtCurrentDepth = 0; break; } case RenderTreeEditType.StepOut: { parent = parent.Parent; currentDepth--; childIndexAtCurrentDepth = currentDepth == 0 ? childIndex : 0; // The childIndex is only ever nonzero at zero depth break; } case RenderTreeEditType.PermutationListEntry: { permutations.Add(new PermutationListEntry(childIndexAtCurrentDepth + edit.SiblingIndex, childIndexAtCurrentDepth + edit.MoveToSiblingIndex)); break; } case RenderTreeEditType.PermutationListEnd: { throw new NotSupportedException(); //permuteLogicalChildren(parent, permutations!); //permutations.Clear(); //break; } default: { throw new Exception($"Unknown edit type: '{edit.Type}'"); } } } }