UTKProperyWindow 개발 중
This commit is contained in:
179
CLAUDE.md
179
CLAUDE.md
@@ -11,6 +11,31 @@
|
||||
- **UI Toolkit(UIElements) 필수 사용**. uGUI(Canvas 기반)는 레거시로 취급합니다.
|
||||
- UXML(구조)과 USS(스타일)를 분리하고, C# 코드에서 인라인 스타일 지정을 지양합니다.
|
||||
|
||||
### 이벤트 콜백 등록 규칙
|
||||
**⚠️ 중요: `RegisterValueChangedCallback` 대신 `RegisterCallback<ChangeEvent<T>>`를 사용합니다.**
|
||||
|
||||
`RegisterValueChangedCallback`은 확장 메서드로 `UnregisterCallback`과 대칭이 맞지 않아 이벤트 해제가 어렵습니다.
|
||||
|
||||
```csharp
|
||||
// ❌ 잘못된 예: 해제가 어려운 방식
|
||||
field.RegisterValueChangedCallback(OnValueChanged);
|
||||
// field.UnregisterValueChangedCallback(OnValueChanged); // 이런 메서드 없음!
|
||||
|
||||
// ✅ 올바른 예: 대칭적인 등록/해제
|
||||
field.RegisterCallback<ChangeEvent<float>>(OnValueChanged);
|
||||
field.UnregisterCallback<ChangeEvent<float>>(OnValueChanged);
|
||||
|
||||
private void OnValueChanged(ChangeEvent<float> evt)
|
||||
{
|
||||
// evt.newValue, evt.previousValue 사용
|
||||
}
|
||||
```
|
||||
|
||||
| 메서드 | 권장 | 이유 |
|
||||
|--------|------|------|
|
||||
| `RegisterValueChangedCallback` | ❌ | 해제용 메서드 없음 |
|
||||
| `RegisterCallback<ChangeEvent<T>>` | ✅ | `UnregisterCallback` 대칭 |
|
||||
|
||||
### 커스텀 VisualElement (Unity 6)
|
||||
Unity 6에서는 **레거시 `UxmlFactory`/`UxmlTraits` 방식을 사용하지 않고**, 소스 생성기 기반의 `[UxmlElement]`와 `[UxmlAttribute]`를 사용합니다.
|
||||
|
||||
@@ -369,45 +394,149 @@ public async UniTask<UserData?> LoadUserAsync(string userId, CancellationToken c
|
||||
|
||||
---
|
||||
|
||||
## 11) View 기본 패턴
|
||||
## 11) UTK 컴포넌트 기본 패턴
|
||||
|
||||
UTK 컴포넌트는 다음 패턴을 따릅니다. `UTKHelpBox`를 예시로 설명합니다.
|
||||
|
||||
```csharp
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
/// <summary>
|
||||
/// UI Toolkit View 기본 구조.
|
||||
/// </summary>
|
||||
public abstract class UIViewBase : IDisposable
|
||||
namespace UVC.UIToolkit
|
||||
{
|
||||
protected VisualElement Root { get; private set; } = null!;
|
||||
protected CancellationTokenSource? Cts { get; private set; }
|
||||
|
||||
public virtual void Initialize(VisualElement root)
|
||||
/// <summary>
|
||||
/// 컴포넌트 설명.
|
||||
/// </summary>
|
||||
[UxmlElement]
|
||||
public partial class UTKExample : VisualElement, IDisposable
|
||||
{
|
||||
Root = root ?? throw new ArgumentNullException(nameof(root));
|
||||
Cts = new CancellationTokenSource();
|
||||
QueryElements();
|
||||
RegisterEvents();
|
||||
}
|
||||
#region Constants
|
||||
private const string UXML_PATH = "UIToolkit/Common/UTKExample";
|
||||
private const string USS_PATH = "UIToolkit/Common/UTKExampleUss";
|
||||
#endregion
|
||||
|
||||
protected abstract void QueryElements();
|
||||
protected abstract void RegisterEvents();
|
||||
protected abstract void UnregisterEvents();
|
||||
#region Fields
|
||||
private bool _disposed;
|
||||
private Label? _label;
|
||||
private string _text = "";
|
||||
#endregion
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
UnregisterEvents();
|
||||
Cts?.Cancel();
|
||||
Cts?.Dispose();
|
||||
Cts = null;
|
||||
#region Properties
|
||||
/// <summary>텍스트</summary>
|
||||
[UxmlAttribute("text")]
|
||||
public string Text
|
||||
{
|
||||
get => _text;
|
||||
set
|
||||
{
|
||||
_text = value;
|
||||
if (_label != null)
|
||||
_label.text = value;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
public UTKExample() : base()
|
||||
{
|
||||
// 1. 테마 적용
|
||||
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
||||
|
||||
// 2. USS 로드
|
||||
var uss = Resources.Load<StyleSheet>(USS_PATH);
|
||||
if (uss != null)
|
||||
{
|
||||
styleSheets.Add(uss);
|
||||
}
|
||||
|
||||
// 3. UI 생성 (UXML 또는 Fallback)
|
||||
CreateUI();
|
||||
|
||||
// 4. 테마 변경 구독
|
||||
SubscribeToThemeChanges();
|
||||
}
|
||||
|
||||
public UTKExample(string text) : this()
|
||||
{
|
||||
Text = text;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Setup
|
||||
private void CreateUI()
|
||||
{
|
||||
AddToClassList("utk-example");
|
||||
|
||||
var asset = Resources.Load<VisualTreeAsset>(UXML_PATH);
|
||||
if (asset != null)
|
||||
{
|
||||
CreateUIFromUxml(asset);
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateUIFallback();
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateUIFromUxml(VisualTreeAsset asset)
|
||||
{
|
||||
var root = asset.Instantiate();
|
||||
// UXML 요소 참조 가져오기
|
||||
_label = root.Q<Label>("label");
|
||||
Add(root);
|
||||
}
|
||||
|
||||
private void CreateUIFallback()
|
||||
{
|
||||
_label = new Label(_text);
|
||||
_label.AddToClassList("utk-example__label");
|
||||
Add(_label);
|
||||
}
|
||||
|
||||
private void SubscribeToThemeChanges()
|
||||
{
|
||||
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
|
||||
RegisterCallback<DetachFromPanelEvent>(_ =>
|
||||
{
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
});
|
||||
}
|
||||
|
||||
private void OnThemeChanged(UTKTheme theme)
|
||||
{
|
||||
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 주요 패턴 요약
|
||||
|
||||
| 항목 | 설명 |
|
||||
|------|------|
|
||||
| **`[UxmlElement]`** | UXML에서 사용 가능하도록 등록 |
|
||||
| **`partial class`** | 소스 생성기 요구사항 |
|
||||
| **`[UxmlAttribute]`** | UXML 속성 매핑 (케밥 케이스) |
|
||||
| **`IDisposable`** | 리소스 정리 인터페이스 |
|
||||
| **`UTKThemeManager`** | 테마 스타일시트 적용 |
|
||||
| **`DetachFromPanelEvent`** | 패널에서 분리 시 이벤트 해제 |
|
||||
| **UXML 경로** | `Resources.Load<VisualTreeAsset>()` 사용 |
|
||||
| **USS 경로** | `Resources.Load<StyleSheet>()` 사용, 파일명에 `Uss` 접미사 |
|
||||
| **Fallback 패턴** | UXML 로드 실패 시 코드로 UI 생성 |
|
||||
|
||||
---
|
||||
|
||||
## 12) Unity Nullable 주의
|
||||
|
||||
Reference in New Issue
Block a user