Files
tooll3--t3/Editor/UiModel/Commands/Graph/DeleteSymbolChildrenCommand.cs
2026-07-13 13:13:17 +08:00

176 lines
7.2 KiB
C#

using T3.Core.Animation;
using T3.Core.Operator;
using T3.Core.Operator.Slots;
namespace T3.Editor.UiModel.Commands.Graph;
public class DeleteSymbolChildrenCommand : ICommand
{
public string Name => "Delete Operator";
public bool IsUndoable => true;
private readonly string _creationStack;
public DeleteSymbolChildrenCommand(SymbolUi compositionSymbolUi, List<SymbolUi.Child> uiChildrenToRemove)
{
if(compositionSymbolUi == null)
throw new ArgumentNullException(nameof(compositionSymbolUi));
_creationStack = Environment.StackTrace;
var compositionSymbol = compositionSymbolUi.Symbol;
if(compositionSymbol == null)
throw new ArgumentNullException(nameof(compositionSymbol));
_removedChildren = new ChildEntry[uiChildrenToRemove.Count];
_compositionSymbolId = compositionSymbol.Id;
for (int i = 0; i < uiChildrenToRemove.Count; i++)
{
var childUi = uiChildrenToRemove[i];
var child = childUi.SymbolChild;
var originalValuesForNonDefaultInputs = new Dictionary<Guid, InputValue>();
foreach (var (id, input) in child.Inputs)
{
if(input.IsDefault )
continue;
originalValuesForNonDefaultInputs[id] = input.Value.Clone();
}
var timeClipSettingsForOutputs = new Dictionary<Guid, TimeClip>();
foreach (var (id, output) in child.Outputs)
{
if (output.OutputData is TimeClip timeClip)
{
timeClipSettingsForOutputs[id] =timeClip.Clone();
}
}
_removedChildren[i] = new ChildEntry()
{
SymbolId = child.Symbol.Id,
ChildId = child.Id,
ChildName = child.Name,
PosInCanvas = childUi.PosOnCanvas,
Size = childUi.Size,
SectionId = childUi.SectionId,
OriginalValuesForInputs = originalValuesForNonDefaultInputs,
TimeClipSettingsForOutputs = timeClipSettingsForOutputs,
};
}
}
public void Do()
{
if(!SymbolUiRegistry.TryGetSymbolUi(_compositionSymbolId, out var compositionSymbolUi))
{
this.LogError(false, $"Could not find symbol with id {_compositionSymbolId} - was it removed?\nCreated at stack:\n{_creationStack}", false);
return;
}
var compositionSymbol = compositionSymbolUi!.Symbol;
_removedConnections.Clear();
foreach (var childUndoData in _removedChildren)
{
// first get the connections to the child that is removed and store these, this must be done before
// each child is removed in order to preserve restore-able multi input indices.
var connectionToRemove = compositionSymbol.Connections.FindAll(con => con.SourceParentOrChildId == childUndoData.ChildId
|| con.TargetParentOrChildId == childUndoData.ChildId);
connectionToRemove.Reverse();
foreach (var con in connectionToRemove)
{
var entry = new ConnectionEntry
{
Connection = con,
MultiInputIndex = compositionSymbol.GetMultiInputIndexFor(con)
};
_removedConnections.Add(entry);
compositionSymbol.RemoveConnection(con, entry.MultiInputIndex);
}
compositionSymbolUi.RemoveChild(childUndoData.ChildId);
}
compositionSymbolUi.FlagAsModified();
_removedConnections.Reverse(); // reverse in order to restore in reversed order
}
public void Undo()
{
if(!SymbolUiRegistry.TryGetSymbolUi(_compositionSymbolId, out var compositionSymbolUi))
{
this.LogError(true, $"Could not find symbol with id {_compositionSymbolId} - was it removed?\nCreated at stack:\n{_creationStack}", false);
return;
}
foreach (var childUndoData in _removedChildren)
{
if(!SymbolUiRegistry.TryGetSymbolUi(childUndoData.SymbolId, out var childSymbolUi))
{
this.LogError(true, $"Could not find symbol {childUndoData.SymbolId} - was it removed?\nCreated at stack:\n{_creationStack}", false);
continue;
}
var symbol = childSymbolUi.Symbol;
var symbolChildUi = compositionSymbolUi.AddChild(symbol, childUndoData.ChildId, childUndoData.PosInCanvas, childUndoData.Size);
// Membership is usually re-derived from geometry, but ops hidden in a collapsed
// section are sticky - without restoring the id they would come back orphaned.
symbolChildUi.SectionId = childUndoData.SectionId;
var symbolChild = symbolChildUi.SymbolChild;
foreach (var (inputId, input) in symbolChild!.Inputs)
{
if(childUndoData.OriginalValuesForInputs.TryGetValue(inputId, out var originalValue))
{
input.Value.Assign(originalValue);
input.IsDefault = false;
}
}
foreach (var (outputId, output) in symbolChild.Outputs)
{
if (childUndoData.TimeClipSettingsForOutputs.TryGetValue(outputId, out var timeClip))
{
output.OutputData.Assign(timeClip);
}
}
symbolChild.Name = childUndoData.ChildName;
}
foreach (var entry in _removedConnections)
{
compositionSymbolUi!.Symbol.AddConnection(entry.Connection, entry.MultiInputIndex);
}
compositionSymbolUi.FlagAsModified();
}
private sealed class ChildEntry
{
public Guid SymbolId { get; set; }
public Guid ChildId { get; set; }
public string ChildName { get; set; }
public Vector2 PosInCanvas { get; set; }
public Vector2 Size { get; set; }
public Guid SectionId { get; set; }
public Dictionary<Guid, InputValue> OriginalValuesForInputs { get; set; }
public Dictionary<Guid, TimeClip> TimeClipSettingsForOutputs { get; set; }
}
private sealed class ConnectionEntry
{
public Symbol.Connection Connection { get; set; }
public int MultiInputIndex { get; set; }
}
private readonly Guid _compositionSymbolId;
private readonly ChildEntry[] _removedChildren;
private readonly List<ConnectionEntry> _removedConnections = new();
}