Files
2026-07-13 13:13:17 +08:00

140 lines
4.2 KiB
C#

#nullable enable
using T3.Core.Animation;
using T3.Core.Operator;
using T3.Editor.UiModel.ProjectHandling;
using T3.Editor.UiModel.Selection;
namespace T3.Editor.Gui.Windows.TimeLine.TimeClips;
/// <summary>
/// Maps selection of <see cref="TimeClip"/>s
/// to <see cref="NodeSelection"/> with <see cref="ISelectableCanvasObject"/>s.
/// </summary>
internal sealed class ClipSelection
{
public ClipSelection(NodeSelection nodeSelection)
{
_nodeSelection = nodeSelection;
}
public HashSet<Guid> SelectedClipsIds { get; } = new(100);
public int Count => SelectedClipsIds.Count;
public IReadOnlyCollection<Guid> AllOrSelectedClipIds => SelectedClipsIds.Count > 0 ? SelectedClipsIds : AllClipIds;
public IReadOnlyCollection<Guid> AllClipIds => CompositionTimeClips.Keys;
public IEnumerable<TimeClip> GetAllOrSelectedClips()
{
if (SelectedClipsIds.Count == 0)
{
foreach (var clip in CompositionTimeClips.Values)
{
yield return clip;
}
yield break;
}
foreach (var id in SelectedClipsIds)
{
// A selected id can briefly outlive its clip (e.g. right after a cut replaces it), so skip
// stale selections rather than throwing KeyNotFoundException.
if (CompositionTimeClips.TryGetValue(id, out var clip))
yield return clip;
}
}
public IEnumerable<TimeClip> GetSelectedClips()
{
foreach (var id in SelectedClipsIds)
{
// A selected id can briefly outlive its clip (e.g. right after a cut replaces it), so skip
// stale selections rather than throwing KeyNotFoundException.
if (CompositionTimeClips.TryGetValue(id, out var clip))
yield return clip;
}
}
public void UpdateForComposition(Instance compositionOp)
{
_compositionOp = compositionOp;
CompositionTimeClips.Clear();
foreach (var s2 in Structure.GetAllTimeClips(compositionOp))
{
CompositionTimeClips[s2.Id] = s2;
}
// TODO: It's unfortunate that Selection is a list makes access slow
SelectedClipsIds.Clear();
foreach (var selectedGraphNode in _nodeSelection.Selection)
{
if (CompositionTimeClips.TryGetValue(selectedGraphNode.Id, out var selectedTimeClip))
{
if (!SelectedClipsIds.Add(selectedTimeClip.Id))
{
Log.Warning($"Clip {selectedTimeClip.Id} selected twice?");
}
}
}
}
public void Clear()
{
if (_compositionOp == null)
return;
foreach (var id in SelectedClipsIds)
{
_nodeSelection.DeselectCompositionChild(_compositionOp, id);
}
SelectedClipsIds.Clear();
}
public void Select(TimeClip timeClip)
{
if (_compositionOp == null)
return;
_nodeSelection.Clear();
_nodeSelection.TrySelectCompositionChild(_compositionOp, timeClip.Id);
SelectedClipsIds.Clear();
SelectedClipsIds.Add(timeClip.Id);
}
public void Deselect(TimeClip timeClip)
{
if (_compositionOp == null)
return;
_nodeSelection.DeselectCompositionChild(_compositionOp, timeClip.Id);
SelectedClipsIds.Remove(timeClip.Id);
}
public void AddSelection(TimeClip matchingClip)
{
if (_compositionOp == null)
return;
_nodeSelection.TrySelectCompositionChild(_compositionOp, matchingClip.Id);
SelectedClipsIds.Add(matchingClip.Id);
}
public bool Contains(TimeClip clip)
{
return SelectedClipsIds.Contains(clip.Id);
}
/// <summary>
/// Reusing static collections to avoid GC leaks
/// </summary>
internal readonly Dictionary<Guid, TimeClip> CompositionTimeClips = new(100);
private Instance? _compositionOp;
private readonly NodeSelection _nodeSelection;
}