UTKToolBar 개발 완료
This commit is contained in:
713
Assets/Scripts/UVC/UIToolkit/Property/CLAUDE.md
Normal file
713
Assets/Scripts/UVC/UIToolkit/Property/CLAUDE.md
Normal file
@@ -0,0 +1,713 @@
|
||||
# UIToolkit Property System
|
||||
|
||||
UIToolkit 기반의 Property 편집 시스템입니다. 데이터(Item)와 뷰(View)를 분리하는 MVVM 아키텍처를 적용하여, 20가지 속성 타입을 지원합니다.
|
||||
|
||||
## 📋 목차
|
||||
- [주요 기능](#주요-기능)
|
||||
- [아키텍처](#아키텍처)
|
||||
- [파일 구조](#파일-구조)
|
||||
- [빠른 시작](#빠른-시작)
|
||||
- [사용 예제](#사용-예제)
|
||||
- [지원 타입 목록](#지원-타입-목록)
|
||||
- [API 문서](#api-문서)
|
||||
- [메모리 관리](#메모리-관리)
|
||||
- [성능 최적화](#성능-최적화)
|
||||
|
||||
---
|
||||
|
||||
## 주요 기능
|
||||
|
||||
✅ **데이터/뷰 완전 분리 (MVVM)**
|
||||
- Item: 순수 데이터 레이어 (UI 코드 없음)
|
||||
- View: 순수 UI 레이어 (비즈니스 로직 없음)
|
||||
- Bind/Unbind 패턴으로 양방향 데이터 동기화
|
||||
|
||||
✅ **20가지 속성 타입 지원**
|
||||
- 기본: String, Int, Float, Bool, Vector2, Vector3, Color
|
||||
- 날짜: Date, DateTime, DateRange, DateTimeRange
|
||||
- 선택: Dropdown, Enum, Radio, MultiSelectDropdown
|
||||
- 범위: IntRange, FloatRange
|
||||
- 특수: ColorState, FloatDropdown, Button
|
||||
|
||||
✅ **ReadOnly 모드**
|
||||
- 모든 View가 ReadOnly 상태 전환 지원
|
||||
- ReadOnly 시 편집 컨트롤이 읽기 전용 InputField로 전환
|
||||
- 그룹 단위 일괄 ReadOnly 설정 가능
|
||||
|
||||
✅ **그룹(Group) 관리**
|
||||
- 속성 아이템을 그룹으로 묶어 관리
|
||||
- 그룹 접기/펼치기 지원
|
||||
- 아이템 추가/제거 이벤트
|
||||
|
||||
✅ **Factory 패턴**
|
||||
- `UTKPropertyItemViewFactory`로 데이터 타입에 맞는 View 자동 생성
|
||||
- 커스텀 View 등록/교체 가능
|
||||
|
||||
✅ **완벽한 메모리 관리**
|
||||
- 모든 클래스 IDisposable 구현
|
||||
- Bind/Unbind 대칭 이벤트 관리
|
||||
- 정적 UXML/USS 캐싱으로 반복 로드 방지
|
||||
|
||||
---
|
||||
|
||||
## 아키텍처
|
||||
|
||||
```
|
||||
Data Layer (Items) View Layer (Views)
|
||||
───────────────── ──────────────────
|
||||
IUTKPropertyEntry IUTKPropertyItemView
|
||||
├─ IUTKPropertyGroup IUTKPropertyItemView<T>
|
||||
└─ IUTKPropertyItem<T> UTKPropertyItemViewBase (abstract)
|
||||
└─ UTKPropertyItemBase<T> └─ UTK{Type}PropertyItemView
|
||||
└─ UTK{Type}PropertyItem
|
||||
|
||||
Factory Bridge
|
||||
──────────────
|
||||
UTKPropertyItemViewFactory
|
||||
CreateView(IUTKPropertyItem)
|
||||
→ new View
|
||||
→ View.Bind(item)
|
||||
→ return VisualElement
|
||||
```
|
||||
|
||||
### 데이터 흐름
|
||||
|
||||
```
|
||||
1. Item 생성 (데이터)
|
||||
var item = new UTKFloatPropertyItem("speed", "이동 속도", 1.0f);
|
||||
|
||||
2. Factory가 View 생성 + 바인딩
|
||||
var view = UTKPropertyItemViewFactory.CreateView(item);
|
||||
|
||||
3. 양방향 동기화
|
||||
Item.Value 변경 → OnTypedValueChanged → View.RefreshUI()
|
||||
View 사용자 입력 → OnValueChanged → Item.Value 업데이트
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 파일 구조
|
||||
|
||||
```
|
||||
Assets/Scripts/UVC/UIToolkit/Property/
|
||||
├── Core/ # 인터페이스 & 핵심 타입
|
||||
│ ├── IUTKPropertyEntry.cs # 트리 엔트리 베이스 인터페이스
|
||||
│ ├── IUTKPropertyGroup.cs # 그룹 인터페이스
|
||||
│ ├── IUTKPropertyItem.cs # 속성 아이템 인터페이스 (비제네릭/제네릭)
|
||||
│ ├── UTKPropertyGroup.cs # 그룹 구현체
|
||||
│ ├── UTKPropertyType.cs # 속성 타입 열거형 (20종)
|
||||
│ └── UTKPropertyValueChangedEventArgs.cs # 값 변경 이벤트 인자
|
||||
├── Data/ # 복합 데이터 구조체
|
||||
│ ├── UTKColorState.cs # 상태-색상 쌍
|
||||
│ ├── UTKRangeValue.cs # 범위 값 (Int/Float/Date/DateTime)
|
||||
│ └── UTKFloatDropdownValue.cs # Float + Dropdown 복합 값
|
||||
├── Items/ # 데이터 레이어 (Item 클래스)
|
||||
│ ├── Base/
|
||||
│ │ └── UTKPropertyItemBase.cs # Item 추상 베이스 클래스
|
||||
│ ├── UTKStringPropertyItem.cs
|
||||
│ ├── UTKIntPropertyItem.cs
|
||||
│ ├── UTKFloatPropertyItem.cs
|
||||
│ ├── UTKBoolPropertyItem.cs
|
||||
│ ├── UTKVector2PropertyItem.cs
|
||||
│ ├── UTKVector3PropertyItem.cs
|
||||
│ ├── UTKColorPropertyItem.cs
|
||||
│ ├── UTKColorStatePropertyItem.cs
|
||||
│ ├── UTKDatePropertyItem.cs
|
||||
│ ├── UTKDateRangePropertyItem.cs
|
||||
│ ├── UTKDateTimePropertyItem.cs
|
||||
│ ├── UTKDateTimeRangePropertyItem.cs
|
||||
│ ├── UTKDropdownPropertyItem.cs
|
||||
│ ├── UTKEnumPropertyItem.cs
|
||||
│ ├── UTKRadioPropertyItem.cs
|
||||
│ ├── UTKIntRangePropertyItem.cs
|
||||
│ ├── UTKFloatRangePropertyItem.cs
|
||||
│ ├── UTKMultiSelectDropdownPropertyItem.cs
|
||||
│ ├── UTKFloatDropdownPropertyItem.cs
|
||||
│ └── UTKButtonItem.cs
|
||||
└── Views/ # 뷰 레이어 (View 클래스)
|
||||
├── IUTKPropertyItemView.cs # View 인터페이스 (비제네릭/제네릭)
|
||||
├── UTKPropertyItemViewBase.cs # View 추상 베이스 클래스
|
||||
├── UTKPropertyItemViewFactory.cs # View 팩토리 (타입별 자동 생성)
|
||||
├── UTKStringPropertyItemView.cs
|
||||
├── UTKIntPropertyItemView.cs
|
||||
├── UTKFloatPropertyItemView.cs
|
||||
├── UTKBoolPropertyItemView.cs
|
||||
├── UTKVector2PropertyItemView.cs
|
||||
├── UTKVector3PropertyItemView.cs
|
||||
├── UTKColorPropertyItemView.cs
|
||||
├── UTKColorStatePropertyItemView.cs
|
||||
├── UTKDatePropertyItemView.cs
|
||||
├── UTKDateRangePropertyItemView.cs
|
||||
├── UTKDateTimePropertyItemView.cs
|
||||
├── UTKDateTimeRangePropertyItemView.cs
|
||||
├── UTKDropdownPropertyItemView.cs
|
||||
├── UTKEnumPropertyItemView.cs
|
||||
├── UTKRadioPropertyItemView.cs
|
||||
├── UTKIntRangePropertyItemView.cs
|
||||
├── UTKFloatRangePropertyItemView.cs
|
||||
├── UTKMultiSelectDropdownPropertyItemView.cs
|
||||
├── UTKFloatDropdownPropertyItemView.cs
|
||||
├── UTKButtonItemView.cs
|
||||
└── CLAUDE.md
|
||||
|
||||
Assets/Resources/UIToolkit/Property/Views/
|
||||
├── UTKPropertyItemViewCommonUss.uss # 공통 스타일 (모든 View 공유)
|
||||
├── UTKStringPropertyItemView.uxml
|
||||
├── UTKStringPropertyItemViewUss.uss
|
||||
├── UTKIntPropertyItemView.uxml
|
||||
├── UTKIntPropertyItemViewUss.uss
|
||||
├── UTKFloatPropertyItemView.uxml
|
||||
├── UTKFloatPropertyItemViewUss.uss
|
||||
├── UTKBoolPropertyItemView.uxml
|
||||
├── UTKBoolPropertyItemViewUss.uss
|
||||
├── UTKVector2PropertyItemView.uxml
|
||||
├── UTKVector2PropertyItemViewUss.uss
|
||||
├── UTKVector3PropertyItemView.uxml
|
||||
├── UTKVector3PropertyItemViewUss.uss
|
||||
├── UTKColorPropertyItemView.uxml
|
||||
├── UTKColorPropertyItemViewUss.uss
|
||||
├── UTKColorStatePropertyItemView.uxml
|
||||
├── UTKColorStatePropertyItemViewUss.uss
|
||||
├── UTKDatePropertyItemView.uxml
|
||||
├── UTKDatePropertyItemViewUss.uss
|
||||
├── UTKDateRangePropertyItemView.uxml
|
||||
├── UTKDateRangePropertyItemViewUss.uss
|
||||
├── UTKDateTimePropertyItemView.uxml
|
||||
├── UTKDateTimePropertyItemViewUss.uss
|
||||
├── UTKDateTimeRangePropertyItemView.uxml
|
||||
├── UTKDateTimeRangePropertyItemViewUss.uss
|
||||
├── UTKDropdownPropertyItemView.uxml
|
||||
├── UTKDropdownPropertyItemViewUss.uss
|
||||
├── UTKEnumPropertyItemView.uxml
|
||||
├── UTKEnumPropertyItemViewUss.uss
|
||||
├── UTKRadioPropertyItemView.uxml
|
||||
├── UTKRadioPropertyItemViewUss.uss
|
||||
├── UTKIntRangePropertyItemView.uxml
|
||||
├── UTKIntRangePropertyItemViewUss.uss
|
||||
├── UTKFloatRangePropertyItemView.uxml
|
||||
├── UTKFloatRangePropertyItemViewUss.uss
|
||||
├── UTKMultiSelectDropdownPropertyItemView.uxml
|
||||
├── UTKMultiSelectDropdownPropertyItemViewUss.uss
|
||||
├── UTKFloatDropdownPropertyItemView.uxml
|
||||
├── UTKFloatDropdownPropertyItemViewUss.uss
|
||||
├── UTKButtonItemView.uxml
|
||||
└── UTKButtonItemViewUss.uss
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 빠른 시작
|
||||
|
||||
### 1. 단일 속성 아이템 생성
|
||||
|
||||
```csharp
|
||||
// 1. 데이터(Item) 생성
|
||||
var nameItem = new UTKStringPropertyItem("name", "이름", "홍길동");
|
||||
|
||||
// 2. Factory로 View 생성 + 바인딩
|
||||
var nameView = UTKPropertyItemViewFactory.CreateView(nameItem);
|
||||
|
||||
// 3. UI에 추가
|
||||
rootVisualElement.Add(nameView);
|
||||
|
||||
// 4. 값 변경 감지
|
||||
nameItem.OnTypedValueChanged += (item, oldVal, newVal) =>
|
||||
{
|
||||
Debug.Log($"이름 변경: {oldVal} → {newVal}");
|
||||
};
|
||||
```
|
||||
|
||||
### 2. 그룹으로 묶어서 관리
|
||||
|
||||
```csharp
|
||||
// 그룹 생성
|
||||
var transformGroup = new UTKPropertyGroup("transform", "Transform");
|
||||
|
||||
// 아이템 추가
|
||||
transformGroup.AddItem(new UTKVector3PropertyItem("position", "Position", Vector3.zero));
|
||||
transformGroup.AddItem(new UTKVector3PropertyItem("rotation", "Rotation", Vector3.zero));
|
||||
transformGroup.AddItem(new UTKVector3PropertyItem("scale", "Scale", Vector3.one));
|
||||
|
||||
// 그룹 전체 ReadOnly 설정
|
||||
transformGroup.SetAllItemsReadOnly(true);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 사용 예제
|
||||
|
||||
### 다양한 속성 타입 생성
|
||||
|
||||
```csharp
|
||||
// 기본 타입
|
||||
var stringItem = new UTKStringPropertyItem("name", "이름", "홍길동");
|
||||
var intItem = new UTKIntPropertyItem("count", "개수", 10);
|
||||
var floatItem = new UTKFloatPropertyItem("speed", "속도", 1.5f);
|
||||
var boolItem = new UTKBoolPropertyItem("active", "활성화", true);
|
||||
|
||||
// 벡터 타입
|
||||
var posItem = new UTKVector2PropertyItem("pos", "위치", Vector2.zero);
|
||||
var rotItem = new UTKVector3PropertyItem("rot", "회전", Vector3.zero);
|
||||
|
||||
// 색상 타입
|
||||
var colorItem = new UTKColorPropertyItem("color", "색상", Color.white);
|
||||
colorItem.UseAlpha = true; // 알파 채널 사용
|
||||
|
||||
// 날짜 타입
|
||||
var dateItem = new UTKDatePropertyItem("start", "시작일", DateTime.Today);
|
||||
dateItem.DateFormat = "yyyy-MM-dd";
|
||||
|
||||
// 드롭다운 타입
|
||||
var dropdownItem = new UTKDropdownPropertyItem("type", "유형", "TypeA");
|
||||
dropdownItem.Choices = new List<string> { "TypeA", "TypeB", "TypeC" };
|
||||
|
||||
// Enum 타입
|
||||
var enumItem = new UTKEnumPropertyItem("align", "정렬", TextAlignment.Center);
|
||||
```
|
||||
|
||||
### 슬라이더/스테퍼 활용
|
||||
|
||||
```csharp
|
||||
// Int 슬라이더
|
||||
var intItem = new UTKIntPropertyItem("volume", "볼륨", 50);
|
||||
intItem.UseSlider = true;
|
||||
intItem.MinValue = 0;
|
||||
intItem.MaxValue = 100;
|
||||
|
||||
// Float 스테퍼
|
||||
var floatItem = new UTKFloatPropertyItem("opacity", "투명도", 1.0f);
|
||||
floatItem.UseStepper = true;
|
||||
floatItem.Step = 0.1f;
|
||||
floatItem.MinValue = 0f;
|
||||
floatItem.MaxValue = 1f;
|
||||
```
|
||||
|
||||
### 범위(Range) 타입 사용
|
||||
|
||||
```csharp
|
||||
// Int 범위
|
||||
var intRange = new UTKIntRangePropertyItem("level", "레벨 범위",
|
||||
new UTKIntRange { Min = 1, Max = 10 });
|
||||
intRange.UseStepper = true;
|
||||
intRange.StepperStep = 1;
|
||||
|
||||
// Float 범위
|
||||
var floatRange = new UTKFloatRangePropertyItem("temp", "온도 범위",
|
||||
new UTKFloatRange { Min = -10f, Max = 40f });
|
||||
|
||||
// 날짜 범위
|
||||
var dateRange = new UTKDateRangePropertyItem("period", "기간",
|
||||
new UTKDateRange { Start = DateTime.Today, End = DateTime.Today.AddDays(30) });
|
||||
```
|
||||
|
||||
### 멀티셀렉트 드롭다운
|
||||
|
||||
```csharp
|
||||
var multiSelect = new UTKMultiSelectDropdownPropertyItem(
|
||||
"tags", "태그", new List<string> { "UI", "3D" });
|
||||
multiSelect.Choices = new List<string> { "UI", "3D", "Network", "Audio", "Physics" };
|
||||
|
||||
// 프로그래밍 방식 선택
|
||||
multiSelect.SelectAll();
|
||||
multiSelect.ClearSelection();
|
||||
multiSelect.SetSelectedValues(new List<string> { "UI", "Audio" });
|
||||
```
|
||||
|
||||
### ColorState 사용
|
||||
|
||||
```csharp
|
||||
var colorState = new UTKColorStatePropertyItem("status", "상태",
|
||||
new UTKColorState { State = "Normal", Color = Color.green });
|
||||
|
||||
// 상태/색상 개별 변경
|
||||
colorState.SetState("Warning");
|
||||
colorState.SetColor(Color.yellow);
|
||||
```
|
||||
|
||||
### FloatDropdown 복합 타입
|
||||
|
||||
```csharp
|
||||
var floatDropdown = new UTKFloatDropdownPropertyItem("size", "크기",
|
||||
new UTKFloatDropdownValue { FloatValue = 100f, DropdownValue = "px" });
|
||||
floatDropdown.Choices = new List<string> { "px", "%", "em", "rem" };
|
||||
floatDropdown.UseStepper = true;
|
||||
```
|
||||
|
||||
### Button 아이템
|
||||
|
||||
```csharp
|
||||
var buttonItem = new UTKButtonItem("save", "저장");
|
||||
buttonItem.Text = "저장하기";
|
||||
buttonItem.Icon = UTKMaterialIcons.Save;
|
||||
buttonItem.Variant = UTKButton.ButtonVariant.Primary;
|
||||
buttonItem.ActionName = "save_action";
|
||||
```
|
||||
|
||||
### String + ActionButton 조합
|
||||
|
||||
```csharp
|
||||
var pathItem = new UTKStringPropertyItem("path", "파일 경로", "");
|
||||
|
||||
// ActionButton 설정 (찾아보기 버튼)
|
||||
var browseButton = new UTKButtonItem("browse", "찾아보기");
|
||||
browseButton.Icon = UTKMaterialIcons.FolderOpen;
|
||||
browseButton.IconOnly = true;
|
||||
pathItem.ActionButton = browseButton;
|
||||
```
|
||||
|
||||
### 커스텀 View 등록
|
||||
|
||||
```csharp
|
||||
// 기본 View를 커스텀 View로 교체
|
||||
UTKPropertyItemViewFactory.RegisterCustomView(
|
||||
UTKPropertyType.String,
|
||||
() => new MyCustomStringView()
|
||||
);
|
||||
|
||||
// 해제
|
||||
UTKPropertyItemViewFactory.UnregisterCustomView(UTKPropertyType.String);
|
||||
```
|
||||
|
||||
### 값 변경 이벤트 처리
|
||||
|
||||
```csharp
|
||||
// 제네릭 이벤트 (타입 안전)
|
||||
floatItem.OnTypedValueChanged += (item, oldVal, newVal) =>
|
||||
{
|
||||
Debug.Log($"{item.Name}: {oldVal} → {newVal}");
|
||||
};
|
||||
|
||||
// 비제네릭 이벤트 (범용)
|
||||
floatItem.OnValueChanged += (item, oldVal, newVal, notify) =>
|
||||
{
|
||||
Debug.Log($"{item.Id} changed");
|
||||
};
|
||||
|
||||
// 상태 변경 이벤트 (ReadOnly, ShowLabel 등)
|
||||
floatItem.OnStateChanged += (item) =>
|
||||
{
|
||||
Debug.Log($"{item.Id} state changed, ReadOnly={item.IsReadOnly}");
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 지원 타입 목록
|
||||
|
||||
| UTKPropertyType | Item 클래스 | View 클래스 | 데이터 타입 | 주요 컨트롤 |
|
||||
|-----------------|------------|------------|------------|------------|
|
||||
| `String` | UTKStringPropertyItem | UTKStringPropertyItemView | string | UTKInputField + ActionButton |
|
||||
| `Int` | UTKIntPropertyItem | UTKIntPropertyItemView | int | UTKIntegerField + Slider/Stepper |
|
||||
| `Float` | UTKFloatPropertyItem | UTKFloatPropertyItemView | float | UTKFloatField + Slider/Stepper |
|
||||
| `Bool` | UTKBoolPropertyItem | UTKBoolPropertyItemView | bool | UTKToggle |
|
||||
| `Vector2` | UTKVector2PropertyItem | UTKVector2PropertyItemView | Vector2 | UTKVector2Field |
|
||||
| `Vector3` | UTKVector3PropertyItem | UTKVector3PropertyItemView | Vector3 | UTKVector3Field |
|
||||
| `Color` | UTKColorPropertyItem | UTKColorPropertyItemView | Color | ColorPreview + UTKColorPicker |
|
||||
| `ColorState` | UTKColorStatePropertyItem | UTKColorStatePropertyItemView | UTKColorState | Label + ColorPreview + Picker |
|
||||
| `Date` | UTKDatePropertyItem | UTKDatePropertyItemView | DateTime | UTKInputField + UTKDatePicker |
|
||||
| `DateTime` | UTKDateTimePropertyItem | UTKDateTimePropertyItemView | DateTime | UTKInputField + UTKDatePicker |
|
||||
| `DateRange` | UTKDateRangePropertyItem | UTKDateRangePropertyItemView | UTKDateRange | InputField×2 + DatePicker×2 |
|
||||
| `DateTimeRange` | UTKDateTimeRangePropertyItem | UTKDateTimeRangePropertyItemView | UTKDateTimeRange | InputField×2 + DatePicker×2 |
|
||||
| `Enum` | UTKEnumPropertyItem | UTKEnumPropertyItemView | Enum | UTKEnumDropDown |
|
||||
| `DropdownList` | UTKDropdownPropertyItem | UTKDropdownPropertyItemView | string | UTKDropdown |
|
||||
| `MultiSelectDropdownList` | UTKMultiSelectDropdownPropertyItem | UTKMultiSelectDropdownPropertyItemView | List\<string\> | UTKMultiSelectDropdown |
|
||||
| `RadioGroup` | UTKRadioPropertyItem | UTKRadioPropertyItemView | int (index) | UTKRadioButton × N |
|
||||
| `IntRange` | UTKIntRangePropertyItem | UTKIntRangePropertyItemView | UTKIntRange | IntegerField×2 + Stepper×2 |
|
||||
| `FloatRange` | UTKFloatRangePropertyItem | UTKFloatRangePropertyItemView | UTKFloatRange | FloatField×2 + Stepper×2 |
|
||||
| `FloatDropdown` | UTKFloatDropdownPropertyItem | UTKFloatDropdownPropertyItemView | UTKFloatDropdownValue | FloatField/Stepper + Dropdown |
|
||||
| `Button` | UTKButtonItem | UTKButtonItemView | string (actionName) | UTKButton |
|
||||
|
||||
---
|
||||
|
||||
## API 문서
|
||||
|
||||
### IUTKPropertyItem (데이터 인터페이스)
|
||||
|
||||
#### Properties
|
||||
|
||||
| 속성 | 타입 | 설명 |
|
||||
|------|------|------|
|
||||
| `Id` | string | 고유 식별자 |
|
||||
| `Name` | string | 표시 이름 |
|
||||
| `DisplayName` | string | 표시 이름 (Name과 동일) |
|
||||
| `Description` | string | 설명 |
|
||||
| `Tooltip` | string | 툴팁 텍스트 |
|
||||
| `IsReadOnly` | bool | 읽기 전용 여부 |
|
||||
| `IsVisible` | bool | 표시 여부 |
|
||||
| `ShowLabel` | bool | 라벨 표시 여부 |
|
||||
| `PropertyType` | UTKPropertyType | 속성 타입 열거형 |
|
||||
| `GroupId` | string | 소속 그룹 ID |
|
||||
|
||||
#### Methods
|
||||
|
||||
| 메서드 | 설명 | 반환 |
|
||||
|--------|------|------|
|
||||
| `GetValue()` | 현재 값 가져오기 (object?) | object? |
|
||||
| `SetValue(object?, bool)` | 값 설정 (notify로 이벤트 발생 제어) | void |
|
||||
|
||||
#### Events
|
||||
|
||||
| 이벤트 | 타입 | 설명 |
|
||||
|--------|------|------|
|
||||
| `OnValueChanged` | Action\<IUTKPropertyItem, object?, object?, bool\> | 값 변경 (비제네릭) |
|
||||
| `OnTypedValueChanged` | Action\<IUTKPropertyItem\<T\>, T, T\> | 값 변경 (제네릭, 타입 안전) |
|
||||
| `OnStateChanged` | Action\<IUTKPropertyItem\> | 상태 변경 (ReadOnly, ShowLabel 등) |
|
||||
|
||||
### IUTKPropertyGroup (그룹 인터페이스)
|
||||
|
||||
#### Properties
|
||||
|
||||
| 속성 | 타입 | 설명 |
|
||||
|------|------|------|
|
||||
| `GroupId` | string | 그룹 고유 ID |
|
||||
| `GroupName` | string | 그룹 표시 이름 |
|
||||
| `IsExpanded` | bool | 접기/펼치기 상태 |
|
||||
| `Items` | IReadOnlyList\<IUTKPropertyItem\> | 하위 아이템 목록 |
|
||||
| `ItemCount` | int | 아이템 수 |
|
||||
|
||||
#### Methods
|
||||
|
||||
| 메서드 | 설명 | 파라미터 |
|
||||
|--------|------|----------|
|
||||
| `AddItem(item)` | 아이템 추가 | IUTKPropertyItem |
|
||||
| `RemoveItem(itemId)` | 아이템 제거 | string → bool |
|
||||
| `GetItem(itemId)` | 아이템 검색 | string → IUTKPropertyItem? |
|
||||
| `Clear()` | 전체 아이템 제거 | - |
|
||||
| `SetAllItemsReadOnly(isReadOnly)` | 일괄 ReadOnly 설정 | bool |
|
||||
|
||||
#### Events
|
||||
|
||||
| 이벤트 | 타입 | 설명 |
|
||||
|--------|------|------|
|
||||
| `OnItemAdded` | Action\<IUTKPropertyGroup, IUTKPropertyItem\> | 아이템 추가 |
|
||||
| `OnItemRemoved` | Action\<IUTKPropertyGroup, IUTKPropertyItem\> | 아이템 제거 |
|
||||
| `OnExpandedChanged` | Action\<IUTKPropertyGroup, bool\> | 접기/펼치기 변경 |
|
||||
|
||||
### UTKPropertyItemViewFactory (팩토리)
|
||||
|
||||
#### Public Methods
|
||||
|
||||
| 메서드 | 설명 | 반환 |
|
||||
|--------|------|------|
|
||||
| `CreateView(data)` | View 생성 + 바인딩 후 VisualElement 반환 | VisualElement? |
|
||||
| `CreateViewInstance(data)` | View 인스턴스만 생성 (데이터 기반 초기화) | IUTKPropertyItemView? |
|
||||
| `CreateViewInstance(type)` | View 인스턴스만 생성 (타입 기반) | IUTKPropertyItemView? |
|
||||
| `GetViewType(data)` | 데이터에 맞는 View Type 조회 | Type? |
|
||||
| `RegisterCustomView(type, factory)` | 커스텀 View 등록 | void |
|
||||
| `UnregisterCustomView(type)` | 커스텀 View 해제 | void |
|
||||
| `ClearCustomViews()` | 모든 커스텀 View 해제 | void |
|
||||
|
||||
### UTKPropertyItemViewBase (View 베이스)
|
||||
|
||||
#### UxmlAttributes
|
||||
|
||||
| 속성 | UXML 어트리뷰트 | 타입 | 설명 |
|
||||
|------|----------------|------|------|
|
||||
| `Label` | `label` | string | 라벨 텍스트 |
|
||||
| `IsReadOnly` | `is-read-only` | bool | 읽기 전용 |
|
||||
| `IsVisible` | `is-visible` | bool | 표시 여부 |
|
||||
| `TooltipText` | `tooltip-text` | string | 툴팁 |
|
||||
| `ShowLabel` | `show-label` | bool | 라벨 표시 여부 |
|
||||
|
||||
#### Public Methods
|
||||
|
||||
| 메서드 | 설명 |
|
||||
|--------|------|
|
||||
| `Bind(data)` | 데이터 바인딩 (양방향 동기화 시작) |
|
||||
| `Unbind()` | 바인딩 해제 (이벤트 구독 해제) |
|
||||
| `RefreshUI()` | UI 갱신 |
|
||||
| `Dispose()` | 리소스 정리 |
|
||||
| `ClearAllCache()` | 정적 UXML/USS 캐시 전체 정리 (static) |
|
||||
|
||||
---
|
||||
|
||||
## 메모리 관리
|
||||
|
||||
### Bind/Unbind 패턴
|
||||
|
||||
모든 View는 `Bind()`에서 이벤트를 구독하고 `Unbind()`에서 해제합니다:
|
||||
|
||||
```csharp
|
||||
// View 내부 구현 패턴
|
||||
public void Bind(IUTKPropertyItem<float> data)
|
||||
{
|
||||
_boundData = data;
|
||||
data.OnTypedValueChanged += OnDataValueChanged;
|
||||
data.OnStateChanged += OnDataStateChanged;
|
||||
RefreshUI();
|
||||
}
|
||||
|
||||
public void Unbind()
|
||||
{
|
||||
if (_boundData != null)
|
||||
{
|
||||
_boundData.OnTypedValueChanged -= OnDataValueChanged;
|
||||
_boundData.OnStateChanged -= OnDataStateChanged;
|
||||
_boundData = null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### IDisposable 구현
|
||||
|
||||
모든 Item과 View 클래스가 IDisposable을 구현합니다:
|
||||
|
||||
```csharp
|
||||
// Item Dispose
|
||||
var item = new UTKFloatPropertyItem("speed", "속도", 1.0f);
|
||||
// ... 사용 ...
|
||||
item.Dispose(); // 모든 이벤트 구독 해제
|
||||
|
||||
// Group Dispose
|
||||
var group = new UTKPropertyGroup("transform", "Transform");
|
||||
group.AddItem(item);
|
||||
// ... 사용 ...
|
||||
group.Dispose(); // 하위 아이템 전부 Dispose + 이벤트 해제
|
||||
|
||||
// View Dispose
|
||||
var view = UTKPropertyItemViewFactory.CreateView(item);
|
||||
// ... 사용 ...
|
||||
if (view is IDisposable disposable)
|
||||
disposable.Dispose(); // Unbind + 테마 구독 해제 + 콜백 해제
|
||||
```
|
||||
|
||||
### 테마 이벤트 관리
|
||||
|
||||
View 베이스 클래스에서 `AttachToPanelEvent`/`DetachFromPanelEvent`로 테마 구독을 관리합니다:
|
||||
|
||||
```csharp
|
||||
// UTKPropertyItemViewBase 내부
|
||||
private void OnAttachToPanel(AttachToPanelEvent evt)
|
||||
{
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
|
||||
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
||||
}
|
||||
|
||||
private void OnDetachFromPanel(DetachFromPanelEvent evt)
|
||||
{
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 성능 최적화
|
||||
|
||||
### 정적 UXML/USS 캐싱
|
||||
|
||||
View 베이스 클래스에서 UXML/USS를 정적 Dictionary로 캐싱하여 `Resources.Load` 호출을 최소화합니다:
|
||||
|
||||
```csharp
|
||||
// UTKPropertyItemViewBase 내부
|
||||
private static readonly Dictionary<string, VisualTreeAsset> _uxmlCache = new();
|
||||
private static readonly Dictionary<string, StyleSheet> _ussCache = new();
|
||||
private static StyleSheet? _commonUssCache;
|
||||
|
||||
// 최초 1회만 Resources.Load 호출, 이후 캐시에서 반환
|
||||
// 결과: 20개 View 타입 × 다수 인스턴스 → 각 타입당 Resources.Load 1회
|
||||
```
|
||||
|
||||
### Fallback UI 패턴
|
||||
|
||||
UXML 로드 실패 시 코드로 UI를 생성합니다:
|
||||
|
||||
```csharp
|
||||
// UXML 존재 → UXML 기반 UI 생성
|
||||
if (CreateUIFromUxml())
|
||||
{
|
||||
// _labelElement, _valueContainer 자동 쿼리
|
||||
}
|
||||
else
|
||||
{
|
||||
// UXML 없음 → 코드로 Fallback UI 생성
|
||||
CreateUIFallback();
|
||||
}
|
||||
```
|
||||
|
||||
### ReadOnly 전환 최적화
|
||||
|
||||
컨트롤을 생성/파괴하지 않고 DisplayStyle 토글로 전환합니다:
|
||||
|
||||
```csharp
|
||||
// ❌ 나쁜 예: 매번 생성/파괴
|
||||
if (isReadOnly)
|
||||
{
|
||||
Remove(_editControl);
|
||||
Add(new UTKInputField(...));
|
||||
}
|
||||
|
||||
// ✅ 좋은 예: 미리 생성 후 DisplayStyle 전환
|
||||
_editControl.style.display = isReadOnly ? DisplayStyle.None : DisplayStyle.Flex;
|
||||
_readOnlyField.style.display = isReadOnly ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
```
|
||||
|
||||
### 성능 요약
|
||||
|
||||
| 항목 | 설명 |
|
||||
|------|------|
|
||||
| **UXML/USS 로드** | 정적 Dictionary 캐싱, 타입당 1회만 로드 |
|
||||
| **공통 USS** | `UTKPropertyItemViewCommonUss.uss` 1회 로드 후 전체 View 공유 |
|
||||
| **쿼리 캐싱** | `Q<T>()` 결과를 필드에 캐싱 (초기화 시 1회) |
|
||||
| **ReadOnly 전환** | DisplayStyle 토글 (생성/파괴 없음) |
|
||||
| **이벤트 관리** | Bind/Unbind로 정확한 구독/해제 대칭 |
|
||||
| **GC Alloc** | LINQ 미사용, 클로저 캡처 최소화 |
|
||||
|
||||
---
|
||||
|
||||
## 문제 해결
|
||||
|
||||
### View가 표시되지 않는 경우
|
||||
|
||||
1. **Factory 사용 확인**
|
||||
```csharp
|
||||
var view = UTKPropertyItemViewFactory.CreateView(item);
|
||||
if (view == null)
|
||||
{
|
||||
Debug.LogError($"지원하지 않는 PropertyType: {item.PropertyType}");
|
||||
}
|
||||
```
|
||||
|
||||
2. **Bind 호출 확인**
|
||||
- `CreateView()`는 내부적으로 Bind까지 수행
|
||||
- `CreateViewInstance()`는 Bind를 별도로 호출해야 함
|
||||
|
||||
3. **UXML/USS 경로 확인**
|
||||
- 리소스 경로: `UIToolkit/Property/Views/{ViewTypeName}`
|
||||
- USS 접미사: `{ViewTypeName}Uss`
|
||||
|
||||
### 값이 동기화되지 않는 경우
|
||||
|
||||
1. **Bind 상태 확인**: `Unbind()` 후 다시 `Bind()` 필요
|
||||
2. **이벤트 확인**: `OnTypedValueChanged` 구독이 정상적으로 등록되었는지 확인
|
||||
3. **SetValue notify 파라미터**: `SetValue(value, false)`는 이벤트를 발생시키지 않음
|
||||
|
||||
---
|
||||
|
||||
## 유지보수 가이드
|
||||
|
||||
> **소스 코드를 수정할 때 반드시 CLAUDE.md와 코드 주석(XML 문서)도 함께 업데이트해야 합니다.**
|
||||
|
||||
| 변경 사항 | 업데이트 대상 |
|
||||
|-----------|--------------|
|
||||
| 클래스/메서드 추가·삭제·이름 변경 | CLAUDE API 문서 + 파일 구조 + 해당 클래스 XML 주석 |
|
||||
| 생성자 파라미터 변경 | CLAUDE API 문서 + 사용 예제 + XML `<param>` 태그 |
|
||||
| 공개 속성(Property) 추가·삭제 | CLAUDE API 테이블 + 지원 타입 목록 + XML `<summary>` 태그 |
|
||||
| 이벤트 추가·삭제 | CLAUDE 이벤트 테이블 + 메모리 관리 섹션 |
|
||||
| 새로운 PropertyType 추가 | CLAUDE 지원 타입 목록 + Factory 문서 + 파일 구조 |
|
||||
| UXML/USS 파일 추가·삭제 | CLAUDE 파일 구조 섹션 |
|
||||
| 새로운 기능 추가 | CLAUDE 주요 기능 + 사용 예제 섹션 |
|
||||
|
||||
---
|
||||
|
||||
## 라이선스
|
||||
|
||||
이 프로젝트는 UVC 프레임워크의 일부입니다.
|
||||
|
||||
---
|
||||
|
||||
## 작성자
|
||||
|
||||
- **작성일**: 2026-02-19
|
||||
- **작성자**: Claude Code Assistant
|
||||
- **버전**: 1.0.0
|
||||
7
Assets/Scripts/UVC/UIToolkit/Property/CLAUDE.md.meta
Normal file
7
Assets/Scripts/UVC/UIToolkit/Property/CLAUDE.md.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ec07f57efe3a4eb44a2f9af0e4ba1c43
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user