using System; using System.Collections.Generic; using System.Linq; using RosettaUI.Builder; using UnityEngine; using UnityEngine.UIElements; namespace RosettaUI.UIToolkit.AnimationCurveEditor { /// /// Context menu for control points in the Animation Curve Editor. /// ///
/// refs: Unity CurveMenuManager /// https://github.com/Unity-Technologies/UnityCsReference/blob/4b463aa72c78ec7490b7f03176bd012399881768/Editor/Mono/Animation/AnimationWindow/CurveMenuManager.cs#L72 ///
public static class ControlPointsPopupMenu { public static void Show(Vector2 position, CurveController curveController, Action showEditPositionPopup, VisualElement target) { PopupMenuUtility.Show( CreateMenuItemsDeleteEdit(curveController, showEditPositionPopup) .Append(new MenuItemSeparator()) .Concat(CreateMenuItemsBothTangentMode(curveController)) .Append(new MenuItemSeparator()) .Concat(CreateMenuItemsTangent(curveController)), position, target ); } private static IEnumerable CreateMenuItemsDeleteEdit( CurveController curveController, Action showEditPositionPopup ) { var isMultiSelection = curveController.IsMultiSelection; yield return new MenuItem(isMultiSelection ? "Delete Keys" : "Delete Key", curveController.CommandForSelection.RemoveAllControlPoints); yield return new MenuItem(isMultiSelection ? "Edit Keys..." : "Edit Key...", showEditPositionPopup); } private static IEnumerable CreateMenuItemsBothTangentMode(CurveController curveController) { var anyKeys = curveController.SelectedControlPoints.Any(); var allFreeSmooth = anyKeys; var allFlat = anyKeys; var allBroken = anyKeys; var controlPoints = curveController.SelectedControlPoints; foreach (var controlPoint in controlPoints) { var key = controlPoint.Keyframe; var leftMode = controlPoint.InTangentMode; var rightMode = controlPoint.OutTangentMode; var broken = controlPoint.IsKeyBroken; if (broken || leftMode != TangentMode.Free || rightMode != TangentMode.Free) allFreeSmooth = false; if (broken || leftMode != TangentMode.Free || key.inTangent != 0 || rightMode != TangentMode.Free || key.outTangent != 0) allFlat = false; if (!broken) allBroken = false; } yield return Create("Free Smooth", allFreeSmooth, curveController.CommandForSelection.SetFreeSmooth); yield return Create("Flat", allFlat, curveController.CommandForSelection.SetFlat); yield return Create("Broken", allBroken, () => curveController.CommandForSelection.SetKeyBroken(true)); yield break; MenuItem Create(string name, bool isChecked, Action action) { return new MenuItem(name, action) { isChecked = isChecked }; } } private static IEnumerable CreateMenuItemsTangent(CurveController curveController) { var anyKeys = curveController.SelectedControlPoints.Any(); var allLeftWeighted = anyKeys; var allLeftFree = anyKeys; var allLeftLinear = anyKeys; var allLeftConstant = anyKeys; var allRightWeighted = anyKeys; var allRightFree = anyKeys; var allRightLinear = anyKeys; var allRightConstant = anyKeys; var controlPoints = curveController.SelectedControlPoints; foreach (var controlPoint in controlPoints) { var key = controlPoint.Keyframe; var leftMode = controlPoint.InTangentMode; var rightMode = controlPoint.OutTangentMode; var broken = controlPoint.IsKeyBroken; if (!broken || leftMode != TangentMode.Free) allLeftFree = false; if (!broken || leftMode != TangentMode.Linear) allLeftLinear = false; if (!broken || leftMode != TangentMode.Constant) allLeftConstant = false; if (!broken || rightMode != TangentMode.Free) allRightFree = false; if (!broken || rightMode != TangentMode.Linear) allRightLinear = false; if (!broken || rightMode != TangentMode.Constant) allRightConstant = false; if (((int)key.weightedMode & (int)WeightedMode.In) == 0) allLeftWeighted = false; if (((int)key.weightedMode & (int)WeightedMode.Out) == 0) allRightWeighted = false; } var leftItems = CreateMenuItemsTangent("Left Tangent", allLeftFree, allLeftLinear, allLeftConstant, allLeftWeighted, mode => curveController.CommandForSelection.SetTangentMode(true, false, mode), weighted => curveController.CommandForSelection.SetWeightedMode(WeightedMode.In, weighted) ); var rightItems = CreateMenuItemsTangent("Right Tangent", allRightFree, allRightLinear, allRightConstant, allRightWeighted, mode => curveController.CommandForSelection.SetTangentMode(false, true, mode), weighted => curveController.CommandForSelection.SetWeightedMode(WeightedMode.Out, weighted) ); var bothItems = CreateMenuItemsTangent("Both Tangents", allLeftFree && allRightFree, allLeftLinear && allRightLinear, allLeftConstant && allRightConstant, allLeftWeighted && allRightWeighted, mode => curveController.CommandForSelection.SetTangentMode(true, true, mode), weighted => curveController.CommandForSelection.SetWeightedMode(WeightedMode.Both, weighted) ); return leftItems.Concat(rightItems).Concat(bothItems); } private static IEnumerable CreateMenuItemsTangent(string menuPath, bool allFree, bool allLinear, bool allConstant, bool allWeighted, Action setTangentMode, Action setWeightedMode) { yield return TangentModeMenu(TangentMode.Free, allFree); yield return TangentModeMenu(TangentMode.Linear, allLinear); yield return TangentModeMenu(TangentMode.Constant, allConstant); yield return new MenuItem($"{menuPath}/Weighted", () => setWeightedMode?.Invoke(!allWeighted)) { isChecked = allWeighted }; yield break; MenuItem TangentModeMenu(TangentMode mode, bool isChecked) { return new MenuItem($"{menuPath}/{mode.ToString()}", () => setTangentMode?.Invoke(mode)) { isChecked = isChecked }; } } } }