#nullable enable
using System.Collections.Generic;
using RTGLite;
using UnityEngine;
using UVC.Core;
using UVC.Studio.Command;
namespace UVC.Studio.Manager
{
///
/// RTGLite Gizmo와 UndoRedoManager를 연결하는 브릿지 컴포넌트
/// Gizmo 드래그 시작/종료를 감지하여 TransformChangeCommand를 기록합니다.
///
[DefaultExecutionOrder(200)] // RTGizmos 초기화 후 실행
public class GizmoUndoBridge : SingletonScene
{
#region Private Fields
///
/// 드래그 중인지 여부
///
private bool _isDragging = false;
///
/// 드래그 종료 후 프레임 카운터 (클릭 무시용)
///
private int _framesSinceDragEnd = int.MaxValue;
///
/// 현재 드래그 중인 Gizmo
///
private Gizmo? _activeGizmo = null;
///
/// 드래그 시작 시 캡처한 TransformChangeCommand
///
private TransformChangeCommand? _pendingCommand = null;
///
/// 드래그 대상 Transform 리스트 (캐시)
///
private readonly List _dragTargets = new();
#endregion
#region Unity Lifecycle
protected override void Init()
{
Debug.Log("[GizmoUndoBridge] Initialized");
}
private void Start()
{
// UVC UndoRedoManager 이벤트 구독
if (UndoRedoManager.Instance != null)
{
UndoRedoManager.Instance.OnUndo += OnUVCUndo;
UndoRedoManager.Instance.OnRedo += OnUVCRedo;
}
}
private void Update()
{
// 드래그 종료 후 프레임 카운터 증가
if (_framesSinceDragEnd < int.MaxValue)
{
_framesSinceDragEnd++;
}
// RTGizmos가 없으면 스킵
if (RTGizmos.get == null) return;
// 활성화된 드래그 Gizmo 찾기 (RTGizmos.draggedGizmo 사용)
var currentDraggingGizmo = RTGizmos.get.draggedGizmo;
// 드래그 시작 감지
if (!_isDragging && currentDraggingGizmo != null)
{
OnDragStart(currentDraggingGizmo);
}
// 드래그 종료 감지
else if (_isDragging && currentDraggingGizmo == null)
{
OnDragEnd();
}
}
protected override void OnDestroy()
{
if (UndoRedoManager.Instance != null)
{
UndoRedoManager.Instance.OnUndo -= OnUVCUndo;
UndoRedoManager.Instance.OnRedo -= OnUVCRedo;
}
base.OnDestroy();
}
#endregion
#region Drag Detection
///
/// 드래그 시작 시 호출
///
private void OnDragStart(Gizmo gizmo)
{
_isDragging = true;
_activeGizmo = gizmo;
// 드래그 대상 수집
_dragTargets.Clear();
CollectDragTargets(gizmo);
if (_dragTargets.Count == 0)
{
Debug.LogWarning("[GizmoUndoBridge] No drag targets found");
return;
}
// Gizmo 타입에 따른 설명 생성
string description = GetGizmoOperationDescription(gizmo);
// TransformChangeCommand 생성 (Before 상태 캡처)
_pendingCommand = new TransformChangeCommand(_dragTargets, description);
Debug.Log($"[GizmoUndoBridge] Drag started: {_dragTargets.Count}개 객체, {description}");
}
///
/// 드래그 종료 시 호출
///
private void OnDragEnd()
{
_isDragging = false;
_framesSinceDragEnd = 0; // 프레임 카운터 리셋
if (_pendingCommand != null)
{
// After 상태 캡처
_pendingCommand.CaptureAfterState();
// 변경사항이 있으면 UndoRedoManager에 기록
if (_pendingCommand.HasChanges())
{
var undoRedoManager = UndoRedoManager.Instance;
if (undoRedoManager != null)
{
undoRedoManager.RecordCommand(_pendingCommand);
Debug.Log($"[GizmoUndoBridge] Drag ended, recorded: {_pendingCommand.Description}");
}
}
else
{
Debug.Log("[GizmoUndoBridge] Drag ended, no changes detected");
}
}
_pendingCommand = null;
_activeGizmo = null;
_dragTargets.Clear();
}
///
/// Gizmo에서 드래그 대상 Transform을 수집합니다.
///
private void CollectDragTargets(Gizmo gizmo)
{
ObjectTransformGizmo? objectTransformGizmo = null;
// Gizmo 타입에 따라 ObjectTransformGizmo 가져오기
if (gizmo is MoveGizmo moveGizmo)
{
objectTransformGizmo = moveGizmo.objectTransformGizmo;
}
else if (gizmo is RotateGizmo rotateGizmo)
{
objectTransformGizmo = rotateGizmo.objectTransformGizmo;
}
else if (gizmo is ScaleGizmo scaleGizmo)
{
objectTransformGizmo = scaleGizmo.objectTransformGizmo;
}
if (objectTransformGizmo == null || objectTransformGizmo.targetCount == 0)
{
return;
}
// SelectionManager에서 선택된 객체 가져오기
if (InjectorAppContext.Instance != null)
{
var selectionManager = InjectorAppContext.Instance.Get();
if (selectionManager != null)
{
foreach (var stageObject in selectionManager.SelectedObjects)
{
if (stageObject.GameObject != null)
{
_dragTargets.Add(stageObject.GameObject.transform);
}
}
}
}
}
///
/// Gizmo 타입에 따른 작업 설명을 반환합니다.
///
private static string GetGizmoOperationDescription(Gizmo gizmo)
{
return gizmo switch
{
MoveGizmo => "객체 이동",
RotateGizmo => "객체 회전",
ScaleGizmo => "객체 크기 변경",
_ => "객체 변환"
};
}
#endregion
#region UVC Undo/Redo Integration
///
/// UVC UndoRedoManager의 Undo 이벤트 핸들러
///
private void OnUVCUndo(UVC.UI.Commands.IUndoableCommand command)
{
// Transform 관련 커맨드면 RTGizmos 새로고침
if (command is TransformChangeCommand)
{
RefreshGizmos();
}
}
///
/// UVC UndoRedoManager의 Redo 이벤트 핸들러
///
private void OnUVCRedo(UVC.UI.Commands.IUndoableCommand command)
{
// Transform 관련 커맨드면 RTGizmos 새로고침
if (command is TransformChangeCommand)
{
RefreshGizmos();
}
}
///
/// 모든 활성 Gizmo를 새로고침합니다.
/// PropertyWindow에서 Transform 속성 변경 시에도 호출됩니다.
///
public void RefreshGizmos()
{
if (RTGizmos.get == null) return;
// CollectObjectTransformGizmos를 사용하여 모든 ObjectTransformGizmo 수집
var objectTransformGizmos = new List();
RTGizmos.get.CollectObjectTransformGizmos(objectTransformGizmos);
foreach (var transformGizmo in objectTransformGizmos)
{
transformGizmo?.Refresh();
}
Debug.Log("[GizmoUndoBridge] Gizmos refreshed after Undo/Redo");
}
#endregion
#region Public Properties
///
/// 드래그 중이거나 드래그 직후인지 여부
/// 빈 공간 클릭 시 선택 해제 방지용
///
public bool IsDraggingOrJustEnded => _isDragging || _framesSinceDragEnd < 2;
#endregion
}
}