internal void ApplyEdits(int componentId, ArrayBuilderSegment <RenderTreeEdit> edits, ArrayRange <RenderTreeFrame> referenceFrames, RenderBatch batch) { Renderer.Dispatcher.AssertAccess(); if (edits.Count == 0) { // TODO: Without this check there's a NullRef in ArrayBuilderSegment? Possibly a Blazor bug? return; } foreach (var edit in edits) { switch (edit.Type) { case RenderTreeEditType.PrependFrame: ApplyPrependFrame(batch, componentId, edit.SiblingIndex, referenceFrames.Array, edit.ReferenceFrameIndex); break; case RenderTreeEditType.RemoveFrame: ApplyRemoveFrame(edit.SiblingIndex); break; case RenderTreeEditType.SetAttribute: ApplySetAttribute(ref referenceFrames.Array[edit.ReferenceFrameIndex]); break; case RenderTreeEditType.RemoveAttribute: // TODO: See whether siblingIndex is needed here ApplyRemoveAttribute(edit.RemovedAttributeName); break; case RenderTreeEditType.UpdateText: throw new NotImplementedException($"Not supported edit type: {edit.Type}"); case RenderTreeEditType.StepIn: { // TODO: Need to implement this. For now it seems safe to ignore. break; } case RenderTreeEditType.StepOut: { // TODO: Need to implement this. For now it seems safe to ignore. break; } case RenderTreeEditType.UpdateMarkup: throw new NotImplementedException($"Not supported edit type: {edit.Type}"); case RenderTreeEditType.PermutationListEntry: throw new NotImplementedException($"Not supported edit type: {edit.Type}"); case RenderTreeEditType.PermutationListEnd: throw new NotImplementedException($"Not supported edit type: {edit.Type}"); default: throw new NotImplementedException($"Invalid edit type: {edit.Type}"); } } }
internal RenderTreeDiff( int componentId, ArrayBuilderSegment <RenderTreeEdit> entries) { ComponentId = componentId; Edits = entries; }
private void UpdateComponent(RenderBatch batch, int componentId, ArrayBuilderSegment <RenderTreeEdit> edits) { if (!Components.TryGetValue(componentId, out var component)) { component = new ComponentNode(componentId); Components.Add(componentId, component); } ApplyEdits(batch, component, 0, edits); }
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}'"); } } } }
internal void ApplyEdits(int componentId, ArrayBuilderSegment <RenderTreeEdit> edits, ArrayRange <RenderTreeFrame> referenceFrames, RenderBatch batch, HashSet <int> processedComponentIds) { Renderer.Dispatcher.AssertAccess(); foreach (var edit in edits) { switch (edit.Type) { case RenderTreeEditType.PrependFrame: ApplyPrependFrame(batch, componentId, edit.SiblingIndex, referenceFrames.Array, edit.ReferenceFrameIndex, processedComponentIds); break; case RenderTreeEditType.RemoveFrame: ApplyRemoveFrame(edit.SiblingIndex); break; case RenderTreeEditType.SetAttribute: ApplySetAttribute(ref referenceFrames.Array[edit.ReferenceFrameIndex]); break; case RenderTreeEditType.RemoveAttribute: // TODO: See whether siblingIndex is needed here ApplyRemoveAttribute(edit.RemovedAttributeName); break; case RenderTreeEditType.UpdateText: { var frame = batch.ReferenceFrames.Array[edit.ReferenceFrameIndex]; if (_targetElement is IHandleChildContentText handleChildContentText) { handleChildContentText.HandleText(edit.SiblingIndex, frame.TextContent); } else if (!string.IsNullOrWhiteSpace(frame.TextContent)) { throw new Exception("Cannot set text content on child that doesn't handle inner text content."); } break; } case RenderTreeEditType.StepIn: { // TODO: Need to implement this. For now it seems safe to ignore. break; } case RenderTreeEditType.StepOut: { // TODO: Need to implement this. For now it seems safe to ignore. break; } case RenderTreeEditType.UpdateMarkup: { var frame = batch.ReferenceFrames.Array[edit.ReferenceFrameIndex]; if (_targetElement is IHandleChildContentText handleChildContentText) { handleChildContentText.HandleText(edit.SiblingIndex, frame.MarkupContent); } else if (!string.IsNullOrWhiteSpace(frame.MarkupContent)) { throw new Exception("Cannot set markup content on child that doesn't handle inner text content."); } break; } case RenderTreeEditType.PermutationListEntry: throw new NotImplementedException($"Not supported edit type: {edit.Type}"); case RenderTreeEditType.PermutationListEnd: throw new NotImplementedException($"Not supported edit type: {edit.Type}"); default: throw new NotImplementedException($"Invalid edit type: {edit.Type}"); } } }