diff --git a/Assets/Editor/FrameAnimation/FrameAnimationGraphAuthoringServices.cs b/Assets/Editor/FrameAnimation/FrameAnimationGraphAuthoringServices.cs index bd562cbe7..aa2216769 100644 --- a/Assets/Editor/FrameAnimation/FrameAnimationGraphAuthoringServices.cs +++ b/Assets/Editor/FrameAnimation/FrameAnimationGraphAuthoringServices.cs @@ -6,6 +6,70 @@ using UnityEngine; namespace AibisDream.FrameAnimation.Editor { + internal static class FrameAnimationFlowFocusPolicy + { + public static AnimationFlow FindFocusedFlow(FrameAnimationGraph graph, string focusedFlowId) + { + return graph?.Flows.FirstOrDefault(flow => flow != null && flow.Id == focusedFlowId); + } + + public static bool Contains( + FrameAnimationGraph graph, + string focusedFlowId, + FrameAnimationEditorSelection selection) + { + var flow = FindFocusedFlow(graph, focusedFlowId); + if (flow == null) + { + return false; + } + var reachable = new FrameAnimationGraphTopology(graph).GetReachable(flow.EntryNodeId); + return selection.Value switch + { + AnimationNode node => reachable.Nodes.Contains(node), + AnimationEdge edge => reachable.Edges.Contains(edge), + AnimationFlow selectedFlow => selectedFlow == flow, + _ => false + }; + } + + public static bool ShouldExitForSelection( + FrameAnimationGraph graph, + string focusedFlowId, + FrameAnimationEditorSelection selection) + { + if (string.IsNullOrEmpty(focusedFlowId)) + { + return false; + } + return selection.Kind switch + { + FrameAnimationEditorSelectionKind.None => false, + FrameAnimationEditorSelectionKind.Flow => false, + FrameAnimationEditorSelectionKind.Node => !Contains(graph, focusedFlowId, selection), + FrameAnimationEditorSelectionKind.Edge => !Contains(graph, focusedFlowId, selection), + _ => true + }; + } + + public static bool ShouldExitForSelectionSet( + FrameAnimationGraph graph, + string focusedFlowId, + IEnumerable selections) + { + if (string.IsNullOrEmpty(focusedFlowId)) + { + return false; + } + var items = (selections ?? Array.Empty()).ToArray(); + return items.Length > 0 && items.Any(item => + (item.Kind == FrameAnimationEditorSelectionKind.Node || + item.Kind == FrameAnimationEditorSelectionKind.Edge) + ? !Contains(graph, focusedFlowId, item) + : ShouldExitForSelection(graph, focusedFlowId, item)); + } + } + internal sealed class FrameAnimationGraphImpact { public IReadOnlyList Flows { get; } diff --git a/Assets/Editor/FrameAnimation/FrameAnimationGraphEditor.uss b/Assets/Editor/FrameAnimation/FrameAnimationGraphEditor.uss new file mode 100644 index 000000000..0bfb16281 --- /dev/null +++ b/Assets/Editor/FrameAnimation/FrameAnimationGraphEditor.uss @@ -0,0 +1,72 @@ +.fa-preview { + position: relative; + overflow: hidden; + border-left-width: 1px; + border-right-width: 1px; + border-top-width: 1px; + border-bottom-width: 1px; + border-left-color: rgb(25, 25, 25); + border-right-color: rgb(25, 25, 25); + border-top-color: rgb(25, 25, 25); + border-bottom-color: rgb(25, 25, 25); +} + +.fa-preview__image { + position: absolute; +} + +.fa-preview__empty { + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + -unity-text-align: middle-center; + color: rgb(190, 190, 190); +} + +.fa-node__preview { + height: 82px; + margin-top: 5px; +} + +.fa-node--preview-current { + border-left-color: rgb(55, 174, 240); + border-right-color: rgb(55, 174, 240); + border-top-color: rgb(55, 174, 240); + border-bottom-color: rgb(55, 174, 240); +} + +.fa-node--preview-passed { + border-left-color: rgb(83, 154, 113); +} + +.fa-node--preview-upcoming { + opacity: 0.72; +} + +.fa-clip-preview-panel { + flex-shrink: 0; + padding-left: 6px; + padding-right: 6px; + padding-top: 4px; + padding-bottom: 5px; + border-bottom-width: 1px; + border-bottom-color: rgb(28, 28, 28); +} + +.fa-clip-preview-panel .fa-preview { + height: 190px; + margin-bottom: 4px; +} + +.fa-preview-controls { + flex-direction: row; + align-items: center; +} + +.fa-preview-status { + font-size: 10px; + color: rgb(185, 185, 185); + margin-top: 2px; +} diff --git a/Assets/Editor/FrameAnimation/FrameAnimationGraphEditor.uss.meta b/Assets/Editor/FrameAnimation/FrameAnimationGraphEditor.uss.meta new file mode 100644 index 000000000..37c3f50ed --- /dev/null +++ b/Assets/Editor/FrameAnimation/FrameAnimationGraphEditor.uss.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: a38dd39c699e42348667cc1264ea9076 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Editor/FrameAnimation/FrameAnimationGraphEditorWindow.cs b/Assets/Editor/FrameAnimation/FrameAnimationGraphEditorWindow.cs index 85e254869..e556ccf70 100644 --- a/Assets/Editor/FrameAnimation/FrameAnimationGraphEditorWindow.cs +++ b/Assets/Editor/FrameAnimation/FrameAnimationGraphEditorWindow.cs @@ -33,6 +33,8 @@ namespace AibisDream.FrameAnimation.Editor private Label canvasLabel; private FrameAnimationGraphView graphView; private PopupField flowFocusField; + private Label flowFocusStatusLabel; + private ToolbarButton exitFlowFocusButton; private string focusedFlowId = string.Empty; private IReadOnlyList multiSelectedNodes = Array.Empty(); private AnimationNode lastSelectedNode; @@ -64,6 +66,21 @@ namespace AibisDream.FrameAnimation.Editor private SerializedObject clipSerializedObject; private FrameClip frameListClip; private ReorderableList frameList; + private readonly FrameAnimationPreviewCoordinator previewCoordinator = new FrameAnimationPreviewCoordinator(); + private readonly List previewTimelineSliders = new List(); + private readonly List