using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using RosettaUI.Builder;
using RosettaUI.UndoSystem;
using UnityEngine;
using UnityEngine.Pool;
namespace RosettaUI.UIToolkit.AnimationCurveEditor
{
///
/// CurveControllerのUndo関連部分
///
public partial class CurveController
{
///
/// Undoできるコマンド
///
public readonly struct UndoableCommand
{
private readonly CurveController _curveController;
private AnimationCurve Curve => _curveController.Curve;
public UndoableCommand(CurveController curveController) => _curveController = curveController;
public void SetPreWrapMode(WrapMode mode)
{
if (Curve == null) return;
var before = Curve.preWrapMode;
if (before == mode) return;
_curveController.SetPreWrapMode(mode);
Undo.RecordValueChange($"{nameof(CurveController)} SetPreWrapMode", before, mode, _curveController.SetPreWrapMode);
}
public void SetPostWrapMode(WrapMode mode)
{
if (Curve == null) return;
var before = Curve.postWrapMode;
if (before == mode) return;
_curveController.SetPostWrapMode(mode);
Undo.RecordValueChange($"{nameof(CurveController)} SetPostWrapMode", before, mode, _curveController.SetPostWrapMode);
}
public void AddKey(Vector2 keyFramePosition)
{
using var _ = _curveController.RecordUndoSnapshot();
_curveController.AddKey(keyFramePosition);
}
public void SetCurve(AnimationCurve newCurve)
{
if (Curve.Equals(newCurve)) return;
using var _ = _curveController.RecordUndoSnapshot();
_curveController.SetCurve(newCurve);
}
public void SetControlPointKeyframe(ControlPoint controlPoint, Keyframe keyframe)
{
using var _ = _curveController.RecordUndoSnapshot();
_curveController.UpdateKeyframes(new[]{(controlPoint, keyframe)});
}
}
public readonly struct UndoableCommandForSelection
{
private readonly CurveController _curveController;
private IEnumerable SelectedControlPoints => _curveController.SelectedControlPoints;
public UndoableCommandForSelection(CurveController curveController) => _curveController = curveController;
public void RemoveAllControlPoints()
{
using var undoScope = _curveController.RecordUndoSnapshot();
using var _ = ListPool.Get(out var list);
list.AddRange(_curveController.SelectedControlPoints);
foreach (var cp in list)
{
_curveController.DoRemoveControlPoint(cp);
}
_curveController.OnCurveChanged();
}
public void SetFreeSmooth()
{
using var _ = _curveController.RecordUndoSnapshot();
SetTangentModeInternal(true, true, TangentMode.Free);
SetKeyBrokenInternal(false);
}
public void SetFlat()
{
using var _ = _curveController.RecordUndoSnapshot();
SetTangentModeInternal(true, true, TangentMode.Free);
SetKeyBrokenInternal(false);
UpdateKeyframes(keyframe =>
{
keyframe.inTangent = 0;
keyframe.outTangent = 0;
return keyframe;
});
}
public void SetKeyBroken(bool isBroken)
{
using var _ = _curveController.RecordUndoSnapshot();
SetKeyBrokenInternal(isBroken);
}
public void SetTangentMode(bool enableIn, bool enableOut, TangentMode mode)
{
using var _ = _curveController.RecordUndoSnapshot();
SetTangentModeInternal(enableIn, enableOut, mode);
SetKeyBrokenInternal(true);
}
public void SetWeightedMode(WeightedMode weightedMode, bool weighted)
{
using var _ = _curveController.RecordUndoSnapshot();
// weightedになったらTangentMode.Freeにする
// https://github.com/Unity-Technologies/UnityCsReference/blob/4b463aa72c78ec7490b7f03176bd012399881768/Editor/Mono/Animation/AnimationWindow/CurveMenuManager.cs#L188-L202
if (weighted)
{
var (enableIn, enableOut) = weightedMode switch
{
WeightedMode.In => (true, false),
WeightedMode.Out => (false, true),
WeightedMode.Both => (true, true),
_ => (false, false)
};
SetTangentModeInternal(enableIn, enableOut, TangentMode.Free);
}
UpdateKeyframes(keyframe =>
{
keyframe.SetWeightedFrag(weightedMode, weighted);
return keyframe;
});
}
public void SetKeyframePosition(float? xOrNull, float? yOrNull)
{
using var _ = _curveController.RecordUndoSnapshot();
UpdateKeyframes(keyframe =>
{
if (xOrNull is { } x) keyframe.time = x;
if (yOrNull is { } y) keyframe.value = y;
return keyframe;
});
}
private void SetTangentModeInternal(bool enableIn, bool enableOut, TangentMode tangentMode)
{
EditControlPoints(cp =>
{
if (enableIn)
{
cp.InTangentMode = tangentMode;
}
if (enableOut)
{
cp.OutTangentMode = tangentMode;
}
});
}
private void SetKeyBrokenInternal(bool isBroken) => EditControlPoints(cp => cp.IsKeyBroken = isBroken);
private void UpdateKeyframes(Func editKeyframeFunc)
{
UpdateControlPointKeyframes(cp => editKeyframeFunc(cp.Keyframe));
}
private void UpdateControlPointKeyframes(Func controlPointToNewKeyframeFunc)
{
_curveController.UpdateKeyframes(SelectedControlPoints.Select(cp => (cp, controlPointToNewKeyframeFunc(cp))));
}
private void EditControlPoints(Action editAction)
{
foreach (var cp in SelectedControlPoints)
{
editAction(cp);
}
}
}
public readonly struct RecordUndoSnapshotScope : IDisposable
{
private readonly string _commandName;
private readonly CurveController _curveController;
private readonly AnimationCurveUndoSnapshot _beforeSnapshot;
public RecordUndoSnapshotScope(string commandName, CurveController curveController)
{
_commandName = commandName;
_curveController = curveController;
_beforeSnapshot = curveController.RentSnapshot();
}
public void Dispose()
{
var afterSnapshot = _curveController.RentSnapshot();
if (_beforeSnapshot.Equals(afterSnapshot))
{
_beforeSnapshot.Dispose();
afterSnapshot.Dispose();
return;
}
Undo.RecordValueChange(
$"{nameof(CurveController)} {_commandName}",
_beforeSnapshot,
afterSnapshot,
_curveController.ApplySnapshot);
}
}
public UndoableCommand Command => new(this);
public UndoableCommandForSelection CommandForSelection => new(this);
public RecordUndoSnapshotScope RecordUndoSnapshot([CallerMemberName]string callerMethodName = "") => new(callerMethodName, this);
public AnimationCurveUndoSnapshot RentSnapshot()
{
var snapshot = AnimationCurveUndoSnapshot.GetPooled();
snapshot.Initialize(Curve, ControlPoints);
return snapshot;
}
public void ApplySnapshot(AnimationCurveUndoSnapshot snapshot)
{
Curve = snapshot.Curve;
ResetControlPoints(snapshot.extraDataList);
onApplySnapshot?.Invoke();
}
}
}