선택한 모델 PropertyWindow 연결 완료

This commit is contained in:
logonkhi
2025-12-19 15:27:35 +09:00
parent deeaa9a7ad
commit 158a42ab9b
24 changed files with 2278 additions and 1185 deletions

View File

@@ -59,6 +59,12 @@ namespace UVC.GLTF
return instantiator.SceneTransform != null ? instantiator.SceneTransform.gameObject : null;
}
/// <summary>
/// 여러 LOD 레벨의 glTF/glb 파일을 로드하고 LODGroup으로 설정합니다. 사용않함
/// </summary>
/// <param name="paths"></param>
/// <param name="parentTransform"></param>
/// <returns></returns>
public static async UniTask<GameObject?> ImportWithLOD(List<string> paths, Transform parentTransform)
{
if (paths == null || paths.Count == 0) return null;

View File

@@ -189,6 +189,16 @@ namespace UVC.UI.Window
treeList.DeleteItem(data);
}
/// <summary>
/// 아이템의 이름을 변경합니다.
/// </summary>
/// <param name="data">변경할 아이템 데이터</param>
/// <param name="newName">새 이름</param>
public void SetItemName(TreeListItemData data, string newName)
{
treeList.SetItemName(data, newName);
}
/// <summary>
/// 이름으로 아이템 선택
/// </summary>

View File

@@ -27,6 +27,7 @@ namespace UVC.UI.Window.PropertyWindow
FloatRange,
DateRange,
DateTimeRange,
ColorState,
}
/// <summary>
@@ -331,5 +332,13 @@ namespace UVC.UI.Window.PropertyWindow
}
}
// --- 복합 타입 속성 ---
public class ColorStateProperty : PropertyItem<Tuple<string, Color?>>
{
public override PropertyType PropertyType => PropertyType.ColorState;
public ColorStateProperty(string id, string name, Tuple<string, Color?> initialValue) : base(id, name, initialValue) { }
}
#endregion
}

View File

@@ -35,6 +35,7 @@ namespace UVC.UI.Window.PropertyWindow
[SerializeField] private GameObject _numberRangePropertyPrefab;
[SerializeField] private GameObject _dateRangePropertyPrefab;
[SerializeField] private GameObject _dateTimeRangePropertyPrefab;
[SerializeField] private GameObject _colorStatePropertyPrefab;
/// <summary>
/// View가 상호작용할 Controller 인스턴스입니다.
@@ -291,6 +292,8 @@ namespace UVC.UI.Window.PropertyWindow
return _dateRangePropertyPrefab;
case PropertyType.DateTimeRange:
return _dateTimeRangePropertyPrefab;
case PropertyType.ColorState:
return _colorStatePropertyPrefab;
default:
Debug.LogWarning($"'{type}' 타입에 대한 프리팹이 정의되지 않았습니다.");
return null;

View File

@@ -402,6 +402,9 @@ namespace UVC.UI.Window.PropertyWindow
_groupIndex.Clear();
_itemIndex.Clear();
EntriesCleared?.Invoke(this, EventArgs.Empty);
// View 갱신하여 UI에서도 항목 제거
Refresh();
}
/// <summary>

View File

@@ -0,0 +1,251 @@
using System;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UVC.Core;
using UVC.Extention;
using UVC.Studio.Manager;
using UVC.UI.Modal.ColorPicker;
using UVC.UI.Tooltip;
using UVC.Util;
namespace UVC.UI.Window.PropertyWindow.UI
{
/// <summary>
/// ColorProperty를 위한 UI를 제어하는 스크립트입니다.
/// Image 컴포넌트로 색상을 표시하고, Button으로 색상 선택기를 엽니다.
/// </summary>
[RequireComponent(typeof(LayoutElement))]
public class ColorStatePropertyUI : MonoBehaviour, IPropertyUI
{
[Header("UI Components")]
[SerializeField]
private TextMeshProUGUI _nameLabel; // 속성 이름을 표시할 Text
[SerializeField]
private TextMeshProUGUI _descriptionLabel;
[SerializeField]
private TMP_InputField _stateLabel;
[SerializeField]
private LayoutGroup _colorLayoutGroup;
[SerializeField]
private Image _colorPreviewImage; // 현재 색상을 표시할 Image
[SerializeField]
private TMP_InputField _colorLabel;
[SerializeField]
private Button _colorPickerButton; // 색상 선택기를 열기 위한 Button
[SerializeField]
private Button _previewButton; // 컬러를 미리보기 위한 Button
private ColorStateProperty _propertyItem;
private PropertyWindow _controller;
private bool openningColorPickered = false;
/// <summary>
/// PropertyView에 의해 호출되어 UI를 초기화하고 데이터를 설정합니다.
/// </summary>
/// <param name="item">UI에 표시할 속성 데이터(IPropertyItem)</param>
/// <param name="controller">상호작용할 PropertyWindow</param>
public void Setup(IPropertyItem item, PropertyWindow controller)
{
if (!(item is ColorStateProperty typedItem))
{
Debug.LogError($"ColorStatePropertyUI에 잘못된 타입의 PropertyItem이 전달되었습니다. {item.GetType()}");
return;
}
_propertyItem = typedItem;
_controller = controller;
// --- 데이터 바인딩 ---
// 1. 속성 이름 설정
_nameLabel.text = _propertyItem.Name;
// 툴팁 설정 (TooltipHandler 컴포넌트가 있다면 연동)
TooltipHandler tooltipHandler = _nameLabel.GetComponent<TooltipHandler>();
if (tooltipHandler != null && !_propertyItem.Tooltip.IsNullOrEmpty())
{
tooltipHandler.Tooltip = _propertyItem.Tooltip;
}
if (_propertyItem.Description.IsNullOrEmpty())
{
_descriptionLabel.gameObject.SetActive(false);
}
else
{
_descriptionLabel.gameObject.SetActive(true);
_descriptionLabel.text = _propertyItem.Description;
}
_stateLabel.text = _propertyItem.Value.Item1;
// 2. 색상 미리보기 Image의 색상 설정
if (_propertyItem.Value.Item2 == null)
{
_colorLayoutGroup.gameObject.SetActive(false);
_previewButton.gameObject.SetActive(false);
}
else
{
_colorLayoutGroup.gameObject.SetActive(true);
_colorPreviewImage.color = _propertyItem.Value.Item2.Value;
_colorLabel.text = ColorUtil.ToHex(_colorPreviewImage.color, true, false);
_colorLabel.interactable = !_propertyItem.IsReadOnly;
// 3. 읽기 전용 상태에 따라 버튼 상호작용 여부 결정
_colorPickerButton.gameObject.SetActive(!_propertyItem.IsReadOnly);
_colorPickerButton.onClick.RemoveAllListeners();
if (!_propertyItem.IsReadOnly)
{
// --- 이벤트 리스너 등록 ---
_colorPickerButton.onClick.AddListener(OpenColorPicker);
}
_previewButton.gameObject.SetActive(true);
// PointerDown/PointerUp 핸들러 설정
SetupPreviewButtonEvents();
}
}
/// <summary>
/// PreviewButton에 PointerDown/PointerUp 이벤트를 설정합니다.
/// </summary>
private void SetupPreviewButtonEvents()
{
// 기존 PreviewButtonHandler가 있으면 제거
var existingHandler = _previewButton.GetComponent<PreviewButtonHandler>();
if (existingHandler != null)
{
Destroy(existingHandler);
}
// 새 핸들러 추가
var handler = _previewButton.gameObject.AddComponent<PreviewButtonHandler>();
handler.Initialize(OnPreviewMouseDown, OnPreViewMouseUp);
}
/// <summary>
/// 색상 선택기 버튼을 클릭했을 때 호출됩니다.
/// </summary>
private async void OpenColorPicker()
{
if (openningColorPickered == true) return;
openningColorPickered = true;
CursorManager.Instance.SetCursor(CursorType.Wait);
await ColorPicker.Create(_colorPreviewImage.color, "Color Picker", null, OnColorSelected, OnCloseColorPicker, true);
CursorManager.Instance.SetDefaultCursor();
Debug.LogWarning($"'{_propertyItem.Name}'의 색상 선택기 로직이 구현되지 않았습니다. 클릭 이벤트만 발생합니다.");
}
/// <summary>
/// 색상 선택기에서 새로운 색상이 선택되었을 때 호출되는 메서드입니다.
/// </summary>
/// <param name="newColor">선택된 새로운 색상</param>
public void OnColorSelected(Color newColor)
{
if (newColor == _propertyItem.Value.Item2.Value)
{
return; // 변경 사항이 없으므로 아무 작업도 하지 않음
}
// 1. UI의 색상 미리보기를 업데이트합니다.
_colorPreviewImage.color = newColor;
_colorLabel.text = ColorUtil.ToHex(_colorPreviewImage.color, true, false);
// 2. PropertyController를 통해 데이터 모델의 값을 업데이트합니다.
_controller.UpdatePropertyValue(_propertyItem.Id, _propertyItem.PropertyType, new Tuple<string, string, Color?>(_propertyItem.Name, _stateLabel.text, newColor));
}
private void OnPreviewMouseDown()
{
// 미리보기 버튼 클릭 시 동작 구현 (필요시)
SelectionManager selectionManager = InjectorAppContext.Instance.Get<SelectionManager>();
if (selectionManager != null) selectionManager.PreviewColor(_propertyItem.Value.Item2!.Value);
}
private void OnPreViewMouseUp()
{
// 미리보기 버튼 클릭 해제 시 동작 구현 (필요시)
SelectionManager selectionManager = InjectorAppContext.Instance.Get<SelectionManager>();
if (selectionManager != null) selectionManager.ClearPreviewColor();
}
private void OnCloseColorPicker()
{
openningColorPickered = false;
}
/// <summary>
/// UI의 읽기 전용 상태를 설정합니다.
/// </summary>
/// <param name="isReadOnly">읽기 전용 여부 (true: 비활성화, false: 활성화)</param>
public void SetReadOnly(bool isReadOnly)
{
if (_propertyItem != null)
{
_propertyItem.IsReadOnly = isReadOnly;
}
if (_colorLabel != null) _colorLabel.interactable = !isReadOnly;
if (_colorPickerButton != null) _colorPickerButton.gameObject.SetActive(!isReadOnly);
}
/// <summary>
/// 이 UI 오브젝트가 파괴될 때 Unity에 의해 호출됩니다.
/// </summary>
private void OnDestroy()
{
if (_colorPickerButton != null && _colorPickerButton.onClick != null)
{
_colorPickerButton.onClick.RemoveAllListeners();
}
if (_previewButton != null)
{
var existingHandler = _previewButton.GetComponent<PreviewButtonHandler>();
if (existingHandler != null)
{
Destroy(existingHandler);
}
}
}
}
/// <summary>
/// PreviewButton의 PointerDown/PointerUp 이벤트를 처리하는 핸들러
/// </summary>
public class PreviewButtonHandler : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
private Action _onPointerDown;
private Action _onPointerUp;
public void Initialize(Action onPointerDown, Action onPointerUp)
{
_onPointerDown = onPointerDown;
_onPointerUp = onPointerUp;
}
public void OnPointerDown(PointerEventData eventData)
{
_onPointerDown?.Invoke();
}
public void OnPointerUp(PointerEventData eventData)
{
_onPointerUp?.Invoke();
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 508447e589a88f149934d68ac57195ff