단축키, Command 설정 개발 중
This commit is contained in:
17
Assets/Scripts/Studio/Command/EditCreatePlaneCommand.cs
Normal file
17
Assets/Scripts/Studio/Command/EditCreatePlaneCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
#nullable enable
|
||||
using UnityEngine;
|
||||
using UVC.UI.Commands;
|
||||
|
||||
namespace UVC.Studio.Command
|
||||
{
|
||||
/// <summary>
|
||||
/// 평면 생성 커맨드
|
||||
/// </summary>
|
||||
public class EditCreatePlaneCommand : ICommand
|
||||
{
|
||||
public void Execute(object? parameter = null)
|
||||
{
|
||||
Debug.Log("[EditCreatePlaneCommand] 평면 생성 실행");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 523b8d20bbc8bb24c8e0ef50a813fa51
|
||||
17
Assets/Scripts/Studio/Command/EditDeleteCommand.cs
Normal file
17
Assets/Scripts/Studio/Command/EditDeleteCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
#nullable enable
|
||||
using UnityEngine;
|
||||
using UVC.UI.Commands;
|
||||
|
||||
namespace UVC.Studio.Command
|
||||
{
|
||||
/// <summary>
|
||||
/// 삭제 커맨드
|
||||
/// </summary>
|
||||
public class EditDeleteCommand : ICommand
|
||||
{
|
||||
public void Execute(object? parameter = null)
|
||||
{
|
||||
Debug.Log("[EditDeleteCommand] 삭제 실행");
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Studio/Command/EditDeleteCommand.cs.meta
Normal file
2
Assets/Scripts/Studio/Command/EditDeleteCommand.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 020434f1949a50b42bc2e42d539f79d0
|
||||
17
Assets/Scripts/Studio/Command/EditDuplicateCommand.cs
Normal file
17
Assets/Scripts/Studio/Command/EditDuplicateCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
#nullable enable
|
||||
using UnityEngine;
|
||||
using UVC.UI.Commands;
|
||||
|
||||
namespace UVC.Studio.Command
|
||||
{
|
||||
/// <summary>
|
||||
/// 복제 커맨드
|
||||
/// </summary>
|
||||
public class EditDuplicateCommand : ICommand
|
||||
{
|
||||
public void Execute(object? parameter = null)
|
||||
{
|
||||
Debug.Log("[EditDuplicateCommand] 복제 실행");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a444000df366b245b0f8de533a3d65d
|
||||
17
Assets/Scripts/Studio/Command/EditRedoCommand.cs
Normal file
17
Assets/Scripts/Studio/Command/EditRedoCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
#nullable enable
|
||||
using UnityEngine;
|
||||
using UVC.UI.Commands;
|
||||
|
||||
namespace UVC.Studio.Command
|
||||
{
|
||||
/// <summary>
|
||||
/// 다시 실행(Redo) 커맨드
|
||||
/// </summary>
|
||||
public class EditRedoCommand : ICommand
|
||||
{
|
||||
public void Execute(object? parameter = null)
|
||||
{
|
||||
Debug.Log("[EditRedoCommand] 다시 실행 실행");
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Studio/Command/EditRedoCommand.cs.meta
Normal file
2
Assets/Scripts/Studio/Command/EditRedoCommand.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db560e448649b964c871f812fb1d13f2
|
||||
17
Assets/Scripts/Studio/Command/EditUndoCommand.cs
Normal file
17
Assets/Scripts/Studio/Command/EditUndoCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
#nullable enable
|
||||
using UnityEngine;
|
||||
using UVC.UI.Commands;
|
||||
|
||||
namespace UVC.Studio.Command
|
||||
{
|
||||
/// <summary>
|
||||
/// 되돌리기(Undo) 커맨드
|
||||
/// </summary>
|
||||
public class EditUndoCommand : ICommand
|
||||
{
|
||||
public void Execute(object? parameter = null)
|
||||
{
|
||||
Debug.Log("[EditUndoCommand] 되돌리기 실행");
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Studio/Command/EditUndoCommand.cs.meta
Normal file
2
Assets/Scripts/Studio/Command/EditUndoCommand.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cba471b0f84f6614bbcab9d29d7b835b
|
||||
17
Assets/Scripts/Studio/Command/FileExportGltfCommand.cs
Normal file
17
Assets/Scripts/Studio/Command/FileExportGltfCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
#nullable enable
|
||||
using UnityEngine;
|
||||
using UVC.UI.Commands;
|
||||
|
||||
namespace UVC.Studio.Command
|
||||
{
|
||||
/// <summary>
|
||||
/// GLTF 내보내기 커맨드
|
||||
/// </summary>
|
||||
public class FileExportGltfCommand : ICommand
|
||||
{
|
||||
public void Execute(object? parameter = null)
|
||||
{
|
||||
Debug.Log("[FileExportGltfCommand] GLTF 내보내기 실행");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ddc95f7402023d14eba701165d846d05
|
||||
17
Assets/Scripts/Studio/Command/FileExportLayoutCommand.cs
Normal file
17
Assets/Scripts/Studio/Command/FileExportLayoutCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
#nullable enable
|
||||
using UnityEngine;
|
||||
using UVC.UI.Commands;
|
||||
|
||||
namespace UVC.Studio.Command
|
||||
{
|
||||
/// <summary>
|
||||
/// Layout Json 내보내기 커맨드
|
||||
/// </summary>
|
||||
public class FileExportLayoutCommand : ICommand
|
||||
{
|
||||
public void Execute(object? parameter = null)
|
||||
{
|
||||
Debug.Log("[FileExportLayoutCommand] Layout Json 내보내기 실행");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af9b092ea7f932545aec538a78f23c4c
|
||||
17
Assets/Scripts/Studio/Command/FileExportMetaCommand.cs
Normal file
17
Assets/Scripts/Studio/Command/FileExportMetaCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
#nullable enable
|
||||
using UnityEngine;
|
||||
using UVC.UI.Commands;
|
||||
|
||||
namespace UVC.Studio.Command
|
||||
{
|
||||
/// <summary>
|
||||
/// Meta Data Json 내보내기 커맨드
|
||||
/// </summary>
|
||||
public class FileExportMetaCommand : ICommand
|
||||
{
|
||||
public void Execute(object? parameter = null)
|
||||
{
|
||||
Debug.Log("[FileExportMetaCommand] Meta Data Json 내보내기 실행");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc4809e03e9bd6343996f1b12cb68b4a
|
||||
17
Assets/Scripts/Studio/Command/FileInsertDbCommand.cs
Normal file
17
Assets/Scripts/Studio/Command/FileInsertDbCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
#nullable enable
|
||||
using UnityEngine;
|
||||
using UVC.UI.Commands;
|
||||
|
||||
namespace UVC.Studio.Command
|
||||
{
|
||||
/// <summary>
|
||||
/// 데이터베이스 삽입 커맨드
|
||||
/// </summary>
|
||||
public class FileInsertDbCommand : ICommand
|
||||
{
|
||||
public void Execute(object? parameter = null)
|
||||
{
|
||||
Debug.Log("[FileInsertDbCommand] 데이터베이스 삽입 실행");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 210fe2c58c6bba44eb2fb812b8bc59dc
|
||||
17
Assets/Scripts/Studio/Command/FileNewCommand.cs
Normal file
17
Assets/Scripts/Studio/Command/FileNewCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
#nullable enable
|
||||
using UnityEngine;
|
||||
using UVC.UI.Commands;
|
||||
|
||||
namespace UVC.Studio.Command
|
||||
{
|
||||
/// <summary>
|
||||
/// 새 파일 생성 커맨드
|
||||
/// </summary>
|
||||
public class FileNewCommand : ICommand
|
||||
{
|
||||
public void Execute(object? parameter = null)
|
||||
{
|
||||
Debug.Log("[FileNewCommand] 새 파일 생성 실행");
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Studio/Command/FileNewCommand.cs.meta
Normal file
2
Assets/Scripts/Studio/Command/FileNewCommand.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 86262663cb50b634689a511589245f0a
|
||||
17
Assets/Scripts/Studio/Command/FileOpenCommand.cs
Normal file
17
Assets/Scripts/Studio/Command/FileOpenCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
#nullable enable
|
||||
using UnityEngine;
|
||||
using UVC.UI.Commands;
|
||||
|
||||
namespace UVC.Studio.Command
|
||||
{
|
||||
/// <summary>
|
||||
/// 파일 열기 커맨드
|
||||
/// </summary>
|
||||
public class FileOpenCommand : ICommand
|
||||
{
|
||||
public void Execute(object? parameter = null)
|
||||
{
|
||||
Debug.Log("[FileOpenCommand] 파일 열기 실행");
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Studio/Command/FileOpenCommand.cs.meta
Normal file
2
Assets/Scripts/Studio/Command/FileOpenCommand.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70708e355b03ef14a8473c8615e57cb2
|
||||
17
Assets/Scripts/Studio/Command/FileSaveAsCommand.cs
Normal file
17
Assets/Scripts/Studio/Command/FileSaveAsCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
#nullable enable
|
||||
using UnityEngine;
|
||||
using UVC.UI.Commands;
|
||||
|
||||
namespace UVC.Studio.Command
|
||||
{
|
||||
/// <summary>
|
||||
/// 다른 이름으로 저장 커맨드
|
||||
/// </summary>
|
||||
public class FileSaveAsCommand : ICommand
|
||||
{
|
||||
public void Execute(object? parameter = null)
|
||||
{
|
||||
Debug.Log("[FileSaveAsCommand] 다른 이름으로 저장 실행");
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Studio/Command/FileSaveAsCommand.cs.meta
Normal file
2
Assets/Scripts/Studio/Command/FileSaveAsCommand.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 897b4bc4deb9e6648ba03de4bc5fec8b
|
||||
17
Assets/Scripts/Studio/Command/FileSaveCommand.cs
Normal file
17
Assets/Scripts/Studio/Command/FileSaveCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
#nullable enable
|
||||
using UnityEngine;
|
||||
using UVC.UI.Commands;
|
||||
|
||||
namespace UVC.Studio.Command
|
||||
{
|
||||
/// <summary>
|
||||
/// 파일 저장 커맨드
|
||||
/// </summary>
|
||||
public class FileSaveCommand : ICommand
|
||||
{
|
||||
public void Execute(object? parameter = null)
|
||||
{
|
||||
Debug.Log("[FileSaveCommand] 파일 저장 실행");
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Studio/Command/FileSaveCommand.cs.meta
Normal file
2
Assets/Scripts/Studio/Command/FileSaveCommand.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7edb402279b68d648b565362457cdbf8
|
||||
8
Assets/Scripts/Studio/Input.meta
Normal file
8
Assets/Scripts/Studio/Input.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 26cbb88db439178438f4bc3bd8180d1f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,8 +1,8 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using EPOOutline;
|
||||
using RTGLite;
|
||||
using UnityEngine;
|
||||
using UVC.Core;
|
||||
using UVC.Studio.Config;
|
||||
@@ -11,11 +11,28 @@ using UVC.UI.Window.PropertyWindow;
|
||||
|
||||
namespace UVC.Studio.Manager
|
||||
{
|
||||
/// <summary>
|
||||
/// 변환 도구 타입
|
||||
/// </summary>
|
||||
public enum TransformToolType
|
||||
{
|
||||
/// <summary>선택 도구 (기즈모 없음)</summary>
|
||||
Select,
|
||||
/// <summary>이동 도구</summary>
|
||||
Move,
|
||||
/// <summary>회전 도구</summary>
|
||||
Rotate,
|
||||
/// <summary>크기 조절 도구</summary>
|
||||
Scale
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 스테이지 객체의 선택 상태를 관리하는 매니저 클래스
|
||||
/// </summary>
|
||||
public class SelectionManager
|
||||
{
|
||||
#region Private Fields - Selection
|
||||
|
||||
/// <summary>
|
||||
/// 현재 선택된 StageObject 목록
|
||||
/// </summary>
|
||||
@@ -36,12 +53,60 @@ namespace UVC.Studio.Manager
|
||||
/// </summary>
|
||||
private StageObjectManager.StageObject? _currentDisplayedObject;
|
||||
|
||||
/// <summary>
|
||||
/// 현재 PropertyWindow에 표시 중인 자식 Transform (모델의 자식 선택 시)
|
||||
/// </summary>
|
||||
private Transform? _currentDisplayedChildTransform;
|
||||
|
||||
/// <summary>
|
||||
/// 미리보기 색상 적용 전 원본 Material 색상을 저장하는 딕셔너리
|
||||
/// Key: Renderer, Value: (프로퍼티 이름, 원본 색상)
|
||||
/// </summary>
|
||||
private readonly Dictionary<Renderer, (string propertyName, Color color)> _originalColors = new();
|
||||
|
||||
/// <summary>
|
||||
/// 캐싱된 Outliner 참조 (매번 FindFirstObjectByType 호출 방지)
|
||||
/// </summary>
|
||||
private Outliner? _cachedOutliner;
|
||||
|
||||
/// <summary>
|
||||
/// Outliner 캐시 초기화 여부
|
||||
/// </summary>
|
||||
private bool _outlinerCacheInitialized = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Fields - Gizmos
|
||||
|
||||
/// <summary>
|
||||
/// 이동 기즈모
|
||||
/// </summary>
|
||||
private MoveGizmo? _moveGizmo;
|
||||
|
||||
/// <summary>
|
||||
/// 회전 기즈모
|
||||
/// </summary>
|
||||
private RotateGizmo? _rotateGizmo;
|
||||
|
||||
/// <summary>
|
||||
/// 크기 조절 기즈모
|
||||
/// </summary>
|
||||
private ScaleGizmo? _scaleGizmo;
|
||||
|
||||
/// <summary>
|
||||
/// 현재 활성화된 변환 도구 타입
|
||||
/// </summary>
|
||||
private TransformToolType _currentToolType = TransformToolType.Select;
|
||||
|
||||
/// <summary>
|
||||
/// 기즈모 초기화 여부
|
||||
/// </summary>
|
||||
private bool _gizmosInitialized = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// 선택 변경 시 발생하는 이벤트
|
||||
/// (선택된 객체, 선택 여부)
|
||||
@@ -54,6 +119,15 @@ namespace UVC.Studio.Manager
|
||||
/// </summary>
|
||||
public event Action<string, string>? OnObjectNameChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 변환 도구가 변경되었을 때 발생하는 이벤트
|
||||
/// </summary>
|
||||
public event Action<TransformToolType>? OnToolChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// 현재 선택된 객체 목록 (읽기 전용)
|
||||
/// </summary>
|
||||
@@ -64,6 +138,13 @@ namespace UVC.Studio.Manager
|
||||
/// </summary>
|
||||
public int SelectedCount => _selectedObjects.Count;
|
||||
|
||||
/// <summary>
|
||||
/// 현재 활성화된 변환 도구 타입
|
||||
/// </summary>
|
||||
public TransformToolType CurrentToolType => _currentToolType;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 생성자
|
||||
/// </summary>
|
||||
@@ -234,22 +315,18 @@ namespace UVC.Studio.Manager
|
||||
EnsureOutlinableSettings(outlinable);
|
||||
|
||||
outlinable.enabled = enabled;
|
||||
Debug.Log($"[SelectionManager] SetOutlinableEnabled: {stageObject.GameObject.name} -> {enabled}, Targets: {outlinable.OutlineTargets.Count}, RenderStyle: {outlinable.RenderStyle}, FrontEnabled: {outlinable.FrontParameters.Enabled}, FrontColor: {outlinable.FrontParameters.Color}");
|
||||
|
||||
// Outliner 존재 여부 확인
|
||||
var outliner = UnityEngine.Object.FindFirstObjectByType<Outliner>();
|
||||
if (outliner != null)
|
||||
// Outliner 존재 여부 확인 (캐싱)
|
||||
if (!_outlinerCacheInitialized)
|
||||
{
|
||||
Debug.Log($"[SelectionManager] Outliner found on: {outliner.gameObject.name}, enabled: {outliner.enabled}");
|
||||
_cachedOutliner = UnityEngine.Object.FindFirstObjectByType<Outliner>();
|
||||
_outlinerCacheInitialized = true;
|
||||
|
||||
if (_cachedOutliner == null)
|
||||
{
|
||||
Debug.LogWarning("[SelectionManager] Outliner component not found in scene! EPOOutline requires Outliner on camera.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[SelectionManager] Outliner component not found in scene! EPOOutline requires Outliner on camera.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[SelectionManager] SetOutlinableEnabled: Outlinable not found on {stageObject.GameObject.name}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,6 +501,53 @@ namespace UVC.Studio.Manager
|
||||
|
||||
#region PropertyWindow Integration
|
||||
|
||||
/// <summary>
|
||||
/// 자식 Transform의 속성을 PropertyWindow에 표시합니다.
|
||||
/// </summary>
|
||||
/// <param name="childTransform">표시할 자식 Transform</param>
|
||||
/// <param name="childName">자식 이름</param>
|
||||
public void DisplayChildTransformProperties(Transform childTransform, string childName)
|
||||
{
|
||||
if (_propertyWindow == null || childTransform == null) return;
|
||||
|
||||
// 기존 StageObject 표시 상태 초기화
|
||||
_currentDisplayedObject = null;
|
||||
_currentDisplayedChildTransform = childTransform;
|
||||
|
||||
var entries = new List<IPropertyEntry>();
|
||||
int orderIndex = 0;
|
||||
|
||||
// 1. 자식 이름 표시 (읽기 전용)
|
||||
var nameProperty = new StringProperty("child_name", "Name", childName)
|
||||
{
|
||||
IsReadOnly = true,
|
||||
Order = orderIndex++
|
||||
};
|
||||
entries.Add(nameProperty);
|
||||
|
||||
// 2. Transform 그룹 추가
|
||||
var transformGroup = new PropertyGroup("transform", "Transform", order: orderIndex++);
|
||||
transformGroup.AddItems(new IPropertyItem[]
|
||||
{
|
||||
new Vector3Property("transform_position", "Position", childTransform.localPosition),
|
||||
new Vector3Property("transform_rotation", "Rotation", childTransform.localEulerAngles),
|
||||
new Vector3Property("transform_scale", "Scale", childTransform.localScale)
|
||||
});
|
||||
entries.Add(transformGroup);
|
||||
|
||||
_propertyWindow.LoadMixedProperties(entries);
|
||||
|
||||
Debug.Log($"[SelectionManager] DisplayChildTransformProperties: {childName}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 자식 Transform 표시 상태를 초기화합니다.
|
||||
/// </summary>
|
||||
public void ClearChildTransformDisplay()
|
||||
{
|
||||
_currentDisplayedChildTransform = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// StageObject의 Equipment PropertiesInfo를 PropertyWindow에 표시합니다.
|
||||
/// </summary>
|
||||
@@ -432,6 +556,8 @@ namespace UVC.Studio.Manager
|
||||
{
|
||||
if (_propertyWindow == null) return;
|
||||
|
||||
// 자식 Transform 표시 상태 초기화
|
||||
_currentDisplayedChildTransform = null;
|
||||
_currentDisplayedObject = stageObject;
|
||||
|
||||
var equipment = stageObject.Equipment;
|
||||
@@ -544,6 +670,33 @@ namespace UVC.Studio.Manager
|
||||
/// </summary>
|
||||
private void OnPropertyValueChanged(object? sender, PropertyValueChangedEventArgs e)
|
||||
{
|
||||
// 자식 Transform이 표시 중인 경우
|
||||
if (_currentDisplayedChildTransform != null)
|
||||
{
|
||||
switch (e.PropertyId)
|
||||
{
|
||||
case "transform_position":
|
||||
if (e.NewValue is Vector3 childPos)
|
||||
{
|
||||
_currentDisplayedChildTransform.localPosition = childPos;
|
||||
}
|
||||
break;
|
||||
case "transform_rotation":
|
||||
if (e.NewValue is Vector3 childRot)
|
||||
{
|
||||
_currentDisplayedChildTransform.localEulerAngles = childRot;
|
||||
}
|
||||
break;
|
||||
case "transform_scale":
|
||||
if (e.NewValue is Vector3 childScale)
|
||||
{
|
||||
_currentDisplayedChildTransform.localScale = childScale;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentDisplayedObject == null) return;
|
||||
|
||||
switch (e.PropertyId)
|
||||
@@ -706,5 +859,314 @@ namespace UVC.Studio.Manager
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Gizmo Management
|
||||
|
||||
/// <summary>
|
||||
/// 기즈모를 초기화합니다. RTGizmos가 준비된 후 호출해야 합니다.
|
||||
/// </summary>
|
||||
public void InitializeGizmos()
|
||||
{
|
||||
if (_gizmosInitialized) return;
|
||||
if (RTGizmos.get == null)
|
||||
{
|
||||
Debug.LogWarning("[SelectionManager] RTGizmos가 아직 초기화되지 않았습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 기즈모 생성
|
||||
_moveGizmo = RTGizmos.get.CreateObjectMoveGizmo();
|
||||
_rotateGizmo = RTGizmos.get.CreateObjectRotateGizmo();
|
||||
_scaleGizmo = RTGizmos.get.CreateObjectScaleGizmo();
|
||||
|
||||
// 기본적으로 모든 기즈모 비활성화 (Select 모드)
|
||||
_moveGizmo.enabled = false;
|
||||
_rotateGizmo.enabled = false;
|
||||
_scaleGizmo.enabled = false;
|
||||
|
||||
_gizmosInitialized = true;
|
||||
_currentToolType = TransformToolType.Select;
|
||||
|
||||
Debug.Log("[SelectionManager] 기즈모가 초기화되었습니다.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 마우스 위치에서 객체를 픽킹합니다. RTScene.Raycast를 사용합니다.
|
||||
/// </summary>
|
||||
/// <returns>픽킹된 GameObject, 없으면 null</returns>
|
||||
public GameObject? PickObject()
|
||||
{
|
||||
if (RTScene.get == null) return null;
|
||||
|
||||
// 마우스 위치에서 Ray 생성
|
||||
Ray ray = Camera.main.ScreenPointToRay(UnityEngine.Input.mousePosition);
|
||||
|
||||
// RTScene의 Raycast 사용 (Collider 없이도 MeshRenderer로 감지)
|
||||
if (RTScene.get.Raycast(ray, null, false, out SceneRayHit rayHit))
|
||||
{
|
||||
return rayHit.objectHit.gameObject;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 마우스 클릭으로 객체를 선택합니다. Update에서 호출됩니다.
|
||||
/// </summary>
|
||||
/// <param name="addToSelection">다중 선택 여부 (Ctrl 키 누름)</param>
|
||||
public void HandleMouseSelection(bool addToSelection = false)
|
||||
{
|
||||
var pickedObject = PickObject();
|
||||
|
||||
if (pickedObject == null)
|
||||
{
|
||||
// 빈 공간 클릭 시 선택 해제 (Ctrl 키가 눌려있지 않을 때만)
|
||||
if (!addToSelection)
|
||||
{
|
||||
DeselectAll();
|
||||
UpdateGizmoTargets();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 픽킹된 객체가 StageObject인지 확인
|
||||
var stageObject = FindStageObjectForGameObject(pickedObject);
|
||||
if (stageObject != null)
|
||||
{
|
||||
if (addToSelection)
|
||||
{
|
||||
// Ctrl+클릭: 토글 선택
|
||||
ToggleSelection(stageObject, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 일반 클릭: 단일 선택
|
||||
Select(stageObject, false);
|
||||
}
|
||||
|
||||
// 기즈모 타겟 업데이트
|
||||
UpdateGizmoTargets();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GameObject로부터 해당하는 StageObject를 찾습니다.
|
||||
/// </summary>
|
||||
/// <param name="gameObject">검색할 GameObject</param>
|
||||
/// <returns>찾은 StageObject, 없으면 null</returns>
|
||||
private StageObjectManager.StageObject? FindStageObjectForGameObject(GameObject gameObject)
|
||||
{
|
||||
if (gameObject == null) return null;
|
||||
|
||||
// StageObjectManager의 GetByGameObject 사용
|
||||
var stageObject = _stageObjectManager.GetByGameObject(gameObject);
|
||||
if (stageObject != null)
|
||||
{
|
||||
return stageObject;
|
||||
}
|
||||
|
||||
// 부모 계층에서 StageObject 검색
|
||||
Transform? current = gameObject.transform.parent;
|
||||
while (current != null)
|
||||
{
|
||||
stageObject = _stageObjectManager.GetByGameObject(current.gameObject);
|
||||
if (stageObject != null)
|
||||
{
|
||||
return stageObject;
|
||||
}
|
||||
current = current.parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 선택된 모든 객체를 기즈모 타겟으로 설정합니다.
|
||||
/// </summary>
|
||||
public void UpdateGizmoTargets()
|
||||
{
|
||||
if (!_gizmosInitialized) return;
|
||||
|
||||
// 선택된 객체들의 GameObject 리스트 생성
|
||||
var targetObjects = new List<GameObject>();
|
||||
foreach (var stageObject in _selectedObjects)
|
||||
{
|
||||
if (stageObject.GameObject != null)
|
||||
{
|
||||
targetObjects.Add(stageObject.GameObject);
|
||||
}
|
||||
}
|
||||
|
||||
// 모든 기즈모에 타겟 설정
|
||||
if (_moveGizmo != null)
|
||||
{
|
||||
_moveGizmo.objectTransformGizmo.SetTargets(targetObjects);
|
||||
}
|
||||
if (_rotateGizmo != null)
|
||||
{
|
||||
_rotateGizmo.objectTransformGizmo.SetTargets(targetObjects);
|
||||
}
|
||||
if (_scaleGizmo != null)
|
||||
{
|
||||
_scaleGizmo.objectTransformGizmo.SetTargets(targetObjects);
|
||||
}
|
||||
|
||||
// 현재 활성 기즈모 새로고침
|
||||
RefreshActiveGizmo();
|
||||
|
||||
Debug.Log($"[SelectionManager] 기즈모 타겟 업데이트: {targetObjects.Count}개 객체");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 활성 변환 도구를 설정합니다.
|
||||
/// </summary>
|
||||
/// <param name="toolType">설정할 도구 타입</param>
|
||||
public void SetActiveTool(TransformToolType toolType)
|
||||
{
|
||||
if (!_gizmosInitialized)
|
||||
{
|
||||
Debug.LogWarning("[SelectionManager] 기즈모가 초기화되지 않았습니다. InitializeGizmos()를 먼저 호출하세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
_currentToolType = toolType;
|
||||
|
||||
// 모든 기즈모 비활성화
|
||||
if (_moveGizmo != null) _moveGizmo.enabled = false;
|
||||
if (_rotateGizmo != null) _rotateGizmo.enabled = false;
|
||||
if (_scaleGizmo != null) _scaleGizmo.enabled = false;
|
||||
|
||||
// 선택된 도구에 해당하는 기즈모 활성화
|
||||
switch (toolType)
|
||||
{
|
||||
case TransformToolType.Move:
|
||||
if (_moveGizmo != null)
|
||||
{
|
||||
_moveGizmo.enabled = true;
|
||||
_moveGizmo.objectTransformGizmo.Refresh();
|
||||
}
|
||||
break;
|
||||
|
||||
case TransformToolType.Rotate:
|
||||
if (_rotateGizmo != null)
|
||||
{
|
||||
_rotateGizmo.enabled = true;
|
||||
_rotateGizmo.objectTransformGizmo.Refresh();
|
||||
}
|
||||
break;
|
||||
|
||||
case TransformToolType.Scale:
|
||||
if (_scaleGizmo != null)
|
||||
{
|
||||
_scaleGizmo.enabled = true;
|
||||
_scaleGizmo.objectTransformGizmo.Refresh();
|
||||
}
|
||||
break;
|
||||
|
||||
case TransformToolType.Select:
|
||||
default:
|
||||
// 모든 기즈모 비활성화 상태 유지
|
||||
break;
|
||||
}
|
||||
|
||||
Debug.Log($"[SelectionManager] 활성 도구 변경: {toolType}");
|
||||
OnToolChanged?.Invoke(toolType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 현재 활성화된 기즈모를 새로고침합니다.
|
||||
/// </summary>
|
||||
public void RefreshActiveGizmo()
|
||||
{
|
||||
if (!_gizmosInitialized) return;
|
||||
|
||||
switch (_currentToolType)
|
||||
{
|
||||
case TransformToolType.Move:
|
||||
_moveGizmo?.objectTransformGizmo.Refresh();
|
||||
break;
|
||||
case TransformToolType.Rotate:
|
||||
_rotateGizmo?.objectTransformGizmo.Refresh();
|
||||
break;
|
||||
case TransformToolType.Scale:
|
||||
_scaleGizmo?.objectTransformGizmo.Refresh();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 기즈모가 초기화되었는지 여부를 반환합니다.
|
||||
/// </summary>
|
||||
public bool IsGizmosInitialized => _gizmosInitialized;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Memory Management
|
||||
|
||||
/// <summary>
|
||||
/// 리소스를 정리합니다. 씬 전환 시 호출해야 합니다.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
// 이벤트 구독 해제
|
||||
if (_propertyWindow != null)
|
||||
{
|
||||
_propertyWindow.PropertyValueChanged -= OnPropertyValueChanged;
|
||||
}
|
||||
|
||||
// 선택 해제
|
||||
DeselectAll();
|
||||
|
||||
// 원본 색상 딕셔너리 정리
|
||||
_originalColors.Clear();
|
||||
|
||||
// 캐시 초기화
|
||||
_cachedOutliner = null;
|
||||
_outlinerCacheInitialized = false;
|
||||
|
||||
// 기즈모 참조 해제 (RTGizmos가 실제 파괴 담당)
|
||||
_moveGizmo = null;
|
||||
_rotateGizmo = null;
|
||||
_scaleGizmo = null;
|
||||
_gizmosInitialized = false;
|
||||
|
||||
Debug.Log("[SelectionManager] Disposed");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 파괴된 Renderer 참조를 정리합니다.
|
||||
/// 주기적으로 또는 객체 삭제 시 호출하면 좋습니다.
|
||||
/// </summary>
|
||||
public void CleanupDestroyedRenderers()
|
||||
{
|
||||
// Unity에서 파괴된 객체는 == null로 체크 가능하지만 실제로는 null이 아님
|
||||
// 따라서 null 체크된 키들을 별도 리스트에 저장 후 삭제
|
||||
var keysToRemove = new List<Renderer>();
|
||||
|
||||
foreach (var kvp in _originalColors)
|
||||
{
|
||||
// Unity 객체의 파괴 여부 확인
|
||||
if (kvp.Key == null || !kvp.Key)
|
||||
{
|
||||
keysToRemove.Add(kvp.Key!);
|
||||
}
|
||||
}
|
||||
|
||||
int removedCount = 0;
|
||||
foreach (var key in keysToRemove)
|
||||
{
|
||||
if (_originalColors.Remove(key))
|
||||
{
|
||||
removedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (removedCount > 0)
|
||||
{
|
||||
Debug.Log($"[SelectionManager] Cleaned up {removedCount} destroyed renderer references");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
455
Assets/Scripts/Studio/Manager/StudioShortcutManager.cs
Normal file
455
Assets/Scripts/Studio/Manager/StudioShortcutManager.cs
Normal file
@@ -0,0 +1,455 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using RTGLite;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UVC.Core;
|
||||
using UVC.Studio.Config;
|
||||
using UVC.UI.Commands;
|
||||
|
||||
namespace UVC.Studio.Manager
|
||||
{
|
||||
/// <summary>
|
||||
/// Studio 단축키 관리자
|
||||
/// RTGLite의 RTInput과 UVC Setting 시스템을 연결하는 브릿지 역할을 합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>[ 개요 ]</b></para>
|
||||
/// <para>RTInput의 카메라/그리드/기즈모 단축키는 그대로 유지하면서,</para>
|
||||
/// <para>UVC의 메뉴와 도구 단축키를 Setting에서 관리합니다.</para>
|
||||
///
|
||||
/// <para><b>[ 사용 예시 ]</b></para>
|
||||
/// <code>
|
||||
/// // 1. 단축키 등록
|
||||
/// shortcutManager.RegisterMenuShortcut("newProject", () => new FileNewCommand().Execute());
|
||||
///
|
||||
/// // 2. 단축키 활성화/비활성화
|
||||
/// shortcutManager.SetShortcutEnabled("newProject", false);
|
||||
///
|
||||
/// // 3. 설정 변경 후 갱신
|
||||
/// shortcutManager.RefreshShortcuts();
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
[DefaultExecutionOrder(100)] // RTInput보다 늦게 실행
|
||||
public class StudioShortcutManager : MonoBehaviour
|
||||
{
|
||||
#region Singleton
|
||||
private static StudioShortcutManager? _instance;
|
||||
public static StudioShortcutManager? Instance => _instance;
|
||||
#endregion
|
||||
|
||||
#region Private Fields
|
||||
private Setting? _setting;
|
||||
|
||||
// 메뉴 단축키 액션 매핑
|
||||
private readonly Dictionary<string, ShortcutEntry> _menuShortcuts = new();
|
||||
|
||||
// 도구 단축키 액션 매핑
|
||||
private readonly Dictionary<string, ShortcutEntry> _toolShortcuts = new();
|
||||
|
||||
// 단축키 활성화 상태
|
||||
private readonly Dictionary<string, bool> _shortcutEnabled = new();
|
||||
|
||||
// 전역 단축키 비활성화 플래그 (모달 등에서 사용)
|
||||
private bool _globalEnabled = true;
|
||||
|
||||
// 입력 필드 포커스 상태 캐싱
|
||||
private bool _isInputFieldFocused = false;
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
/// <summary>
|
||||
/// 단축키 전역 활성화 상태
|
||||
/// </summary>
|
||||
public bool GlobalEnabled
|
||||
{
|
||||
get => _globalEnabled;
|
||||
set => _globalEnabled = value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Unity Lifecycle
|
||||
private void Awake()
|
||||
{
|
||||
if (_instance != null && _instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
_instance = this;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// Setting 가져오기
|
||||
if (InjectorAppContext.Instance != null)
|
||||
{
|
||||
_setting = InjectorAppContext.Instance.Get<Setting>();
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// 전역 비활성화 상태면 스킵
|
||||
if (!_globalEnabled) return;
|
||||
|
||||
// 입력 필드에 포커스가 있으면 스킵
|
||||
if (IsInputFieldFocused()) return;
|
||||
|
||||
// RTInput의 마우스 조작 중에는 스킵 (카메라 이동 등)
|
||||
if (RTInput.get != null && RTInput.get.anyMBPressed) return;
|
||||
|
||||
// 단축키 처리
|
||||
ProcessShortcuts();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (_instance == this)
|
||||
{
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
/// <summary>
|
||||
/// Setting을 주입합니다.
|
||||
/// </summary>
|
||||
public void SetSetting(Setting setting)
|
||||
{
|
||||
_setting = setting;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 메뉴 단축키를 등록합니다.
|
||||
/// </summary>
|
||||
/// <param name="shortcutId">단축키 ID (Setting의 필드명과 매칭)</param>
|
||||
/// <param name="action">실행할 액션</param>
|
||||
public void RegisterMenuShortcut(string shortcutId, Action action)
|
||||
{
|
||||
_menuShortcuts[shortcutId] = new ShortcutEntry(action);
|
||||
_shortcutEnabled[shortcutId] = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 메뉴 단축키를 ICommand와 함께 등록합니다.
|
||||
/// </summary>
|
||||
public void RegisterMenuShortcut(string shortcutId, ICommand command)
|
||||
{
|
||||
RegisterMenuShortcut(shortcutId, () => command.Execute());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 도구 단축키를 등록합니다.
|
||||
/// </summary>
|
||||
public void RegisterToolShortcut(string shortcutId, Action action)
|
||||
{
|
||||
_toolShortcuts[shortcutId] = new ShortcutEntry(action);
|
||||
_shortcutEnabled[shortcutId] = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 도구 단축키를 ICommand와 함께 등록합니다.
|
||||
/// </summary>
|
||||
public void RegisterToolShortcut(string shortcutId, ICommand command)
|
||||
{
|
||||
RegisterToolShortcut(shortcutId, () => command.Execute());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 단축키 활성화 상태를 설정합니다.
|
||||
/// </summary>
|
||||
public void SetShortcutEnabled(string shortcutId, bool enabled)
|
||||
{
|
||||
_shortcutEnabled[shortcutId] = enabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 모든 단축키의 활성화 상태를 조회합니다.
|
||||
/// </summary>
|
||||
public bool IsShortcutEnabled(string shortcutId)
|
||||
{
|
||||
return _shortcutEnabled.TryGetValue(shortcutId, out var enabled) && enabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setting에서 단축키 정보를 다시 읽어옵니다.
|
||||
/// </summary>
|
||||
public void RefreshShortcuts()
|
||||
{
|
||||
if (_setting == null && InjectorAppContext.Instance != null)
|
||||
{
|
||||
_setting = InjectorAppContext.Instance.Get<Setting>();
|
||||
}
|
||||
|
||||
Debug.Log("[StudioShortcutManager] 단축키가 갱신되었습니다.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 등록된 단축키를 제거합니다.
|
||||
/// </summary>
|
||||
public void UnregisterShortcut(string shortcutId)
|
||||
{
|
||||
_menuShortcuts.Remove(shortcutId);
|
||||
_toolShortcuts.Remove(shortcutId);
|
||||
_shortcutEnabled.Remove(shortcutId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 모든 등록된 단축키를 제거합니다.
|
||||
/// </summary>
|
||||
public void ClearAllShortcuts()
|
||||
{
|
||||
_menuShortcuts.Clear();
|
||||
_toolShortcuts.Clear();
|
||||
_shortcutEnabled.Clear();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
/// <summary>
|
||||
/// 단축키를 처리합니다.
|
||||
/// </summary>
|
||||
private void ProcessShortcuts()
|
||||
{
|
||||
if (_setting == null) return;
|
||||
|
||||
var shortcuts = _setting.Data?.shortcuts;
|
||||
if (shortcuts == null) return;
|
||||
|
||||
// 메뉴 단축키 처리
|
||||
ProcessMenuShortcuts(shortcuts.menu);
|
||||
|
||||
// 도구 단축키 처리 (카메라 Fly 모드가 아닐 때만)
|
||||
if (RTCamera.get == null || RTCamera.get.navigationMode == ECameraNavigationMode.None)
|
||||
{
|
||||
ProcessToolShortcuts(shortcuts.tools);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 메뉴 단축키를 처리합니다.
|
||||
/// </summary>
|
||||
private void ProcessMenuShortcuts(MenuShortcuts? menu)
|
||||
{
|
||||
if (menu == null) return;
|
||||
|
||||
TryExecuteShortcut("newProject", menu.newProject?.key, _menuShortcuts);
|
||||
TryExecuteShortcut("openProject", menu.openProject?.key, _menuShortcuts);
|
||||
TryExecuteShortcut("saveProject", menu.saveProject?.key, _menuShortcuts);
|
||||
TryExecuteShortcut("saveAsProject", menu.saveAsProject?.key, _menuShortcuts);
|
||||
TryExecuteShortcut("insertDb", menu.insertDb?.key, _menuShortcuts);
|
||||
TryExecuteShortcut("exportLayout", menu.exportLayout?.key, _menuShortcuts);
|
||||
TryExecuteShortcut("exportMeta", menu.exportMeta?.key, _menuShortcuts);
|
||||
TryExecuteShortcut("exportGltf", menu.exportGltf?.key, _menuShortcuts);
|
||||
TryExecuteShortcut("undo", menu.undo?.key, _menuShortcuts);
|
||||
TryExecuteShortcut("redo", menu.redo?.key, _menuShortcuts);
|
||||
TryExecuteShortcut("duplicate", menu.duplicate?.key, _menuShortcuts);
|
||||
TryExecuteShortcut("delete", menu.delete?.key, _menuShortcuts);
|
||||
TryExecuteShortcut("createPlane", menu.createPlane?.key, _menuShortcuts);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 도구 단축키를 처리합니다.
|
||||
/// </summary>
|
||||
private void ProcessToolShortcuts(ToolShortcuts? tools)
|
||||
{
|
||||
if (tools == null) return;
|
||||
|
||||
TryExecuteShortcut("select", tools.select?.key, _toolShortcuts);
|
||||
TryExecuteShortcut("move", tools.move?.key, _toolShortcuts);
|
||||
TryExecuteShortcut("rotate", tools.rotate?.key, _toolShortcuts);
|
||||
TryExecuteShortcut("scale", tools.scale?.key, _toolShortcuts);
|
||||
TryExecuteShortcut("snap", tools.snap?.key, _toolShortcuts);
|
||||
TryExecuteShortcut("guide", tools.guide?.key, _toolShortcuts);
|
||||
TryExecuteShortcut("node", tools.node?.key, _toolShortcuts);
|
||||
TryExecuteShortcut("link", tools.link?.key, _toolShortcuts);
|
||||
TryExecuteShortcut("arc", tools.arc?.key, _toolShortcuts);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 단축키가 눌렸는지 확인하고 액션을 실행합니다.
|
||||
/// </summary>
|
||||
private void TryExecuteShortcut(string shortcutId, string? keyCombo, Dictionary<string, ShortcutEntry> shortcuts)
|
||||
{
|
||||
if (string.IsNullOrEmpty(keyCombo)) return;
|
||||
if (!shortcuts.TryGetValue(shortcutId, out var entry)) return;
|
||||
if (!IsShortcutEnabled(shortcutId)) return;
|
||||
|
||||
if (IsKeyComboPressed(keyCombo))
|
||||
{
|
||||
entry.Action?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 키 조합이 눌렸는지 확인합니다.
|
||||
/// </summary>
|
||||
/// <param name="keyCombo">"Ctrl+S", "Ctrl+Shift+Z", "W" 형식의 키 조합</param>
|
||||
private bool IsKeyComboPressed(string keyCombo)
|
||||
{
|
||||
var binding = ParseKeyCombo(keyCombo);
|
||||
return IsBindingActive(binding);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 키 조합 문자열을 파싱합니다.
|
||||
/// </summary>
|
||||
private ShortcutBinding ParseKeyCombo(string keyCombo)
|
||||
{
|
||||
var binding = new ShortcutBinding();
|
||||
|
||||
if (string.IsNullOrEmpty(keyCombo)) return binding;
|
||||
|
||||
// 공백 제거 및 '+' 로 분리
|
||||
var parts = keyCombo.Replace(" ", "").Split('+');
|
||||
|
||||
foreach (var part in parts)
|
||||
{
|
||||
var p = part.ToLower();
|
||||
|
||||
switch (p)
|
||||
{
|
||||
case "ctrl":
|
||||
case "control":
|
||||
binding.NeedCtrl = true;
|
||||
break;
|
||||
case "shift":
|
||||
binding.NeedShift = true;
|
||||
break;
|
||||
case "alt":
|
||||
binding.NeedAlt = true;
|
||||
break;
|
||||
case "cmd":
|
||||
case "command":
|
||||
binding.NeedCmd = true;
|
||||
break;
|
||||
default:
|
||||
// 메인 키 파싱
|
||||
if (KeyEx.KeyFromText(p, out Key key))
|
||||
{
|
||||
binding.MainKey = key;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 특수 키 처리
|
||||
binding.MainKey = ParseSpecialKey(p);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 특수 키를 파싱합니다.
|
||||
/// </summary>
|
||||
private Key ParseSpecialKey(string keyText)
|
||||
{
|
||||
return keyText.ToLower() switch
|
||||
{
|
||||
"delete" => Key.Delete,
|
||||
"backspace" => Key.Backspace,
|
||||
"enter" => Key.Enter,
|
||||
"return" => Key.Enter,
|
||||
"escape" => Key.Escape,
|
||||
"esc" => Key.Escape,
|
||||
"space" => Key.Space,
|
||||
"tab" => Key.Tab,
|
||||
"up" => Key.UpArrow,
|
||||
"down" => Key.DownArrow,
|
||||
"left" => Key.LeftArrow,
|
||||
"right" => Key.RightArrow,
|
||||
"f1" => Key.F1,
|
||||
"f2" => Key.F2,
|
||||
"f3" => Key.F3,
|
||||
"f4" => Key.F4,
|
||||
"f5" => Key.F5,
|
||||
"f6" => Key.F6,
|
||||
"f7" => Key.F7,
|
||||
"f8" => Key.F8,
|
||||
"f9" => Key.F9,
|
||||
"f10" => Key.F10,
|
||||
"f11" => Key.F11,
|
||||
"f12" => Key.F12,
|
||||
_ => Key.None
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 바인딩이 활성화되었는지 확인합니다.
|
||||
/// </summary>
|
||||
private bool IsBindingActive(ShortcutBinding binding)
|
||||
{
|
||||
var rtInput = RTInput.get;
|
||||
if (rtInput == null) return false;
|
||||
|
||||
// 수정자 키 확인
|
||||
if (binding.NeedCtrl != rtInput.ctrlPressed) return false;
|
||||
if (binding.NeedShift != rtInput.shiftPressed) return false;
|
||||
if (binding.NeedAlt != rtInput.altPresed) return false;
|
||||
if (binding.NeedCmd != rtInput.cmdPressed) return false;
|
||||
|
||||
// 메인 키 확인 (KeyWentDown으로 단발 입력만 감지)
|
||||
if (binding.MainKey != Key.None && !rtInput.KeyWentDown(binding.MainKey))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 입력 필드에 포커스가 있는지 확인합니다.
|
||||
/// </summary>
|
||||
private bool IsInputFieldFocused()
|
||||
{
|
||||
// Unity EventSystem에서 현재 선택된 객체 확인
|
||||
var eventSystem = UnityEngine.EventSystems.EventSystem.current;
|
||||
if (eventSystem == null) return false;
|
||||
|
||||
var selected = eventSystem.currentSelectedGameObject;
|
||||
if (selected == null) return false;
|
||||
|
||||
// TMP_InputField 또는 InputField 컴포넌트 확인
|
||||
var tmpInput = selected.GetComponent<TMPro.TMP_InputField>();
|
||||
if (tmpInput != null && tmpInput.isFocused) return true;
|
||||
|
||||
var legacyInput = selected.GetComponent<UnityEngine.UI.InputField>();
|
||||
if (legacyInput != null && legacyInput.isFocused) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Nested Types
|
||||
/// <summary>
|
||||
/// 단축키 바인딩 정보
|
||||
/// </summary>
|
||||
private struct ShortcutBinding
|
||||
{
|
||||
public bool NeedCtrl;
|
||||
public bool NeedShift;
|
||||
public bool NeedAlt;
|
||||
public bool NeedCmd;
|
||||
public Key MainKey;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 단축키 엔트리
|
||||
/// </summary>
|
||||
private class ShortcutEntry
|
||||
{
|
||||
public Action? Action { get; }
|
||||
|
||||
public ShortcutEntry(Action action)
|
||||
{
|
||||
Action = action;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9bd93a337d873344bbb0fe708dd4df34
|
||||
@@ -242,6 +242,12 @@ namespace UVC.Studio.Modal.Settings
|
||||
{
|
||||
await setting.SaveAsync();
|
||||
Debug.Log("Shortcut settings saved.");
|
||||
|
||||
// TopMenu 단축키 갱신
|
||||
if (StudioSceneMain.Instance != null)
|
||||
{
|
||||
StudioSceneMain.Instance.RefreshMenuShortcuts();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using RTGLite;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UVC.Core;
|
||||
using UVC.Data;
|
||||
using UVC.Data.Core;
|
||||
using UVC.Locale;
|
||||
using UVC.Studio.Command;
|
||||
using UVC.Studio.Config;
|
||||
using UVC.Studio.Manager;
|
||||
using UVC.Studio.Modal.Settings;
|
||||
using UVC.UI.Commands;
|
||||
using UVC.UI.Loading;
|
||||
using UVC.UI.Menu;
|
||||
using UVC.UI.Modal;
|
||||
using UVC.UI.Toolbar;
|
||||
using UVC.UI.Toolbar.Model;
|
||||
using UVC.UI.ToolBar;
|
||||
@@ -39,11 +38,14 @@ namespace UVC.Studio
|
||||
[Inject]
|
||||
private PropertyWindow propertyWindow;
|
||||
|
||||
//test code
|
||||
MoveGizmo mMoveGizmo; // 이동 기즈모
|
||||
RotateGizmo mRotateGizmo; // 회전 기즈모
|
||||
ScaleGizmo mScaleGizmo; // 크기 조절 기즈모
|
||||
[Inject]
|
||||
private Setting setting;
|
||||
|
||||
[Inject]
|
||||
private SelectionManager selectionManager;
|
||||
|
||||
// 단축키 관리자
|
||||
private StudioShortcutManager? shortcutManager;
|
||||
|
||||
public Action Initialized;
|
||||
|
||||
@@ -73,65 +75,42 @@ namespace UVC.Studio
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
|
||||
SetupTopMenu();
|
||||
SetupToolBox();
|
||||
SetupShortcutManager();
|
||||
sideTabBar.InitTab();
|
||||
|
||||
// SelectionManager 기즈모 초기화
|
||||
if (selectionManager != null)
|
||||
{
|
||||
selectionManager.InitializeGizmos();
|
||||
}
|
||||
|
||||
Initialized?.Invoke();
|
||||
|
||||
UILoading.Hide();
|
||||
|
||||
|
||||
// 오브젝트를 이동, 회전, 크기 조절할 수 있는 변환 기즈모를 생성합니다
|
||||
mMoveGizmo = RTGizmos.get.CreateObjectMoveGizmo();
|
||||
mRotateGizmo = RTGizmos.get.CreateObjectRotateGizmo();
|
||||
mScaleGizmo = RTGizmos.get.CreateObjectScaleGizmo();
|
||||
// 이 간단한 데모에서는 단일 타겟 오브젝트가 있으며 모든 기즈모가 이 오브젝트를 제어합니다
|
||||
GameObject targetObject = GameObject.Find("Cube");
|
||||
mMoveGizmo.objectTransformGizmo.SetTarget(targetObject);
|
||||
mRotateGizmo.objectTransformGizmo.SetTarget(targetObject);
|
||||
mScaleGizmo.objectTransformGizmo.SetTarget(targetObject);
|
||||
|
||||
// 기본적으로 이동 기즈모를 제외한 모든 기즈모를 비활성화합니다. 그런 다음 단축키로 기즈모를 켜고 끌 수 있습니다.
|
||||
mRotateGizmo.enabled = false;
|
||||
mScaleGizmo.enabled = false;
|
||||
}
|
||||
|
||||
void Update()
|
||||
private void Update()
|
||||
{
|
||||
// 단축키를 사용하여 활성 기즈모를 변경합니다.
|
||||
// 참고: 이러한 키 중 일부는 카메라 탐색 키와 충돌할 수 있으므로,
|
||||
// 카메라가 현재 탐색 중이 아닌 경우에만 진행합니다.
|
||||
if (RTCamera.get.navigationMode == ECameraNavigationMode.None)
|
||||
// 마우스 왼쪽 버튼 클릭 시 객체 선택 처리
|
||||
if (UnityEngine.Input.GetMouseButtonDown(0))
|
||||
{
|
||||
if (RTInput.get.KeyWentDown(Key.W)) // 이동 기즈모 활성화
|
||||
// UI 위에서 클릭한 경우 무시
|
||||
if (UnityEngine.EventSystems.EventSystem.current != null &&
|
||||
UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject())
|
||||
{
|
||||
mMoveGizmo.enabled = true;
|
||||
mRotateGizmo.enabled = false;
|
||||
mScaleGizmo.enabled = false;
|
||||
mMoveGizmo.objectTransformGizmo.Refresh();
|
||||
}
|
||||
else
|
||||
if (RTInput.get.KeyWentDown(Key.E)) // 회전 기즈모 활성화
|
||||
{
|
||||
mMoveGizmo.enabled = false;
|
||||
mRotateGizmo.enabled = true;
|
||||
mScaleGizmo.enabled = false;
|
||||
mRotateGizmo.objectTransformGizmo.Refresh();
|
||||
}
|
||||
else
|
||||
if (RTInput.get.KeyWentDown(Key.R)) // 크기 조절 기즈모 활성화
|
||||
{
|
||||
mMoveGizmo.enabled = false;
|
||||
mRotateGizmo.enabled = false;
|
||||
mScaleGizmo.enabled = true;
|
||||
mScaleGizmo.objectTransformGizmo.Refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl 키가 눌려있으면 다중 선택
|
||||
bool addToSelection = UnityEngine.Input.GetKey(KeyCode.LeftControl) ||
|
||||
UnityEngine.Input.GetKey(KeyCode.RightControl);
|
||||
|
||||
selectionManager?.HandleMouseSelection(addToSelection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void SetupTopMenu()
|
||||
{
|
||||
if (topMenu == null)
|
||||
@@ -140,30 +119,47 @@ namespace UVC.Studio
|
||||
return;
|
||||
}
|
||||
|
||||
// Setting에서 단축키 정보 가져오기
|
||||
// Scene Injection이 실패할 경우 App 레벨에서 직접 가져오기
|
||||
Setting currentSetting = setting;
|
||||
if (currentSetting == null && InjectorAppContext.Instance != null)
|
||||
{
|
||||
currentSetting = InjectorAppContext.Instance.Get<Setting>();
|
||||
}
|
||||
MenuShortcuts menuShortcuts = currentSetting?.Data?.shortcuts?.menu;
|
||||
|
||||
if (menuShortcuts == null)
|
||||
{
|
||||
Debug.LogWarning("[SetupTopMenu] Setting 또는 shortcuts.menu가 null입니다. 단축키가 표시되지 않습니다.");
|
||||
}
|
||||
|
||||
// File 메뉴
|
||||
topMenu.AddMenuItem(new MenuItemData("file", "menu_file", subMenuItems: new List<MenuItemData>
|
||||
{
|
||||
new MenuItemData("file_new", "menu_file_new", new DebugLogCommand("새 파일 선택됨 (Command 실행)")),
|
||||
new MenuItemData("file_open", "menu_file_open", new DebugLogCommand("파일 열기 선택됨 (Command 실행)")),
|
||||
new MenuItemData("file_save", "menu_file_save", new DebugLogCommand("파일 저장 선택됨 (Command 실행)")),
|
||||
new MenuItemData("file_saveas", "menu_file_save_as", new DebugLogCommand("다른 이름으로 저장 선택됨 (Command 실행)")),
|
||||
new MenuItemData("file_insert_db", "menu_file_insert_db", new DebugLogCommand("데이터베이스 삽입 선택됨 (Command 실행)")),
|
||||
new MenuItemData("file_new", "menu_file_new", new FileNewCommand(), shortcut: menuShortcuts?.newProject?.key),
|
||||
new MenuItemData("file_open", "menu_file_open", new FileOpenCommand(), shortcut: menuShortcuts?.openProject?.key),
|
||||
new MenuItemData("file_save", "menu_file_save", new FileSaveCommand(), shortcut: menuShortcuts?.saveProject?.key),
|
||||
new MenuItemData("file_saveas", "menu_file_save_as", new FileSaveAsCommand(), shortcut: menuShortcuts?.saveAsProject?.key),
|
||||
new MenuItemData("file_insert_db", "menu_file_insert_db", new FileInsertDbCommand(), shortcut: menuShortcuts?.insertDb?.key),
|
||||
new MenuItemData("file_export", "menu_file_export", subMenuItems: new List<MenuItemData>
|
||||
{
|
||||
new MenuItemData("file_export_layout", "menu_file_export_layout", new DebugLogCommand("Layout Json 내보내기 선택됨 (Command 실행)")),
|
||||
new MenuItemData("file_export_meta", "menu_file_export_meta", new DebugLogCommand("Meta Data Json 내보내기 선택됨 (Command 실행)")),
|
||||
new MenuItemData("file_export_gltf", "menu_file_export_gltf", new DebugLogCommand("GLTF 내보내기 선택됨 (Command 실행)")),
|
||||
new MenuItemData("file_export_layout", "menu_file_export_layout", new FileExportLayoutCommand(), shortcut: menuShortcuts?.exportLayout?.key),
|
||||
new MenuItemData("file_export_meta", "menu_file_export_meta", new FileExportMetaCommand(), shortcut: menuShortcuts?.exportMeta?.key),
|
||||
new MenuItemData("file_export_gltf", "menu_file_export_gltf", new FileExportGltfCommand(), shortcut: menuShortcuts?.exportGltf?.key),
|
||||
}),
|
||||
}));
|
||||
|
||||
// pool 로그
|
||||
|
||||
// Edit 메뉴
|
||||
topMenu.AddMenuItem(new MenuItemData("edit", "menu_edit", subMenuItems: new List<MenuItemData>
|
||||
{
|
||||
new MenuItemData("edit_undo", "menu_edit_undo", new DebugLogCommand("되돌리기 선택됨 (Command 실행)")),
|
||||
new MenuItemData("edit_redo", "menu_edit_redo", new DebugLogCommand("다시 실행 선택됨 (Command 실행)")),
|
||||
new MenuItemData("edit_duplicate", "menu_edit_duplicate", new DebugLogCommand("복제 선택됨 (Command 실행)")),
|
||||
new MenuItemData("edit_delete", "menu_edit_delete", new DebugLogCommand("삭제 선택됨 (Command 실행)")),
|
||||
new MenuItemData("edit_create_plane", "menu_edit_create_plane", new DebugLogCommand("평면 생성 선택됨 (Command 실행)")),
|
||||
new MenuItemData("edit_undo", "menu_edit_undo", new EditUndoCommand(), shortcut: menuShortcuts?.undo?.key),
|
||||
new MenuItemData("edit_redo", "menu_edit_redo", new EditRedoCommand(), shortcut: menuShortcuts?.redo?.key),
|
||||
new MenuItemData("edit_duplicate", "menu_edit_duplicate", new EditDuplicateCommand(), shortcut: menuShortcuts?.duplicate?.key),
|
||||
new MenuItemData("edit_delete", "menu_edit_delete", new EditDeleteCommand(), shortcut: menuShortcuts?.delete?.key),
|
||||
new MenuItemData("edit_create_plane", "menu_edit_create_plane", new EditCreatePlaneCommand(), shortcut: menuShortcuts?.createPlane?.key),
|
||||
}));
|
||||
|
||||
// Setting 메뉴
|
||||
topMenu.AddMenuItem(new MenuItemData("setting", "menu_setting", subMenuItems: new List<MenuItemData>
|
||||
{
|
||||
new MenuItemData("setting_db", "menu_setting_db", new SettingOpenCommand("shortcut:Database")),
|
||||
@@ -171,10 +167,173 @@ namespace UVC.Studio
|
||||
new MenuItemData("setting_library", "menu_setting_library", new SettingOpenCommand("shortcut:Library")),
|
||||
new MenuItemData("setting_shortcut", "menu_setting_shortcut", new SettingOpenCommand("shortcut:Shortcut")),
|
||||
}));
|
||||
|
||||
|
||||
topMenu.Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setting에서 단축키가 변경된 후 TopMenu의 단축키를 갱신합니다.
|
||||
/// </summary>
|
||||
public void RefreshMenuShortcuts()
|
||||
{
|
||||
if (topMenu == null) return;
|
||||
|
||||
Setting currentSetting = setting;
|
||||
if (currentSetting == null && InjectorAppContext.Instance != null)
|
||||
{
|
||||
currentSetting = InjectorAppContext.Instance.Get<Setting>();
|
||||
}
|
||||
MenuShortcuts menuShortcuts = currentSetting?.Data?.shortcuts?.menu;
|
||||
|
||||
if (menuShortcuts == null)
|
||||
{
|
||||
Debug.LogWarning("[RefreshMenuShortcuts] Setting 또는 shortcuts.menu가 null입니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// File 메뉴 단축키 업데이트
|
||||
topMenu.SetMenuItemShortcut("file_new", menuShortcuts.newProject?.key);
|
||||
topMenu.SetMenuItemShortcut("file_open", menuShortcuts.openProject?.key);
|
||||
topMenu.SetMenuItemShortcut("file_save", menuShortcuts.saveProject?.key);
|
||||
topMenu.SetMenuItemShortcut("file_saveas", menuShortcuts.saveAsProject?.key);
|
||||
topMenu.SetMenuItemShortcut("file_insert_db", menuShortcuts.insertDb?.key);
|
||||
topMenu.SetMenuItemShortcut("file_export_layout", menuShortcuts.exportLayout?.key);
|
||||
topMenu.SetMenuItemShortcut("file_export_meta", menuShortcuts.exportMeta?.key);
|
||||
topMenu.SetMenuItemShortcut("file_export_gltf", menuShortcuts.exportGltf?.key);
|
||||
|
||||
// Edit 메뉴 단축키 업데이트
|
||||
topMenu.SetMenuItemShortcut("edit_undo", menuShortcuts.undo?.key);
|
||||
topMenu.SetMenuItemShortcut("edit_redo", menuShortcuts.redo?.key);
|
||||
topMenu.SetMenuItemShortcut("edit_duplicate", menuShortcuts.duplicate?.key);
|
||||
topMenu.SetMenuItemShortcut("edit_delete", menuShortcuts.delete?.key);
|
||||
topMenu.SetMenuItemShortcut("edit_create_plane", menuShortcuts.createPlane?.key);
|
||||
|
||||
// ShortcutManager 갱신
|
||||
shortcutManager?.RefreshShortcuts();
|
||||
|
||||
Debug.Log("[RefreshMenuShortcuts] TopMenu 단축키가 갱신되었습니다.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 단축키 관리자를 설정합니다.
|
||||
/// </summary>
|
||||
private void SetupShortcutManager()
|
||||
{
|
||||
// StudioShortcutManager 생성 또는 가져오기
|
||||
shortcutManager = StudioShortcutManager.Instance;
|
||||
if (shortcutManager == null)
|
||||
{
|
||||
var go = new GameObject("StudioShortcutManager");
|
||||
shortcutManager = go.AddComponent<StudioShortcutManager>();
|
||||
DontDestroyOnLoad(go);
|
||||
}
|
||||
|
||||
// Setting 주입
|
||||
Setting currentSetting = setting;
|
||||
if (currentSetting == null && InjectorAppContext.Instance != null)
|
||||
{
|
||||
currentSetting = InjectorAppContext.Instance.Get<Setting>();
|
||||
}
|
||||
if (currentSetting != null)
|
||||
{
|
||||
shortcutManager.SetSetting(currentSetting);
|
||||
}
|
||||
|
||||
// 메뉴 단축키 등록
|
||||
RegisterMenuShortcuts();
|
||||
|
||||
// 도구 단축키 등록
|
||||
RegisterToolShortcuts();
|
||||
|
||||
Debug.Log("[SetupShortcutManager] 단축키 관리자가 설정되었습니다.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 메뉴 단축키를 등록합니다.
|
||||
/// </summary>
|
||||
private void RegisterMenuShortcuts()
|
||||
{
|
||||
if (shortcutManager == null) return;
|
||||
|
||||
// File 메뉴
|
||||
shortcutManager.RegisterMenuShortcut("newProject", new FileNewCommand());
|
||||
shortcutManager.RegisterMenuShortcut("openProject", new FileOpenCommand());
|
||||
shortcutManager.RegisterMenuShortcut("saveProject", new FileSaveCommand());
|
||||
shortcutManager.RegisterMenuShortcut("saveAsProject", new FileSaveAsCommand());
|
||||
shortcutManager.RegisterMenuShortcut("insertDb", new FileInsertDbCommand());
|
||||
shortcutManager.RegisterMenuShortcut("exportLayout", new FileExportLayoutCommand());
|
||||
shortcutManager.RegisterMenuShortcut("exportMeta", new FileExportMetaCommand());
|
||||
shortcutManager.RegisterMenuShortcut("exportGltf", new FileExportGltfCommand());
|
||||
|
||||
// Edit 메뉴
|
||||
shortcutManager.RegisterMenuShortcut("undo", new EditUndoCommand());
|
||||
shortcutManager.RegisterMenuShortcut("redo", new EditRedoCommand());
|
||||
shortcutManager.RegisterMenuShortcut("duplicate", new EditDuplicateCommand());
|
||||
shortcutManager.RegisterMenuShortcut("delete", new EditDeleteCommand());
|
||||
shortcutManager.RegisterMenuShortcut("createPlane", new EditCreatePlaneCommand());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 도구 단축키를 등록합니다.
|
||||
/// </summary>
|
||||
private void RegisterToolShortcuts()
|
||||
{
|
||||
if (shortcutManager == null) return;
|
||||
|
||||
// 도구 단축키 - SelectionManager를 통해 기즈모 제어
|
||||
shortcutManager.RegisterToolShortcut("select", () =>
|
||||
{
|
||||
if (selectionManager != null) selectionManager.SetActiveTool(TransformToolType.Select);
|
||||
if (toolBox != null) toolBox.SetRadioButtonSelection("SizeControlGroup", "Selection Tool");
|
||||
});
|
||||
|
||||
shortcutManager.RegisterToolShortcut("move", () =>
|
||||
{
|
||||
if (selectionManager != null) selectionManager.SetActiveTool(TransformToolType.Move);
|
||||
if (toolBox != null) toolBox.SetRadioButtonSelection("SizeControlGroup", "Movement Tool");
|
||||
});
|
||||
|
||||
shortcutManager.RegisterToolShortcut("rotate", () =>
|
||||
{
|
||||
if (selectionManager != null) selectionManager.SetActiveTool(TransformToolType.Rotate);
|
||||
if (toolBox != null) toolBox.SetRadioButtonSelection("SizeControlGroup", "Rotation Tool");
|
||||
});
|
||||
|
||||
shortcutManager.RegisterToolShortcut("scale", () =>
|
||||
{
|
||||
if (selectionManager != null) selectionManager.SetActiveTool(TransformToolType.Scale);
|
||||
if (toolBox != null) toolBox.SetRadioButtonSelection("SizeControlGroup", "Scale Tool");
|
||||
});
|
||||
|
||||
shortcutManager.RegisterToolShortcut("snap", () =>
|
||||
{
|
||||
Debug.Log("[Shortcut] Snap Tool 토글");
|
||||
});
|
||||
|
||||
shortcutManager.RegisterToolShortcut("guide", () =>
|
||||
{
|
||||
Debug.Log("[Shortcut] Guide Tool 토글");
|
||||
});
|
||||
|
||||
shortcutManager.RegisterToolShortcut("node", () =>
|
||||
{
|
||||
if (toolBox != null) toolBox.SetRadioButtonSelection("NodeControlGroup", "Node Tool");
|
||||
CursorManager.Instance.SetCursor(CursorType.Node);
|
||||
});
|
||||
|
||||
shortcutManager.RegisterToolShortcut("link", () =>
|
||||
{
|
||||
if (toolBox != null) toolBox.SetRadioButtonSelection("NodeControlGroup", "Link Tool");
|
||||
CursorManager.Instance.SetCursor(CursorType.Link);
|
||||
});
|
||||
|
||||
shortcutManager.RegisterToolShortcut("arc", () =>
|
||||
{
|
||||
if (toolBox != null) toolBox.SetRadioButtonSelection("NodeControlGroup", "Arc Tool");
|
||||
CursorManager.Instance.SetCursor(CursorType.Arc);
|
||||
});
|
||||
}
|
||||
|
||||
private void SetupToolBox()
|
||||
{
|
||||
// ToolbarModel 인스턴스 생성
|
||||
|
||||
@@ -63,19 +63,29 @@ namespace UVC.Studio.Tab
|
||||
/// <summary>
|
||||
/// HierarchyWindow에서 아이템이 선택되었을 때 호출
|
||||
/// SelectionManager를 통해 객체를 선택하고 Outlinable을 활성화
|
||||
/// 자식 항목 선택 시 해당 Transform의 속성을 PropertyWindow에 표시
|
||||
/// </summary>
|
||||
private void OnItemSelectedHandler(TreeListItemData item)
|
||||
{
|
||||
Debug.Log($"[StudioSideTabBarHierarchy] Item selected: {item.Name} (ExternalKey: {item.ExternalKey})");
|
||||
|
||||
if (_isProcessingSelection) return;
|
||||
if (_selectionManager == null || string.IsNullOrEmpty(item.ExternalKey)) return;
|
||||
if (_selectionManager == null) return;
|
||||
|
||||
_isProcessingSelection = true;
|
||||
try
|
||||
{
|
||||
// SelectionManager를 통해 객체 선택 (Outlinable 활성화됨)
|
||||
_selectionManager.SelectById(item.ExternalKey);
|
||||
// ExternalKey가 있으면 루트 StageObject 선택
|
||||
if (!string.IsNullOrEmpty(item.ExternalKey))
|
||||
{
|
||||
// SelectionManager를 통해 객체 선택 (Outlinable 활성화됨)
|
||||
_selectionManager.SelectById(item.ExternalKey);
|
||||
}
|
||||
// ExternalKey가 없으면 자식 항목 - Tag에서 Transform 가져와서 PropertyWindow에 표시
|
||||
else if (item.Tag is Transform childTransform)
|
||||
{
|
||||
_selectionManager.DisplayChildTransformProperties(childTransform, item.Name);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -92,13 +102,22 @@ namespace UVC.Studio.Tab
|
||||
Debug.Log($"[StudioSideTabBarHierarchy] Item deselected: {item.Name} (ExternalKey: {item.ExternalKey})");
|
||||
|
||||
if (_isProcessingSelection) return;
|
||||
if (_selectionManager == null || string.IsNullOrEmpty(item.ExternalKey)) return;
|
||||
if (_selectionManager == null) return;
|
||||
|
||||
_isProcessingSelection = true;
|
||||
try
|
||||
{
|
||||
// SelectionManager를 통해 객체 선택 해제 (Outlinable 비활성화됨)
|
||||
_selectionManager.DeselectById(item.ExternalKey);
|
||||
// ExternalKey가 있으면 루트 StageObject 선택 해제
|
||||
if (!string.IsNullOrEmpty(item.ExternalKey))
|
||||
{
|
||||
// SelectionManager를 통해 객체 선택 해제 (Outlinable 비활성화됨)
|
||||
_selectionManager.DeselectById(item.ExternalKey);
|
||||
}
|
||||
// ExternalKey가 없으면 자식 항목 - 자식 Transform 표시 상태 초기화
|
||||
else if (item.Tag is Transform)
|
||||
{
|
||||
_selectionManager.ClearChildTransformDisplay();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -254,6 +273,9 @@ namespace UVC.Studio.Tab
|
||||
treeItem.ExternalKey = externalKey;
|
||||
}
|
||||
|
||||
// Transform을 Tag에 저장 (자식 Transform 접근용)
|
||||
treeItem.Tag = transform;
|
||||
|
||||
// Transform이 있으면 자식들을 재귀적으로 추가
|
||||
if (transform != null)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user