UTKProperyWIndow 수정 중
This commit is contained in:
547
Assets/Scripts/UVC/UIToolkit/CLAUDE.md
Normal file
547
Assets/Scripts/UVC/UIToolkit/CLAUDE.md
Normal file
@@ -0,0 +1,547 @@
|
||||
# UVC.UIToolkit 컴포넌트 라이브러리
|
||||
|
||||
본 문서는 `Assets/Scripts/UVC/UIToolkit` 폴더의 UI Toolkit 컴포넌트 라이브러리에 대한 참조 가이드입니다.
|
||||
|
||||
---
|
||||
|
||||
## 1) 폴더 구조
|
||||
|
||||
```
|
||||
Assets/Scripts/UVC/UIToolkit/
|
||||
├── Button/ # 버튼 컴포넌트
|
||||
│ ├── UTKButton.cs # 메인 버튼 (9가지 variant)
|
||||
│ ├── UTKCheckBox.cs # 체크박스
|
||||
│ ├── UTKToggle.cs # 토글 스위치
|
||||
│ ├── UTKRadioButton.cs # 라디오 버튼
|
||||
│ └── UTKToggleButtonGroup.cs # 토글 버튼 그룹
|
||||
├── Card/
|
||||
│ └── UTKCard.cs # 카드 컨테이너
|
||||
├── Common/ # 공통 유틸리티
|
||||
│ ├── UTKCodeBlock.cs # 코드 블록 표시
|
||||
│ ├── UTKFoldout.cs # 접이식 컨테이너
|
||||
│ ├── UTKHelpBox.cs # 도움말/정보 박스
|
||||
│ ├── UTKImage.cs # 이미지 로더 (내부/URL)
|
||||
│ ├── UTKImageIcons.cs # 이미지 아이콘 레지스트리
|
||||
│ ├── UTKMaterialIcons.cs # Material Symbols (4102개)
|
||||
│ ├── UTKScrollView.cs # 커스텀 스크롤 뷰
|
||||
│ └── UTKTooltipManager.cs # 툴팁 시스템
|
||||
├── Dropdown/
|
||||
│ ├── UTKDropdown.cs # 일반 드롭다운
|
||||
│ └── UTKEnumDropDown.cs # Enum 전용 드롭다운
|
||||
├── Input/ # 입력 필드
|
||||
│ ├── UTKBoundsField.cs # Bounds 입력
|
||||
│ ├── UTKDoubleField.cs # Double 입력
|
||||
│ ├── UTKFloatField.cs # Float 입력
|
||||
│ ├── UTKInputField.cs # 텍스트 입력
|
||||
│ ├── UTKIntegerField.cs # Integer 입력
|
||||
│ ├── UTKLongField.cs # Long 입력
|
||||
│ ├── UTKNumberStepper.cs # 숫자 스테퍼 (+/-)
|
||||
│ ├── UTKRectField.cs # Rect 입력
|
||||
│ ├── UTKVector2Field.cs # Vector2 입력
|
||||
│ ├── UTKVector3Field.cs # Vector3 입력
|
||||
│ └── UTKVector4Field.cs # Vector4 입력
|
||||
├── Label/
|
||||
│ └── UTKLabel.cs # 커스텀 레이블
|
||||
├── List/ # 리스트/트리 컴포넌트
|
||||
│ ├── UTKAccordionList.cs # 아코디언 리스트 (섹션+그리드)
|
||||
│ ├── UTKAccordionListItemData.cs
|
||||
│ ├── UTKComponentList.cs # 컴포넌트 리스트
|
||||
│ ├── UTKComponentListItemData.cs
|
||||
│ ├── UTKImageList.cs # 이미지 그리드 리스트
|
||||
│ ├── UTKImageListItemData.cs
|
||||
│ ├── UTKListView.cs # 일반 리스트 뷰
|
||||
│ ├── UTKMultiColumnListView.cs # 다중 컬럼 리스트
|
||||
│ ├── UTKMultiColumnTreeView.cs # 다중 컬럼 트리
|
||||
│ ├── UTKTreeListItemData.cs
|
||||
│ └── UTKTreeView.cs # 트리 뷰
|
||||
├── Modal/ # 모달/다이얼로그
|
||||
│ ├── UTKAlert.cs # 알림 다이얼로그
|
||||
│ ├── UTKColorPicker.cs # 색상 선택기
|
||||
│ ├── UTKDatePicker.cs # 날짜 선택기
|
||||
│ ├── UTKModal.cs # 기본 모달
|
||||
│ ├── UTKModalBlocker.cs # 모달 배경 블로커
|
||||
│ ├── UTKNotification.cs # 알림 토스트
|
||||
│ ├── UTKPanel.cs # 패널 컨테이너
|
||||
│ └── UTKToast.cs # 토스트 알림
|
||||
├── Property/ # 프로퍼티 에디터 시스템
|
||||
│ ├── Core/ # 핵심 인터페이스
|
||||
│ │ ├── IUTKPropertyEntry.cs
|
||||
│ │ ├── IUTKPropertyGroup.cs
|
||||
│ │ ├── IUTKPropertyItem.cs
|
||||
│ │ ├── UTKPropertyGroup.cs
|
||||
│ │ ├── UTKPropertyType.cs
|
||||
│ │ └── UTKPropertyValueChangedEventArgs.cs
|
||||
│ ├── Data/ # 데이터 타입
|
||||
│ │ ├── UTKColorState.cs
|
||||
│ │ └── UTKRangeValue.cs
|
||||
│ ├── Items/ # 프로퍼티 아이템 (16종)
|
||||
│ │ ├── Base/UTKPropertyItemBase.cs
|
||||
│ │ ├── UTKBoolPropertyItem.cs
|
||||
│ │ ├── UTKColorPropertyItem.cs
|
||||
│ │ ├── UTKColorStatePropertyItem.cs
|
||||
│ │ ├── UTKDatePropertyItem.cs
|
||||
│ │ ├── UTKDateRangePropertyItem.cs
|
||||
│ │ ├── UTKDateTimePropertyItem.cs
|
||||
│ │ ├── UTKDateTimeRangePropertyItem.cs
|
||||
│ │ ├── UTKDropdownPropertyItem.cs
|
||||
│ │ ├── UTKEnumPropertyItem.cs
|
||||
│ │ ├── UTKFloatPropertyItem.cs
|
||||
│ │ ├── UTKFloatRangePropertyItem.cs
|
||||
│ │ ├── UTKIntPropertyItem.cs
|
||||
│ │ ├── UTKIntRangePropertyItem.cs
|
||||
│ │ ├── UTKRadioPropertyItem.cs
|
||||
│ │ ├── UTKStringPropertyItem.cs
|
||||
│ │ ├── UTKVector2PropertyItem.cs
|
||||
│ │ └── UTKVector3PropertyItem.cs
|
||||
│ ├── UTKPropertyList.cs # 프로퍼티 리스트 컨테이너
|
||||
│ └── UTKPropertyWindow.cs # 프로퍼티 윈도우
|
||||
├── Slider/
|
||||
│ ├── UTKMinMaxSlider.cs # 범위 슬라이더
|
||||
│ ├── UTKProgressBar.cs # 프로그레스 바
|
||||
│ └── UTKSlider.cs # 단일 슬라이더
|
||||
├── Tab/
|
||||
│ ├── UTKTab.cs # 개별 탭
|
||||
│ └── UTKTabView.cs # 탭 컨테이너
|
||||
├── Window/ # 윈도우 컴포넌트
|
||||
│ ├── UTKAccordionListWindow.cs
|
||||
│ ├── UTKComponentListWindow.cs
|
||||
│ ├── UTKComponentTabListWindow.cs
|
||||
│ ├── UTKImageListWindow.cs
|
||||
│ └── UTKTreeListWindow.cs
|
||||
├── UTKStyleGuide.cs # 스타일 상수 및 색상
|
||||
└── UTKThemeManager.cs # 테마 관리 (Light/Dark)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2) 핵심 시스템
|
||||
|
||||
### 테마 관리 (UTKThemeManager)
|
||||
|
||||
라이트/다크 테마 전환을 관리하는 싱글톤입니다.
|
||||
|
||||
```csharp
|
||||
// 테마 등록 및 전환
|
||||
UTKThemeManager.Instance.RegisterRoot(rootVisualElement);
|
||||
UTKThemeManager.Instance.SetTheme(UTKTheme.Dark);
|
||||
UTKThemeManager.Instance.ToggleTheme();
|
||||
|
||||
// 테마 변경 이벤트 구독
|
||||
UTKThemeManager.OnThemeChanged += OnThemeChanged;
|
||||
|
||||
// 관리 USS 파일
|
||||
// - UTKVariables.uss (CSS 변수)
|
||||
// - UTKThemeDark.uss / UTKThemeLight.uss (테마별 스타일)
|
||||
// - UTKComponents.uss (컴포넌트 공통 스타일)
|
||||
```
|
||||
|
||||
### 스타일 가이드 (UTKStyleGuide)
|
||||
|
||||
색상, 간격, 폰트, 컴포넌트 크기 등의 상수를 정의합니다.
|
||||
|
||||
| 카테고리 | 설명 |
|
||||
|----------|------|
|
||||
| Base Colors | Base01~Base21 기본 색상 팔레트 |
|
||||
| Accent Colors | Blue, Red, Green, Purple, Yellow, Orange, Gray, Seti |
|
||||
| Semantic Colors | Text, Background, Border, Button, Calendar, State, Scrollbar |
|
||||
|
||||
### 아이콘 시스템
|
||||
|
||||
**1순위: Material Icons (UTKMaterialIcons)**
|
||||
```csharp
|
||||
// 폰트 로드
|
||||
await UTKMaterialIcons.LoadFontAsync();
|
||||
|
||||
// 아이콘 적용
|
||||
var icon = UTKMaterialIcons.GetIcon("settings");
|
||||
UTKMaterialIcons.ApplyIconStyle(label, fontSize: 24);
|
||||
|
||||
// 버튼에 아이콘 설정
|
||||
button.SetMaterialIcon(UTKMaterialIcons.Home);
|
||||
```
|
||||
|
||||
**2순위: Image Icons (UTKImageIcons)**
|
||||
```csharp
|
||||
// 필요한 아이콘이 Material에 없는 경우에만 사용
|
||||
var texture = UTKImageIcons.LoadTexture("path/to/icon");
|
||||
button.SetImageIcon(UTKImageIcons.CustomIcon);
|
||||
```
|
||||
|
||||
> **참고**: Material Icons는 4,102개의 아이콘을 제공하며, 폰트 기반으로 메모리 효율이 높습니다.
|
||||
|
||||
---
|
||||
|
||||
## 3) 주요 컴포넌트
|
||||
|
||||
### UTKButton
|
||||
|
||||
다양한 스타일과 크기를 지원하는 버튼 컴포넌트입니다.
|
||||
|
||||
**Variants:**
|
||||
- Normal, Primary, Secondary, Ghost, Danger
|
||||
- OutlineNormal, OutlinePrimary, OutlineDanger, Text
|
||||
|
||||
**Sizes:** Small, Medium, Large
|
||||
|
||||
```csharp
|
||||
var button = new UTKButton("확인", UTKButton.Variant.Primary, UTKButton.Size.Medium);
|
||||
button.SetMaterialIcon(UTKMaterialIcons.Check);
|
||||
button.OnClicked += () => Debug.Log("Clicked!");
|
||||
|
||||
// UXML 사용
|
||||
<utk:UTKButton text="확인"
|
||||
icon="check"
|
||||
variant="Primary"
|
||||
size="Medium"
|
||||
icon-only="false" />
|
||||
```
|
||||
|
||||
### UTKAccordionList
|
||||
|
||||
섹션별 접기/펼치기가 가능한 아코디언 리스트입니다.
|
||||
|
||||
```csharp
|
||||
var accordionList = new UTKAccordionList();
|
||||
accordionList.SetData(sections); // List<UTKAccordionListItemData>
|
||||
accordionList.Search("검색어");
|
||||
accordionList.ExpandAll();
|
||||
accordionList.CollapseAll();
|
||||
|
||||
// 이벤트
|
||||
accordionList.OnItemClick += (item) => { };
|
||||
accordionList.OnDragExitList += (item) => { };
|
||||
accordionList.OnSectionToggled += (section, expanded) => { };
|
||||
```
|
||||
|
||||
**특징:**
|
||||
- TreeView 기반 가상화
|
||||
- 검색 필터링
|
||||
- 드래그 앤 드롭 지원
|
||||
- 비동기 이미지 로딩 및 캐싱
|
||||
- 그리드 레이아웃 (2열)
|
||||
|
||||
### UTKTreeView / UTKTreeListWindow
|
||||
|
||||
계층 구조 데이터를 트리 형태로 표시합니다.
|
||||
|
||||
```csharp
|
||||
var treeView = new UTKTreeView();
|
||||
treeView.SetData(rootItems); // List<UTKTreeListItemData>
|
||||
|
||||
// TreeListWindow 사용
|
||||
var window = new UTKTreeListWindow();
|
||||
window.SetData(items);
|
||||
window.OnItemSelected += (item) => { };
|
||||
```
|
||||
|
||||
### UTKModal / UTKColorPicker / UTKDatePicker
|
||||
|
||||
모달 다이얼로그 및 특수 선택기입니다.
|
||||
|
||||
```csharp
|
||||
// 기본 모달
|
||||
var modal = new UTKModal("제목");
|
||||
modal.AddContent(contentElement);
|
||||
modal.AddToFooter(confirmButton);
|
||||
modal.Show();
|
||||
modal.OnClosed += () => { };
|
||||
|
||||
// 색상 선택기
|
||||
var colorPicker = new UTKColorPicker();
|
||||
colorPicker.Color = Color.red;
|
||||
colorPicker.OnColorChanged += (color) => { };
|
||||
|
||||
// 날짜 선택기
|
||||
var datePicker = new UTKDatePicker();
|
||||
datePicker.SelectedDate = DateTime.Now;
|
||||
datePicker.OnDateSelected += (date) => { };
|
||||
```
|
||||
|
||||
### UTKImage
|
||||
|
||||
내부 리소스 및 외부 URL에서 이미지를 로드합니다.
|
||||
|
||||
```csharp
|
||||
var image = new UTKImage();
|
||||
|
||||
// Resources 로드
|
||||
await image.LoadAsync("UIToolkit/Icons/sample");
|
||||
|
||||
// HTTP/HTTPS 로드
|
||||
await image.LoadAsync("https://example.com/image.png");
|
||||
|
||||
// 이벤트
|
||||
image.OnImageLoaded += (texture) => { };
|
||||
image.OnImageFailed += (error) => { };
|
||||
|
||||
// 스케일 모드
|
||||
image.ScaleMode = ScaleMode.ScaleToFit; // ScaleToFit, ScaleAndCrop, StretchToFill
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4) 프로퍼티 에디터 시스템
|
||||
|
||||
Inspector 스타일의 프로퍼티 편집 시스템입니다.
|
||||
|
||||
### 아키텍처
|
||||
|
||||
```
|
||||
IUTKPropertyEntry (기본 인터페이스)
|
||||
├── IUTKPropertyItem<T> # 개별 프로퍼티
|
||||
│ ├── OnValueChanged event
|
||||
│ ├── GetValue() / SetValue()
|
||||
│ └── CreateUI() / BindUI() / UnbindUI()
|
||||
└── IUTKPropertyGroup # 프로퍼티 그룹
|
||||
├── Items 컬렉션
|
||||
├── AddItem() / RemoveItem()
|
||||
└── OnExpandedChanged event
|
||||
```
|
||||
|
||||
### 프로퍼티 타입 (16종)
|
||||
|
||||
| 타입 | 클래스 | 설명 |
|
||||
|------|--------|------|
|
||||
| String | UTKStringPropertyItem | 텍스트 입력 |
|
||||
| Int | UTKIntPropertyItem | 정수 (슬라이더 옵션) |
|
||||
| Float | UTKFloatPropertyItem | 실수 (슬라이더 옵션) |
|
||||
| Bool | UTKBoolPropertyItem | 토글 스위치 |
|
||||
| Vector2 | UTKVector2PropertyItem | 2D 벡터 |
|
||||
| Vector3 | UTKVector3PropertyItem | 3D 벡터 |
|
||||
| Color | UTKColorPropertyItem | 색상 선택기 |
|
||||
| Date | UTKDatePropertyItem | 날짜 선택 |
|
||||
| DateTime | UTKDateTimePropertyItem | 날짜+시간 선택 |
|
||||
| Enum | UTKEnumPropertyItem | Enum 드롭다운 |
|
||||
| Dropdown | UTKDropdownPropertyItem | 문자열 드롭다운 |
|
||||
| Radio | UTKRadioPropertyItem | 라디오 버튼 그룹 |
|
||||
| IntRange | UTKIntRangePropertyItem | 정수 범위 (Min/Max) |
|
||||
| FloatRange | UTKFloatRangePropertyItem | 실수 범위 |
|
||||
| DateRange | UTKDateRangePropertyItem | 날짜 범위 |
|
||||
| DateTimeRange | UTKDateTimeRangePropertyItem | 날짜+시간 범위 |
|
||||
| ColorState | UTKColorStatePropertyItem | 색상+상태 (커스텀) |
|
||||
|
||||
### 사용 예시
|
||||
|
||||
```csharp
|
||||
// 프로퍼티 윈도우 생성
|
||||
var propertyWindow = new UTKPropertyWindow("설정");
|
||||
|
||||
// 그룹 생성
|
||||
var generalGroup = new UTKPropertyGroup("일반 설정");
|
||||
|
||||
// 프로퍼티 아이템 추가
|
||||
generalGroup.AddItem(new UTKStringPropertyItem("이름", "기본값"));
|
||||
generalGroup.AddItem(new UTKIntPropertyItem("레벨", 1, minValue: 1, maxValue: 100));
|
||||
generalGroup.AddItem(new UTKBoolPropertyItem("활성화", true));
|
||||
generalGroup.AddItem(new UTKColorPropertyItem("색상", Color.white));
|
||||
|
||||
// 그룹을 윈도우에 추가
|
||||
propertyWindow.PropertyList.AddGroup(generalGroup);
|
||||
|
||||
// 값 변경 이벤트
|
||||
generalGroup.OnValueChanged += (sender, args) => {
|
||||
Debug.Log($"{args.PropertyName}: {args.OldValue} → {args.NewValue}");
|
||||
};
|
||||
```
|
||||
|
||||
**특징:**
|
||||
- 디바운스 지원 (기본 300ms)
|
||||
- 읽기 전용 모드
|
||||
- 툴팁 지원
|
||||
- IDisposable 패턴으로 메모리 관리
|
||||
|
||||
---
|
||||
|
||||
## 5) Window 컴포넌트
|
||||
|
||||
### UTKAccordionListWindow
|
||||
|
||||
아코디언 리스트를 포함한 윈도우입니다.
|
||||
|
||||
```csharp
|
||||
var window = new UTKAccordionListWindow();
|
||||
window.SetTitle("아이템 목록");
|
||||
window.SetData(sections);
|
||||
window.OnItemSelected += (item) => { };
|
||||
```
|
||||
|
||||
### UTKComponentListWindow
|
||||
|
||||
컴포넌트 리스트 윈도우입니다.
|
||||
|
||||
```csharp
|
||||
var window = new UTKComponentListWindow();
|
||||
window.SetData(components);
|
||||
window.OnComponentSelected += (component) => { };
|
||||
```
|
||||
|
||||
### UTKTreeListWindow
|
||||
|
||||
트리 구조 데이터를 표시하는 윈도우입니다.
|
||||
|
||||
```csharp
|
||||
var window = new UTKTreeListWindow();
|
||||
window.SetData(treeItems);
|
||||
window.OnItemSelected += (item) => { };
|
||||
window.ExpandAll();
|
||||
```
|
||||
|
||||
### UTKImageListWindow
|
||||
|
||||
이미지 그리드를 표시하는 윈도우입니다.
|
||||
|
||||
```csharp
|
||||
var window = new UTKImageListWindow();
|
||||
window.SetData(images);
|
||||
window.OnImageSelected += (image) => { };
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6) 입력 필드
|
||||
|
||||
### 기본 입력 필드
|
||||
|
||||
```csharp
|
||||
// 정수 입력
|
||||
var intField = new UTKIntegerField("수량");
|
||||
intField.Value = 10;
|
||||
intField.OnValueChanged += (value) => { };
|
||||
|
||||
// 실수 입력
|
||||
var floatField = new UTKFloatField("크기");
|
||||
floatField.Value = 1.5f;
|
||||
|
||||
// 벡터 입력
|
||||
var vector3Field = new UTKVector3Field("위치");
|
||||
vector3Field.Value = Vector3.zero;
|
||||
```
|
||||
|
||||
### UTKNumberStepper
|
||||
|
||||
증가/감소 버튼이 있는 숫자 입력 필드입니다.
|
||||
|
||||
```csharp
|
||||
var stepper = new UTKNumberStepper();
|
||||
stepper.Value = 5;
|
||||
stepper.Min = 0;
|
||||
stepper.Max = 100;
|
||||
stepper.Step = 1;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7) UXML 속성 규칙
|
||||
|
||||
Unity 6 소스 생성기 기반의 `[UxmlElement]`와 `[UxmlAttribute]`를 사용합니다.
|
||||
|
||||
```csharp
|
||||
[UxmlElement]
|
||||
public partial class UTKCustomComponent : VisualElement
|
||||
{
|
||||
[UxmlAttribute("title")]
|
||||
public string Title { get; set; }
|
||||
|
||||
[UxmlAttribute("is-enabled")]
|
||||
public bool IsEnabled { get; set; }
|
||||
|
||||
[UxmlAttribute("border-width")]
|
||||
public int BorderWidth { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
**UXML 사용:**
|
||||
```xml
|
||||
<utk:UTKCustomComponent title="제목" is-enabled="true" border-width="2" />
|
||||
```
|
||||
|
||||
**주의사항:**
|
||||
- 클래스는 반드시 `partial`로 선언
|
||||
- 속성명은 케밥 케이스(kebab-case) 소문자 사용
|
||||
- 레거시 `UxmlFactory`/`UxmlTraits` 방식 사용 금지
|
||||
|
||||
---
|
||||
|
||||
## 8) 네이밍 규칙
|
||||
|
||||
| 항목 | 규칙 | 예시 |
|
||||
|------|------|------|
|
||||
| 클래스 | UTK 접두사 + PascalCase | `UTKButton`, `UTKAccordionList` |
|
||||
| UXML 파일 | `{ComponentName}.uxml` | `UTKAccordionListWindow.uxml` |
|
||||
| USS 파일 | `{ComponentName}Uss.uss` | `UTKAccordionListWindowUss.uss` |
|
||||
| Public 멤버 | PascalCase | `OnClicked`, `Value` |
|
||||
| Private 필드 | _camelCase | `_button`, `_labelCache` |
|
||||
| 이벤트 | On 접두사 | `OnValueChanged`, `OnClicked` |
|
||||
|
||||
> **중요**: UXML과 USS 파일명을 다르게 지정해야 `Resources.Load<T>()` 충돌을 방지할 수 있습니다.
|
||||
|
||||
---
|
||||
|
||||
## 9) 메모리 관리 패턴
|
||||
|
||||
### 이벤트 구독/해제
|
||||
|
||||
```csharp
|
||||
private EventCallback<ClickEvent>? _onClick;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
_onClick = OnButtonClick;
|
||||
_button?.RegisterCallback(_onClick);
|
||||
UTKThemeManager.OnThemeChanged += OnThemeChanged;
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
_button?.UnregisterCallback(_onClick);
|
||||
UTKThemeManager.OnThemeChanged -= OnThemeChanged;
|
||||
}
|
||||
```
|
||||
|
||||
### IDisposable 패턴
|
||||
|
||||
```csharp
|
||||
public void Dispose()
|
||||
{
|
||||
UnregisterEvents();
|
||||
_cts?.Cancel();
|
||||
_cts?.Dispose();
|
||||
_cts = null;
|
||||
}
|
||||
```
|
||||
|
||||
### 체크리스트
|
||||
|
||||
- [ ] `RegisterCallback<T>` ↔ `UnregisterCallback<T>` 대칭 확인
|
||||
- [ ] `UTKThemeManager.OnThemeChanged` 구독 해제
|
||||
- [ ] `CancellationTokenSource` 정리 (`Cancel` → `Dispose`)
|
||||
- [ ] 람다/클로저 캡처로 인한 누수 점검
|
||||
- [ ] 이미지 캐시 정리 (해당 시)
|
||||
|
||||
---
|
||||
|
||||
## 10) 주요 API 요약
|
||||
|
||||
| 카테고리 | 클래스 | 주요 메서드 |
|
||||
|----------|--------|-------------|
|
||||
| **테마** | UTKThemeManager | `RegisterRoot`, `SetTheme`, `ToggleTheme` |
|
||||
| **버튼** | UTKButton | `SetMaterialIcon`, `SetImageIcon`, `OnClicked` |
|
||||
| **리스트** | UTKAccordionList | `SetData`, `Search`, `ExpandAll`, `OnItemClick` |
|
||||
| **트리** | UTKTreeView | `SetData`, `ExpandAll`, `CollapseAll` |
|
||||
| **프로퍼티** | UTKPropertyWindow | `LoadProperties`, `AddGroup`, `UpdatePropertyValue` |
|
||||
| **모달** | UTKModal | `Show`, `Close`, `AddContent`, `AddToFooter` |
|
||||
| **이미지** | UTKImage | `LoadAsync`, `SetTexture`, `ClearImage` |
|
||||
| **입력** | UTK*Field | `Value`, `OnValueChanged` |
|
||||
|
||||
---
|
||||
|
||||
## 11) 참조 리소스
|
||||
|
||||
| 경로 | 설명 |
|
||||
|------|------|
|
||||
| `Assets/Resources/UIToolkit/Style/` | USS 스타일 파일 |
|
||||
| `Assets/Resources/UIToolkit/Common/` | 공통 UXML 컴포넌트 |
|
||||
| `Assets/Resources/UIToolkit/Window/` | 윈도우 UXML 템플릿 |
|
||||
| `Assets/Resources/UIToolkit/Modal/` | 모달 UXML 템플릿 |
|
||||
| `Assets/Resources/UIToolkit/List/` | 리스트 UXML 템플릿 |
|
||||
| `Assets/Resources/UIToolkit/Property/` | 프로퍼티 UXML 템플릿 |
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c578c7c6f7f524444942296a64ad16be
|
||||
guid: 212bcd23b16ea4443a97af8619e3d333
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
@@ -83,24 +83,24 @@ namespace UVC.UIToolkit
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <!-- 내부 리소스 이미지 -->
|
||||
/// <utk:UTKImage src="UIToolkit/Images/icon_setting_22"
|
||||
/// style="width: 48px; height: 48px;" />
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <!-- 내부 리소스 이미지 -->
|
||||
/// <utk:UTKImage src="UIToolkit/Images/icon_setting_22"
|
||||
/// style="width: 48px; height: 48px;" />
|
||||
///
|
||||
/// <!-- 외부 URL 이미지 -->
|
||||
/// <utk:UTKImage src="https://example.com/image.png"
|
||||
/// <!-- 외부 URL 이미지 -->
|
||||
/// <utk:UTKImage src="https://example.com/image.png"
|
||||
/// style="width: 200px; height: 150px;"
|
||||
/// scale-mode="ScaleToFit" />
|
||||
/// scale-mode="ScaleToFit" />
|
||||
///
|
||||
/// <!-- 둥근 프로필 이미지 -->
|
||||
/// <utk:UTKImage src="https://api.example.com/avatar"
|
||||
/// class="profile-avatar" />
|
||||
/// <!-- 둥근 프로필 이미지 -->
|
||||
/// <utk:UTKImage src="https://api.example.com/avatar"
|
||||
/// class="profile-avatar" />
|
||||
///
|
||||
/// <!-- 틴트 색상 적용 -->
|
||||
/// <utk:UTKImage src="UIToolkit/Images/icon_home"
|
||||
/// tint-color="#FF5500" />
|
||||
/// </ui:UXML>
|
||||
/// <!-- 틴트 색상 적용 -->
|
||||
/// <utk:UTKImage src="UIToolkit/Images/icon_home"
|
||||
/// tint-color="#FF5500" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e911fc8aa11c95841af462e5a845974b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
@@ -18,6 +19,11 @@ namespace UVC.UIToolkit
|
||||
protected const int DEFAULT_DEBOUNCE_MS = 300;
|
||||
protected const string USS_CLASS_READONLY = "utk-property-item--readonly";
|
||||
protected const string USS_CLASS_HIDDEN = "utk-property-item--hidden";
|
||||
protected const string UXML_BASE_PATH = "UIToolkit/Property/Items/";
|
||||
#endregion
|
||||
|
||||
#region Static Cache
|
||||
private static readonly Dictionary<string, VisualTreeAsset> _uxmlCache = new();
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
@@ -243,9 +249,9 @@ namespace UVC.UIToolkit
|
||||
_rootElement.SetTooltip(_tooltip);
|
||||
}
|
||||
|
||||
protected Label CreateNameLabel()
|
||||
protected UTKLabel CreateNameLabel()
|
||||
{
|
||||
var label = new Label(Name);
|
||||
var label = new UTKLabel(Name, UTKLabel.LabelSize.Body2);
|
||||
label.AddToClassList("utk-property-item__label");
|
||||
return label;
|
||||
}
|
||||
@@ -263,6 +269,62 @@ namespace UVC.UIToolkit
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UXML 템플릿에서 UI 생성
|
||||
/// </summary>
|
||||
/// <param name="uxmlName">UXML 파일명 (확장자 제외)</param>
|
||||
/// <returns>생성된 루트 VisualElement</returns>
|
||||
protected VisualElement? CreateUIFromUxml(string uxmlName)
|
||||
{
|
||||
var asset = LoadUxmlAsset(uxmlName);
|
||||
if (asset == null)
|
||||
{
|
||||
Debug.LogWarning($"[UTKPropertyItem] UXML not found: {uxmlName}");
|
||||
return null;
|
||||
}
|
||||
|
||||
var root = asset.Instantiate();
|
||||
var itemRoot = root.Q<VisualElement>("item-root") ?? root;
|
||||
|
||||
// name-label에 Name 설정
|
||||
var nameLabel = itemRoot.Q<UTKLabel>("name-label");
|
||||
if (nameLabel != null)
|
||||
{
|
||||
nameLabel.Text = Name;
|
||||
}
|
||||
|
||||
return itemRoot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UXML 에셋 로드 (캐시 사용)
|
||||
/// </summary>
|
||||
protected static VisualTreeAsset? LoadUxmlAsset(string uxmlName)
|
||||
{
|
||||
string path = UXML_BASE_PATH + uxmlName;
|
||||
|
||||
if (_uxmlCache.TryGetValue(path, out var cached))
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
|
||||
var asset = Resources.Load<VisualTreeAsset>(path);
|
||||
if (asset != null)
|
||||
{
|
||||
_uxmlCache[path] = asset;
|
||||
}
|
||||
|
||||
return asset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UXML 캐시 클리어 (에디터 용도)
|
||||
/// </summary>
|
||||
public static void ClearUxmlCache()
|
||||
{
|
||||
_uxmlCache.Clear();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace UVC.UIToolkit
|
||||
public class UTKBoolPropertyItem : UTKPropertyItemBase<bool>
|
||||
{
|
||||
#region Fields
|
||||
private Toggle? _toggle;
|
||||
private UTKToggle? _toggle;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
@@ -26,6 +26,23 @@ namespace UVC.UIToolkit
|
||||
|
||||
#region Override Methods
|
||||
public override VisualElement CreateUI()
|
||||
{
|
||||
var container = CreateUIFromUxml("UTKBoolPropertyItem");
|
||||
if (container == null)
|
||||
{
|
||||
return CreateUIFallback();
|
||||
}
|
||||
|
||||
_toggle = container.Q<UTKToggle>("value-field");
|
||||
if (_toggle != null)
|
||||
{
|
||||
_toggle.IsOn = Value;
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement CreateUIFallback()
|
||||
{
|
||||
var container = CreateContainer();
|
||||
|
||||
@@ -35,9 +52,9 @@ namespace UVC.UIToolkit
|
||||
var valueContainer = new VisualElement();
|
||||
valueContainer.AddToClassList("utk-property-item__value");
|
||||
|
||||
_toggle = new Toggle();
|
||||
_toggle = new UTKToggle();
|
||||
_toggle.name = "value-field";
|
||||
_toggle.value = Value;
|
||||
_toggle.IsOn = Value;
|
||||
|
||||
valueContainer.Add(_toggle);
|
||||
container.Add(valueContainer);
|
||||
@@ -49,12 +66,12 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.BindUI(element);
|
||||
|
||||
_toggle = element.Q<Toggle>("value-field");
|
||||
_toggle = element.Q<UTKToggle>("value-field");
|
||||
if (_toggle != null)
|
||||
{
|
||||
_toggle.value = Value;
|
||||
_toggle.SetEnabled(!IsReadOnly);
|
||||
_toggle.RegisterValueChangedCallback(OnToggleChanged);
|
||||
_toggle.IsOn = Value;
|
||||
_toggle.IsEnabled = !IsReadOnly;
|
||||
_toggle.OnValueChanged += OnToggleChanged;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +79,7 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
if (_toggle != null)
|
||||
{
|
||||
_toggle.UnregisterValueChangedCallback(OnToggleChanged);
|
||||
_toggle.OnValueChanged -= OnToggleChanged;
|
||||
_toggle = null;
|
||||
}
|
||||
|
||||
@@ -71,9 +88,9 @@ namespace UVC.UIToolkit
|
||||
|
||||
public override void RefreshUI()
|
||||
{
|
||||
if (_toggle != null && _toggle.value != Value)
|
||||
if (_toggle != null && _toggle.IsOn != Value)
|
||||
{
|
||||
_toggle.SetValueWithoutNotify(Value);
|
||||
_toggle.SetOn(Value, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,16 +100,16 @@ namespace UVC.UIToolkit
|
||||
|
||||
if (_toggle != null)
|
||||
{
|
||||
_toggle.SetEnabled(!IsReadOnly);
|
||||
_toggle.IsEnabled = !IsReadOnly;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
private void OnToggleChanged(ChangeEvent<bool> evt)
|
||||
private void OnToggleChanged(bool newValue)
|
||||
{
|
||||
// Toggle은 디바운스 없이 즉시 적용
|
||||
Value = evt.newValue;
|
||||
Value = newValue;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
#region Fields
|
||||
private VisualElement? _colorPreview;
|
||||
private Button? _pickerButton;
|
||||
private TextField? _hexField;
|
||||
private UTKButton? _pickerButton;
|
||||
private UTKInputField? _hexField;
|
||||
private UTKColorPicker? _currentPicker;
|
||||
private bool _useAlpha;
|
||||
#endregion
|
||||
@@ -39,6 +39,31 @@ namespace UVC.UIToolkit
|
||||
|
||||
#region Override Methods
|
||||
public override VisualElement CreateUI()
|
||||
{
|
||||
var container = CreateUIFromUxml("UTKColorPropertyItem");
|
||||
if (container == null)
|
||||
{
|
||||
return CreateUIFallback();
|
||||
}
|
||||
|
||||
_colorPreview = container.Q<VisualElement>("color-preview");
|
||||
_hexField = container.Q<UTKInputField>("hex-field");
|
||||
_pickerButton = container.Q<UTKButton>("picker-btn");
|
||||
|
||||
if (_colorPreview != null)
|
||||
{
|
||||
_colorPreview.style.backgroundColor = Value;
|
||||
}
|
||||
|
||||
if (_hexField != null)
|
||||
{
|
||||
_hexField.Value = ColorToHex(Value);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement CreateUIFallback()
|
||||
{
|
||||
var container = CreateContainer();
|
||||
|
||||
@@ -56,17 +81,16 @@ namespace UVC.UIToolkit
|
||||
valueContainer.Add(_colorPreview);
|
||||
|
||||
// Hex 입력
|
||||
_hexField = new TextField();
|
||||
_hexField = new UTKInputField();
|
||||
_hexField.name = "hex-field";
|
||||
_hexField.value = ColorToHex(Value);
|
||||
_hexField.Value = ColorToHex(Value);
|
||||
_hexField.style.width = 80;
|
||||
_hexField.style.marginLeft = 5;
|
||||
valueContainer.Add(_hexField);
|
||||
|
||||
// 피커 버튼
|
||||
_pickerButton = new Button();
|
||||
_pickerButton = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
|
||||
_pickerButton.name = "picker-btn";
|
||||
_pickerButton.text = "...";
|
||||
_pickerButton.AddToClassList("utk-property-item__picker-btn");
|
||||
valueContainer.Add(_pickerButton);
|
||||
|
||||
@@ -80,8 +104,8 @@ namespace UVC.UIToolkit
|
||||
base.BindUI(element);
|
||||
|
||||
_colorPreview = element.Q<VisualElement>("color-preview");
|
||||
_hexField = element.Q<TextField>("hex-field");
|
||||
_pickerButton = element.Q<Button>("picker-btn");
|
||||
_hexField = element.Q<UTKInputField>("hex-field");
|
||||
_pickerButton = element.Q<UTKButton>("picker-btn");
|
||||
|
||||
if (_colorPreview != null)
|
||||
{
|
||||
@@ -91,15 +115,15 @@ namespace UVC.UIToolkit
|
||||
|
||||
if (_hexField != null)
|
||||
{
|
||||
_hexField.value = ColorToHex(Value);
|
||||
_hexField.Value = ColorToHex(Value);
|
||||
_hexField.SetEnabled(!IsReadOnly);
|
||||
_hexField.RegisterValueChangedCallback(OnHexChanged);
|
||||
_hexField.OnValueChanged += OnHexChanged;
|
||||
}
|
||||
|
||||
if (_pickerButton != null)
|
||||
{
|
||||
_pickerButton.SetEnabled(!IsReadOnly);
|
||||
_pickerButton.clicked += OnPickerButtonClicked;
|
||||
_pickerButton.IsEnabled = !IsReadOnly;
|
||||
_pickerButton.OnClicked += OnPickerButtonClicked;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,13 +137,13 @@ namespace UVC.UIToolkit
|
||||
|
||||
if (_hexField != null)
|
||||
{
|
||||
_hexField.UnregisterValueChangedCallback(OnHexChanged);
|
||||
_hexField.OnValueChanged -= OnHexChanged;
|
||||
_hexField = null;
|
||||
}
|
||||
|
||||
if (_pickerButton != null)
|
||||
{
|
||||
_pickerButton.clicked -= OnPickerButtonClicked;
|
||||
_pickerButton.OnClicked -= OnPickerButtonClicked;
|
||||
_pickerButton = null;
|
||||
}
|
||||
|
||||
@@ -138,9 +162,9 @@ namespace UVC.UIToolkit
|
||||
if (_hexField != null)
|
||||
{
|
||||
var hex = ColorToHex(Value);
|
||||
if (_hexField.value != hex)
|
||||
if (_hexField.Value != hex)
|
||||
{
|
||||
_hexField.SetValueWithoutNotify(hex);
|
||||
_hexField.SetValue(hex, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,7 +174,10 @@ namespace UVC.UIToolkit
|
||||
base.UpdateReadOnlyState();
|
||||
|
||||
_hexField?.SetEnabled(!IsReadOnly);
|
||||
_pickerButton?.SetEnabled(!IsReadOnly);
|
||||
if (_pickerButton != null)
|
||||
{
|
||||
_pickerButton.IsEnabled = !IsReadOnly;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -205,7 +232,7 @@ namespace UVC.UIToolkit
|
||||
|
||||
if (_hexField != null)
|
||||
{
|
||||
_hexField.SetValueWithoutNotify(ColorToHex(color));
|
||||
_hexField.SetValue(ColorToHex(color), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,9 +242,9 @@ namespace UVC.UIToolkit
|
||||
ClosePicker();
|
||||
}
|
||||
|
||||
private void OnHexChanged(ChangeEvent<string> evt)
|
||||
private void OnHexChanged(string newValue)
|
||||
{
|
||||
if (TryParseHex(evt.newValue, out Color color))
|
||||
if (TryParseHex(newValue, out Color color))
|
||||
{
|
||||
DebounceValueChange(color, 300).Forget();
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ namespace UVC.UIToolkit
|
||||
public class UTKColorStatePropertyItem : UTKPropertyItemBase<UTKColorState>
|
||||
{
|
||||
#region Fields
|
||||
private Label? _stateLabel;
|
||||
private UTKLabel? _stateLabel;
|
||||
private VisualElement? _colorPreview;
|
||||
private Button? _pickerButton;
|
||||
private UTKButton? _pickerButton;
|
||||
private UTKColorPicker? _currentPicker;
|
||||
#endregion
|
||||
|
||||
@@ -35,6 +35,31 @@ namespace UVC.UIToolkit
|
||||
|
||||
#region Override Methods
|
||||
public override VisualElement CreateUI()
|
||||
{
|
||||
var container = CreateUIFromUxml("UTKColorStatePropertyItem");
|
||||
if (container == null)
|
||||
{
|
||||
return CreateUIFallback();
|
||||
}
|
||||
|
||||
_stateLabel = container.Q<UTKLabel>("state-label");
|
||||
_colorPreview = container.Q<VisualElement>("color-preview");
|
||||
_pickerButton = container.Q<UTKButton>("picker-btn");
|
||||
|
||||
if (_stateLabel != null)
|
||||
{
|
||||
_stateLabel.Text = Value.State;
|
||||
}
|
||||
|
||||
if (_colorPreview != null)
|
||||
{
|
||||
_colorPreview.style.backgroundColor = Value.Color;
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement CreateUIFallback()
|
||||
{
|
||||
var container = CreateContainer();
|
||||
|
||||
@@ -46,7 +71,7 @@ namespace UVC.UIToolkit
|
||||
valueContainer.style.flexDirection = FlexDirection.Row;
|
||||
|
||||
// 상태 라벨
|
||||
_stateLabel = new Label(Value.State);
|
||||
_stateLabel = new UTKLabel(Value.State, UTKLabel.LabelSize.Body2);
|
||||
_stateLabel.name = "state-label";
|
||||
_stateLabel.AddToClassList("utk-property-item__state-label");
|
||||
valueContainer.Add(_stateLabel);
|
||||
@@ -59,7 +84,7 @@ namespace UVC.UIToolkit
|
||||
valueContainer.Add(_colorPreview);
|
||||
|
||||
// 피커 버튼
|
||||
_pickerButton = new Button { text = "..." };
|
||||
_pickerButton = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
|
||||
_pickerButton.name = "picker-btn";
|
||||
_pickerButton.AddToClassList("utk-property-item__picker-btn");
|
||||
valueContainer.Add(_pickerButton);
|
||||
@@ -73,13 +98,13 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.BindUI(element);
|
||||
|
||||
_stateLabel = element.Q<Label>("state-label");
|
||||
_stateLabel = element.Q<UTKLabel>("state-label");
|
||||
_colorPreview = element.Q<VisualElement>("color-preview");
|
||||
_pickerButton = element.Q<Button>("picker-btn");
|
||||
_pickerButton = element.Q<UTKButton>("picker-btn");
|
||||
|
||||
if (_stateLabel != null)
|
||||
{
|
||||
_stateLabel.text = Value.State;
|
||||
_stateLabel.Text = Value.State;
|
||||
}
|
||||
|
||||
if (_colorPreview != null)
|
||||
@@ -90,8 +115,8 @@ namespace UVC.UIToolkit
|
||||
|
||||
if (_pickerButton != null)
|
||||
{
|
||||
_pickerButton.SetEnabled(!IsReadOnly);
|
||||
_pickerButton.clicked += OnPickerButtonClicked;
|
||||
_pickerButton.IsEnabled = !IsReadOnly;
|
||||
_pickerButton.OnClicked += OnPickerButtonClicked;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +130,7 @@ namespace UVC.UIToolkit
|
||||
|
||||
if (_pickerButton != null)
|
||||
{
|
||||
_pickerButton.clicked -= OnPickerButtonClicked;
|
||||
_pickerButton.OnClicked -= OnPickerButtonClicked;
|
||||
_pickerButton = null;
|
||||
}
|
||||
|
||||
@@ -116,9 +141,9 @@ namespace UVC.UIToolkit
|
||||
|
||||
public override void RefreshUI()
|
||||
{
|
||||
if (_stateLabel != null && _stateLabel.text != Value.State)
|
||||
if (_stateLabel != null && _stateLabel.Text != Value.State)
|
||||
{
|
||||
_stateLabel.text = Value.State;
|
||||
_stateLabel.Text = Value.State;
|
||||
}
|
||||
|
||||
if (_colorPreview != null)
|
||||
@@ -130,7 +155,7 @@ namespace UVC.UIToolkit
|
||||
protected override void UpdateReadOnlyState()
|
||||
{
|
||||
base.UpdateReadOnlyState();
|
||||
_pickerButton?.SetEnabled(!IsReadOnly);
|
||||
if (_pickerButton != null) _pickerButton.IsEnabled = !IsReadOnly;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ namespace UVC.UIToolkit
|
||||
public class UTKDatePropertyItem : UTKPropertyItemBase<DateTime>
|
||||
{
|
||||
#region Fields
|
||||
private TextField? _dateField;
|
||||
private Button? _pickerButton;
|
||||
private UTKInputField? _dateField;
|
||||
private UTKButton? _pickerButton;
|
||||
private UTKDatePicker? _currentPicker;
|
||||
private string _dateFormat = "yyyy-MM-dd";
|
||||
#endregion
|
||||
@@ -41,6 +41,25 @@ namespace UVC.UIToolkit
|
||||
|
||||
#region Override Methods
|
||||
public override VisualElement CreateUI()
|
||||
{
|
||||
var container = CreateUIFromUxml("UTKDatePropertyItem");
|
||||
if (container == null)
|
||||
{
|
||||
return CreateUIFallback();
|
||||
}
|
||||
|
||||
_dateField = container.Q<UTKInputField>("date-field");
|
||||
_pickerButton = container.Q<UTKButton>("picker-btn");
|
||||
|
||||
if (_dateField != null)
|
||||
{
|
||||
_dateField.Value = Value.ToString(_dateFormat);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement CreateUIFallback()
|
||||
{
|
||||
var container = CreateContainer();
|
||||
|
||||
@@ -50,15 +69,14 @@ namespace UVC.UIToolkit
|
||||
var valueContainer = new VisualElement();
|
||||
valueContainer.AddToClassList("utk-property-item__value");
|
||||
|
||||
_dateField = new TextField();
|
||||
_dateField = new UTKInputField();
|
||||
_dateField.name = "date-field";
|
||||
_dateField.value = Value.ToString(_dateFormat);
|
||||
_dateField.Value = Value.ToString(_dateFormat);
|
||||
_dateField.style.flexGrow = 1;
|
||||
valueContainer.Add(_dateField);
|
||||
|
||||
_pickerButton = new Button();
|
||||
_pickerButton = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
|
||||
_pickerButton.name = "picker-btn";
|
||||
_pickerButton.text = "...";
|
||||
_pickerButton.AddToClassList("utk-property-item__picker-btn");
|
||||
valueContainer.Add(_pickerButton);
|
||||
|
||||
@@ -71,20 +89,20 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.BindUI(element);
|
||||
|
||||
_dateField = element.Q<TextField>("date-field");
|
||||
_pickerButton = element.Q<Button>("picker-btn");
|
||||
_dateField = element.Q<UTKInputField>("date-field");
|
||||
_pickerButton = element.Q<UTKButton>("picker-btn");
|
||||
|
||||
if (_dateField != null)
|
||||
{
|
||||
_dateField.value = Value.ToString(_dateFormat);
|
||||
_dateField.Value = Value.ToString(_dateFormat);
|
||||
_dateField.SetEnabled(!IsReadOnly);
|
||||
_dateField.RegisterValueChangedCallback(OnDateTextChanged);
|
||||
_dateField.OnValueChanged += OnDateTextChanged;
|
||||
}
|
||||
|
||||
if (_pickerButton != null)
|
||||
{
|
||||
_pickerButton.SetEnabled(!IsReadOnly);
|
||||
_pickerButton.clicked += OnPickerButtonClicked;
|
||||
_pickerButton.IsEnabled = !IsReadOnly;
|
||||
_pickerButton.OnClicked += OnPickerButtonClicked;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,13 +110,13 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
if (_dateField != null)
|
||||
{
|
||||
_dateField.UnregisterValueChangedCallback(OnDateTextChanged);
|
||||
_dateField.OnValueChanged -= OnDateTextChanged;
|
||||
_dateField = null;
|
||||
}
|
||||
|
||||
if (_pickerButton != null)
|
||||
{
|
||||
_pickerButton.clicked -= OnPickerButtonClicked;
|
||||
_pickerButton.OnClicked -= OnPickerButtonClicked;
|
||||
_pickerButton = null;
|
||||
}
|
||||
|
||||
@@ -112,9 +130,9 @@ namespace UVC.UIToolkit
|
||||
if (_dateField != null)
|
||||
{
|
||||
var formatted = Value.ToString(_dateFormat);
|
||||
if (_dateField.value != formatted)
|
||||
if (_dateField.Value != formatted)
|
||||
{
|
||||
_dateField.SetValueWithoutNotify(formatted);
|
||||
_dateField.SetValue(formatted, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,7 +142,7 @@ namespace UVC.UIToolkit
|
||||
base.UpdateReadOnlyState();
|
||||
|
||||
_dateField?.SetEnabled(!IsReadOnly);
|
||||
_pickerButton?.SetEnabled(!IsReadOnly);
|
||||
if (_pickerButton != null) _pickerButton.IsEnabled = !IsReadOnly;
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -171,9 +189,9 @@ namespace UVC.UIToolkit
|
||||
_currentPicker = null;
|
||||
}
|
||||
|
||||
private void OnDateTextChanged(ChangeEvent<string> evt)
|
||||
private void OnDateTextChanged(string newValue)
|
||||
{
|
||||
if (DateTime.TryParse(evt.newValue, out DateTime date))
|
||||
if (DateTime.TryParse(newValue, out DateTime date))
|
||||
{
|
||||
DebounceValueChange(date.Date, 500).Forget();
|
||||
}
|
||||
|
||||
@@ -11,10 +11,10 @@ namespace UVC.UIToolkit
|
||||
public class UTKDateRangePropertyItem : UTKPropertyItemBase<UTKDateRange>
|
||||
{
|
||||
#region Fields
|
||||
private TextField? _startField;
|
||||
private TextField? _endField;
|
||||
private Button? _startPickerBtn;
|
||||
private Button? _endPickerBtn;
|
||||
private UTKInputField? _startField;
|
||||
private UTKInputField? _endField;
|
||||
private UTKButton? _startPickerBtn;
|
||||
private UTKButton? _endPickerBtn;
|
||||
private UTKDatePicker? _currentPicker;
|
||||
private bool _isEditingStart;
|
||||
private string _dateFormat = "yyyy-MM-dd";
|
||||
@@ -48,6 +48,32 @@ namespace UVC.UIToolkit
|
||||
|
||||
#region Override Methods
|
||||
public override VisualElement CreateUI()
|
||||
{
|
||||
var container = CreateUIFromUxml("UTKDateRangePropertyItem");
|
||||
if (container == null)
|
||||
{
|
||||
return CreateUIFallback();
|
||||
}
|
||||
|
||||
_startField = container.Q<UTKInputField>("start-field");
|
||||
_endField = container.Q<UTKInputField>("end-field");
|
||||
_startPickerBtn = container.Q<UTKButton>("start-picker-btn");
|
||||
_endPickerBtn = container.Q<UTKButton>("end-picker-btn");
|
||||
|
||||
if (_startField != null)
|
||||
{
|
||||
_startField.Value = Value.Start.ToString(_dateFormat);
|
||||
}
|
||||
|
||||
if (_endField != null)
|
||||
{
|
||||
_endField.Value = Value.End.ToString(_dateFormat);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement CreateUIFallback()
|
||||
{
|
||||
var container = CreateContainer();
|
||||
|
||||
@@ -59,29 +85,29 @@ namespace UVC.UIToolkit
|
||||
valueContainer.style.flexDirection = FlexDirection.Row;
|
||||
|
||||
// Start field
|
||||
_startField = new TextField();
|
||||
_startField = new UTKInputField();
|
||||
_startField.name = "start-field";
|
||||
_startField.value = Value.Start.ToString(_dateFormat);
|
||||
_startField.Value = Value.Start.ToString(_dateFormat);
|
||||
_startField.style.flexGrow = 1;
|
||||
valueContainer.Add(_startField);
|
||||
|
||||
_startPickerBtn = new Button { text = "..." };
|
||||
_startPickerBtn = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
|
||||
_startPickerBtn.name = "start-picker-btn";
|
||||
_startPickerBtn.AddToClassList("utk-property-item__picker-btn");
|
||||
valueContainer.Add(_startPickerBtn);
|
||||
|
||||
var separator = new Label("~");
|
||||
var separator = new UTKLabel("~", UTKLabel.LabelSize.Body2);
|
||||
separator.AddToClassList("utk-property-item__range-separator");
|
||||
valueContainer.Add(separator);
|
||||
|
||||
// End field
|
||||
_endField = new TextField();
|
||||
_endField = new UTKInputField();
|
||||
_endField.name = "end-field";
|
||||
_endField.value = Value.End.ToString(_dateFormat);
|
||||
_endField.Value = Value.End.ToString(_dateFormat);
|
||||
_endField.style.flexGrow = 1;
|
||||
valueContainer.Add(_endField);
|
||||
|
||||
_endPickerBtn = new Button { text = "..." };
|
||||
_endPickerBtn = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
|
||||
_endPickerBtn.name = "end-picker-btn";
|
||||
_endPickerBtn.AddToClassList("utk-property-item__picker-btn");
|
||||
valueContainer.Add(_endPickerBtn);
|
||||
@@ -95,35 +121,35 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.BindUI(element);
|
||||
|
||||
_startField = element.Q<TextField>("start-field");
|
||||
_endField = element.Q<TextField>("end-field");
|
||||
_startPickerBtn = element.Q<Button>("start-picker-btn");
|
||||
_endPickerBtn = element.Q<Button>("end-picker-btn");
|
||||
_startField = element.Q<UTKInputField>("start-field");
|
||||
_endField = element.Q<UTKInputField>("end-field");
|
||||
_startPickerBtn = element.Q<UTKButton>("start-picker-btn");
|
||||
_endPickerBtn = element.Q<UTKButton>("end-picker-btn");
|
||||
|
||||
if (_startField != null)
|
||||
{
|
||||
_startField.value = Value.Start.ToString(_dateFormat);
|
||||
_startField.Value = Value.Start.ToString(_dateFormat);
|
||||
_startField.SetEnabled(!IsReadOnly);
|
||||
_startField.RegisterValueChangedCallback(OnStartTextChanged);
|
||||
_startField.OnValueChanged += OnStartTextChanged;
|
||||
}
|
||||
|
||||
if (_endField != null)
|
||||
{
|
||||
_endField.value = Value.End.ToString(_dateFormat);
|
||||
_endField.Value = Value.End.ToString(_dateFormat);
|
||||
_endField.SetEnabled(!IsReadOnly);
|
||||
_endField.RegisterValueChangedCallback(OnEndTextChanged);
|
||||
_endField.OnValueChanged += OnEndTextChanged;
|
||||
}
|
||||
|
||||
if (_startPickerBtn != null)
|
||||
{
|
||||
_startPickerBtn.SetEnabled(!IsReadOnly);
|
||||
_startPickerBtn.clicked += OnStartPickerClicked;
|
||||
_startPickerBtn.IsEnabled = !IsReadOnly;
|
||||
_startPickerBtn.OnClicked += OnStartPickerClicked;
|
||||
}
|
||||
|
||||
if (_endPickerBtn != null)
|
||||
{
|
||||
_endPickerBtn.SetEnabled(!IsReadOnly);
|
||||
_endPickerBtn.clicked += OnEndPickerClicked;
|
||||
_endPickerBtn.IsEnabled = !IsReadOnly;
|
||||
_endPickerBtn.OnClicked += OnEndPickerClicked;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,25 +157,25 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
if (_startField != null)
|
||||
{
|
||||
_startField.UnregisterValueChangedCallback(OnStartTextChanged);
|
||||
_startField.OnValueChanged -= OnStartTextChanged;
|
||||
_startField = null;
|
||||
}
|
||||
|
||||
if (_endField != null)
|
||||
{
|
||||
_endField.UnregisterValueChangedCallback(OnEndTextChanged);
|
||||
_endField.OnValueChanged -= OnEndTextChanged;
|
||||
_endField = null;
|
||||
}
|
||||
|
||||
if (_startPickerBtn != null)
|
||||
{
|
||||
_startPickerBtn.clicked -= OnStartPickerClicked;
|
||||
_startPickerBtn.OnClicked -= OnStartPickerClicked;
|
||||
_startPickerBtn = null;
|
||||
}
|
||||
|
||||
if (_endPickerBtn != null)
|
||||
{
|
||||
_endPickerBtn.clicked -= OnEndPickerClicked;
|
||||
_endPickerBtn.OnClicked -= OnEndPickerClicked;
|
||||
_endPickerBtn = null;
|
||||
}
|
||||
|
||||
@@ -162,18 +188,18 @@ namespace UVC.UIToolkit
|
||||
if (_startField != null)
|
||||
{
|
||||
var formatted = Value.Start.ToString(_dateFormat);
|
||||
if (_startField.value != formatted)
|
||||
if (_startField.Value != formatted)
|
||||
{
|
||||
_startField.SetValueWithoutNotify(formatted);
|
||||
_startField.SetValue(formatted, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (_endField != null)
|
||||
{
|
||||
var formatted = Value.End.ToString(_dateFormat);
|
||||
if (_endField.value != formatted)
|
||||
if (_endField.Value != formatted)
|
||||
{
|
||||
_endField.SetValueWithoutNotify(formatted);
|
||||
_endField.SetValue(formatted, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,8 +210,8 @@ namespace UVC.UIToolkit
|
||||
|
||||
_startField?.SetEnabled(!IsReadOnly);
|
||||
_endField?.SetEnabled(!IsReadOnly);
|
||||
_startPickerBtn?.SetEnabled(!IsReadOnly);
|
||||
_endPickerBtn?.SetEnabled(!IsReadOnly);
|
||||
if (_startPickerBtn != null) _startPickerBtn.IsEnabled = !IsReadOnly;
|
||||
if (_endPickerBtn != null) _endPickerBtn.IsEnabled = !IsReadOnly;
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -241,21 +267,21 @@ namespace UVC.UIToolkit
|
||||
|
||||
private void OnPickerClosed() => _currentPicker = null;
|
||||
|
||||
private void OnStartTextChanged(ChangeEvent<string> evt)
|
||||
private void OnStartTextChanged(string newValue)
|
||||
{
|
||||
if (DateTime.TryParse(evt.newValue, out DateTime date))
|
||||
if (DateTime.TryParse(newValue, out DateTime date))
|
||||
{
|
||||
var newValue = new UTKDateRange(date.Date, Value.End);
|
||||
DebounceValueChange(newValue, 500).Forget();
|
||||
var range = new UTKDateRange(date.Date, Value.End);
|
||||
DebounceValueChange(range, 500).Forget();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEndTextChanged(ChangeEvent<string> evt)
|
||||
private void OnEndTextChanged(string newValue)
|
||||
{
|
||||
if (DateTime.TryParse(evt.newValue, out DateTime date))
|
||||
if (DateTime.TryParse(newValue, out DateTime date))
|
||||
{
|
||||
var newValue = new UTKDateRange(Value.Start, date.Date);
|
||||
DebounceValueChange(newValue, 500).Forget();
|
||||
var range = new UTKDateRange(Value.Start, date.Date);
|
||||
DebounceValueChange(range, 500).Forget();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -11,8 +11,8 @@ namespace UVC.UIToolkit
|
||||
public class UTKDateTimePropertyItem : UTKPropertyItemBase<DateTime>
|
||||
{
|
||||
#region Fields
|
||||
private TextField? _dateTimeField;
|
||||
private Button? _pickerButton;
|
||||
private UTKInputField? _dateTimeField;
|
||||
private UTKButton? _pickerButton;
|
||||
private UTKDatePicker? _currentPicker;
|
||||
private string _dateTimeFormat = "yyyy-MM-dd HH:mm";
|
||||
#endregion
|
||||
@@ -41,6 +41,25 @@ namespace UVC.UIToolkit
|
||||
|
||||
#region Override Methods
|
||||
public override VisualElement CreateUI()
|
||||
{
|
||||
var container = CreateUIFromUxml("UTKDateTimePropertyItem");
|
||||
if (container == null)
|
||||
{
|
||||
return CreateUIFallback();
|
||||
}
|
||||
|
||||
_dateTimeField = container.Q<UTKInputField>("datetime-field");
|
||||
_pickerButton = container.Q<UTKButton>("picker-btn");
|
||||
|
||||
if (_dateTimeField != null)
|
||||
{
|
||||
_dateTimeField.Value = Value.ToString(_dateTimeFormat);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement CreateUIFallback()
|
||||
{
|
||||
var container = CreateContainer();
|
||||
|
||||
@@ -50,15 +69,14 @@ namespace UVC.UIToolkit
|
||||
var valueContainer = new VisualElement();
|
||||
valueContainer.AddToClassList("utk-property-item__value");
|
||||
|
||||
_dateTimeField = new TextField();
|
||||
_dateTimeField = new UTKInputField();
|
||||
_dateTimeField.name = "datetime-field";
|
||||
_dateTimeField.value = Value.ToString(_dateTimeFormat);
|
||||
_dateTimeField.Value = Value.ToString(_dateTimeFormat);
|
||||
_dateTimeField.style.flexGrow = 1;
|
||||
valueContainer.Add(_dateTimeField);
|
||||
|
||||
_pickerButton = new Button();
|
||||
_pickerButton = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
|
||||
_pickerButton.name = "picker-btn";
|
||||
_pickerButton.text = "...";
|
||||
_pickerButton.AddToClassList("utk-property-item__picker-btn");
|
||||
valueContainer.Add(_pickerButton);
|
||||
|
||||
@@ -71,20 +89,20 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.BindUI(element);
|
||||
|
||||
_dateTimeField = element.Q<TextField>("datetime-field");
|
||||
_pickerButton = element.Q<Button>("picker-btn");
|
||||
_dateTimeField = element.Q<UTKInputField>("datetime-field");
|
||||
_pickerButton = element.Q<UTKButton>("picker-btn");
|
||||
|
||||
if (_dateTimeField != null)
|
||||
{
|
||||
_dateTimeField.value = Value.ToString(_dateTimeFormat);
|
||||
_dateTimeField.Value = Value.ToString(_dateTimeFormat);
|
||||
_dateTimeField.SetEnabled(!IsReadOnly);
|
||||
_dateTimeField.RegisterValueChangedCallback(OnDateTimeTextChanged);
|
||||
_dateTimeField.OnValueChanged += OnDateTimeTextChanged;
|
||||
}
|
||||
|
||||
if (_pickerButton != null)
|
||||
{
|
||||
_pickerButton.SetEnabled(!IsReadOnly);
|
||||
_pickerButton.clicked += OnPickerButtonClicked;
|
||||
_pickerButton.IsEnabled = !IsReadOnly;
|
||||
_pickerButton.OnClicked += OnPickerButtonClicked;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,13 +110,13 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
if (_dateTimeField != null)
|
||||
{
|
||||
_dateTimeField.UnregisterValueChangedCallback(OnDateTimeTextChanged);
|
||||
_dateTimeField.OnValueChanged -= OnDateTimeTextChanged;
|
||||
_dateTimeField = null;
|
||||
}
|
||||
|
||||
if (_pickerButton != null)
|
||||
{
|
||||
_pickerButton.clicked -= OnPickerButtonClicked;
|
||||
_pickerButton.OnClicked -= OnPickerButtonClicked;
|
||||
_pickerButton = null;
|
||||
}
|
||||
|
||||
@@ -112,9 +130,9 @@ namespace UVC.UIToolkit
|
||||
if (_dateTimeField != null)
|
||||
{
|
||||
var formatted = Value.ToString(_dateTimeFormat);
|
||||
if (_dateTimeField.value != formatted)
|
||||
if (_dateTimeField.Value != formatted)
|
||||
{
|
||||
_dateTimeField.SetValueWithoutNotify(formatted);
|
||||
_dateTimeField.SetValue(formatted, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,7 +142,7 @@ namespace UVC.UIToolkit
|
||||
base.UpdateReadOnlyState();
|
||||
|
||||
_dateTimeField?.SetEnabled(!IsReadOnly);
|
||||
_pickerButton?.SetEnabled(!IsReadOnly);
|
||||
if (_pickerButton != null) _pickerButton.IsEnabled = !IsReadOnly;
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -171,9 +189,9 @@ namespace UVC.UIToolkit
|
||||
_currentPicker = null;
|
||||
}
|
||||
|
||||
private void OnDateTimeTextChanged(ChangeEvent<string> evt)
|
||||
private void OnDateTimeTextChanged(string newValue)
|
||||
{
|
||||
if (DateTime.TryParse(evt.newValue, out DateTime dateTime))
|
||||
if (DateTime.TryParse(newValue, out DateTime dateTime))
|
||||
{
|
||||
DebounceValueChange(dateTime, 500).Forget();
|
||||
}
|
||||
|
||||
@@ -11,10 +11,10 @@ namespace UVC.UIToolkit
|
||||
public class UTKDateTimeRangePropertyItem : UTKPropertyItemBase<UTKDateTimeRange>
|
||||
{
|
||||
#region Fields
|
||||
private TextField? _startField;
|
||||
private TextField? _endField;
|
||||
private Button? _startPickerBtn;
|
||||
private Button? _endPickerBtn;
|
||||
private UTKInputField? _startField;
|
||||
private UTKInputField? _endField;
|
||||
private UTKButton? _startPickerBtn;
|
||||
private UTKButton? _endPickerBtn;
|
||||
private UTKDatePicker? _currentPicker;
|
||||
private bool _isEditingStart;
|
||||
private string _dateTimeFormat = "yyyy-MM-dd HH:mm";
|
||||
@@ -48,6 +48,32 @@ namespace UVC.UIToolkit
|
||||
|
||||
#region Override Methods
|
||||
public override VisualElement CreateUI()
|
||||
{
|
||||
var container = CreateUIFromUxml("UTKDateTimeRangePropertyItem");
|
||||
if (container == null)
|
||||
{
|
||||
return CreateUIFallback();
|
||||
}
|
||||
|
||||
_startField = container.Q<UTKInputField>("start-field");
|
||||
_endField = container.Q<UTKInputField>("end-field");
|
||||
_startPickerBtn = container.Q<UTKButton>("start-picker-btn");
|
||||
_endPickerBtn = container.Q<UTKButton>("end-picker-btn");
|
||||
|
||||
if (_startField != null)
|
||||
{
|
||||
_startField.Value = Value.Start.ToString(_dateTimeFormat);
|
||||
}
|
||||
|
||||
if (_endField != null)
|
||||
{
|
||||
_endField.Value = Value.End.ToString(_dateTimeFormat);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement CreateUIFallback()
|
||||
{
|
||||
var container = CreateContainer();
|
||||
|
||||
@@ -59,29 +85,29 @@ namespace UVC.UIToolkit
|
||||
valueContainer.style.flexDirection = FlexDirection.Row;
|
||||
|
||||
// Start field
|
||||
_startField = new TextField();
|
||||
_startField = new UTKInputField();
|
||||
_startField.name = "start-field";
|
||||
_startField.value = Value.Start.ToString(_dateTimeFormat);
|
||||
_startField.Value = Value.Start.ToString(_dateTimeFormat);
|
||||
_startField.style.flexGrow = 1;
|
||||
valueContainer.Add(_startField);
|
||||
|
||||
_startPickerBtn = new Button { text = "..." };
|
||||
_startPickerBtn = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
|
||||
_startPickerBtn.name = "start-picker-btn";
|
||||
_startPickerBtn.AddToClassList("utk-property-item__picker-btn");
|
||||
valueContainer.Add(_startPickerBtn);
|
||||
|
||||
var separator = new Label("~");
|
||||
var separator = new UTKLabel("~", UTKLabel.LabelSize.Body2);
|
||||
separator.AddToClassList("utk-property-item__range-separator");
|
||||
valueContainer.Add(separator);
|
||||
|
||||
// End field
|
||||
_endField = new TextField();
|
||||
_endField = new UTKInputField();
|
||||
_endField.name = "end-field";
|
||||
_endField.value = Value.End.ToString(_dateTimeFormat);
|
||||
_endField.Value = Value.End.ToString(_dateTimeFormat);
|
||||
_endField.style.flexGrow = 1;
|
||||
valueContainer.Add(_endField);
|
||||
|
||||
_endPickerBtn = new Button { text = "..." };
|
||||
_endPickerBtn = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
|
||||
_endPickerBtn.name = "end-picker-btn";
|
||||
_endPickerBtn.AddToClassList("utk-property-item__picker-btn");
|
||||
valueContainer.Add(_endPickerBtn);
|
||||
@@ -95,35 +121,35 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.BindUI(element);
|
||||
|
||||
_startField = element.Q<TextField>("start-field");
|
||||
_endField = element.Q<TextField>("end-field");
|
||||
_startPickerBtn = element.Q<Button>("start-picker-btn");
|
||||
_endPickerBtn = element.Q<Button>("end-picker-btn");
|
||||
_startField = element.Q<UTKInputField>("start-field");
|
||||
_endField = element.Q<UTKInputField>("end-field");
|
||||
_startPickerBtn = element.Q<UTKButton>("start-picker-btn");
|
||||
_endPickerBtn = element.Q<UTKButton>("end-picker-btn");
|
||||
|
||||
if (_startField != null)
|
||||
{
|
||||
_startField.value = Value.Start.ToString(_dateTimeFormat);
|
||||
_startField.Value = Value.Start.ToString(_dateTimeFormat);
|
||||
_startField.SetEnabled(!IsReadOnly);
|
||||
_startField.RegisterValueChangedCallback(OnStartTextChanged);
|
||||
_startField.OnValueChanged += OnStartTextChanged;
|
||||
}
|
||||
|
||||
if (_endField != null)
|
||||
{
|
||||
_endField.value = Value.End.ToString(_dateTimeFormat);
|
||||
_endField.Value = Value.End.ToString(_dateTimeFormat);
|
||||
_endField.SetEnabled(!IsReadOnly);
|
||||
_endField.RegisterValueChangedCallback(OnEndTextChanged);
|
||||
_endField.OnValueChanged += OnEndTextChanged;
|
||||
}
|
||||
|
||||
if (_startPickerBtn != null)
|
||||
{
|
||||
_startPickerBtn.SetEnabled(!IsReadOnly);
|
||||
_startPickerBtn.clicked += OnStartPickerClicked;
|
||||
_startPickerBtn.IsEnabled = !IsReadOnly;
|
||||
_startPickerBtn.OnClicked += OnStartPickerClicked;
|
||||
}
|
||||
|
||||
if (_endPickerBtn != null)
|
||||
{
|
||||
_endPickerBtn.SetEnabled(!IsReadOnly);
|
||||
_endPickerBtn.clicked += OnEndPickerClicked;
|
||||
_endPickerBtn.IsEnabled = !IsReadOnly;
|
||||
_endPickerBtn.OnClicked += OnEndPickerClicked;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,25 +157,25 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
if (_startField != null)
|
||||
{
|
||||
_startField.UnregisterValueChangedCallback(OnStartTextChanged);
|
||||
_startField.OnValueChanged -= OnStartTextChanged;
|
||||
_startField = null;
|
||||
}
|
||||
|
||||
if (_endField != null)
|
||||
{
|
||||
_endField.UnregisterValueChangedCallback(OnEndTextChanged);
|
||||
_endField.OnValueChanged -= OnEndTextChanged;
|
||||
_endField = null;
|
||||
}
|
||||
|
||||
if (_startPickerBtn != null)
|
||||
{
|
||||
_startPickerBtn.clicked -= OnStartPickerClicked;
|
||||
_startPickerBtn.OnClicked -= OnStartPickerClicked;
|
||||
_startPickerBtn = null;
|
||||
}
|
||||
|
||||
if (_endPickerBtn != null)
|
||||
{
|
||||
_endPickerBtn.clicked -= OnEndPickerClicked;
|
||||
_endPickerBtn.OnClicked -= OnEndPickerClicked;
|
||||
_endPickerBtn = null;
|
||||
}
|
||||
|
||||
@@ -162,18 +188,18 @@ namespace UVC.UIToolkit
|
||||
if (_startField != null)
|
||||
{
|
||||
var formatted = Value.Start.ToString(_dateTimeFormat);
|
||||
if (_startField.value != formatted)
|
||||
if (_startField.Value != formatted)
|
||||
{
|
||||
_startField.SetValueWithoutNotify(formatted);
|
||||
_startField.SetValue(formatted, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (_endField != null)
|
||||
{
|
||||
var formatted = Value.End.ToString(_dateTimeFormat);
|
||||
if (_endField.value != formatted)
|
||||
if (_endField.Value != formatted)
|
||||
{
|
||||
_endField.SetValueWithoutNotify(formatted);
|
||||
_endField.SetValue(formatted, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,8 +210,8 @@ namespace UVC.UIToolkit
|
||||
|
||||
_startField?.SetEnabled(!IsReadOnly);
|
||||
_endField?.SetEnabled(!IsReadOnly);
|
||||
_startPickerBtn?.SetEnabled(!IsReadOnly);
|
||||
_endPickerBtn?.SetEnabled(!IsReadOnly);
|
||||
if (_startPickerBtn != null) _startPickerBtn.IsEnabled = !IsReadOnly;
|
||||
if (_endPickerBtn != null) _endPickerBtn.IsEnabled = !IsReadOnly;
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -241,21 +267,21 @@ namespace UVC.UIToolkit
|
||||
|
||||
private void OnPickerClosed() => _currentPicker = null;
|
||||
|
||||
private void OnStartTextChanged(ChangeEvent<string> evt)
|
||||
private void OnStartTextChanged(string newValue)
|
||||
{
|
||||
if (DateTime.TryParse(evt.newValue, out DateTime dateTime))
|
||||
if (DateTime.TryParse(newValue, out DateTime dateTime))
|
||||
{
|
||||
var newValue = new UTKDateTimeRange(dateTime, Value.End);
|
||||
DebounceValueChange(newValue, 500).Forget();
|
||||
var range = new UTKDateTimeRange(dateTime, Value.End);
|
||||
DebounceValueChange(range, 500).Forget();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEndTextChanged(ChangeEvent<string> evt)
|
||||
private void OnEndTextChanged(string newValue)
|
||||
{
|
||||
if (DateTime.TryParse(evt.newValue, out DateTime dateTime))
|
||||
if (DateTime.TryParse(newValue, out DateTime dateTime))
|
||||
{
|
||||
var newValue = new UTKDateTimeRange(Value.Start, dateTime);
|
||||
DebounceValueChange(newValue, 500).Forget();
|
||||
var range = new UTKDateTimeRange(Value.Start, dateTime);
|
||||
DebounceValueChange(range, 500).Forget();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace UVC.UIToolkit
|
||||
public class UTKDropdownPropertyItem : UTKPropertyItemBase<string>
|
||||
{
|
||||
#region Fields
|
||||
private DropdownField? _dropdown;
|
||||
private UTKDropdown? _dropdown;
|
||||
private List<string> _choices;
|
||||
#endregion
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace UVC.UIToolkit
|
||||
_choices = value ?? new List<string>();
|
||||
if (_dropdown != null)
|
||||
{
|
||||
_dropdown.choices = _choices;
|
||||
_dropdown.SetOptions(_choices);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,25 @@ namespace UVC.UIToolkit
|
||||
|
||||
#region Override Methods
|
||||
public override VisualElement CreateUI()
|
||||
{
|
||||
var container = CreateUIFromUxml("UTKDropdownPropertyItem");
|
||||
if (container == null)
|
||||
{
|
||||
return CreateUIFallback();
|
||||
}
|
||||
|
||||
_dropdown = container.Q<UTKDropdown>("dropdown-field");
|
||||
if (_dropdown != null)
|
||||
{
|
||||
_dropdown.SetOptions(_choices);
|
||||
int selectedIndex = _choices.IndexOf(Value);
|
||||
_dropdown.SelectedIndex = Math.Max(0, selectedIndex);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement CreateUIFallback()
|
||||
{
|
||||
var container = CreateContainer();
|
||||
|
||||
@@ -75,9 +94,11 @@ namespace UVC.UIToolkit
|
||||
var valueContainer = new VisualElement();
|
||||
valueContainer.AddToClassList("utk-property-item__value");
|
||||
|
||||
int selectedIndex = _choices.IndexOf(Value);
|
||||
_dropdown = new DropdownField(_choices, Math.Max(0, selectedIndex));
|
||||
_dropdown = new UTKDropdown();
|
||||
_dropdown.name = "dropdown-field";
|
||||
_dropdown.SetOptions(_choices);
|
||||
int selectedIndex = _choices.IndexOf(Value);
|
||||
_dropdown.SelectedIndex = Math.Max(0, selectedIndex);
|
||||
valueContainer.Add(_dropdown);
|
||||
|
||||
container.Add(valueContainer);
|
||||
@@ -89,15 +110,15 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.BindUI(element);
|
||||
|
||||
_dropdown = element.Q<DropdownField>("dropdown-field");
|
||||
_dropdown = element.Q<UTKDropdown>("dropdown-field");
|
||||
|
||||
if (_dropdown != null)
|
||||
{
|
||||
_dropdown.choices = _choices;
|
||||
_dropdown.SetOptions(_choices);
|
||||
int selectedIndex = _choices.IndexOf(Value);
|
||||
_dropdown.index = Math.Max(0, selectedIndex);
|
||||
_dropdown.SetEnabled(!IsReadOnly);
|
||||
_dropdown.RegisterValueChangedCallback(OnDropdownChanged);
|
||||
_dropdown.SelectedIndex = Math.Max(0, selectedIndex);
|
||||
_dropdown.IsEnabled = !IsReadOnly;
|
||||
_dropdown.OnSelectionChanged += OnDropdownChanged;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +126,7 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
if (_dropdown != null)
|
||||
{
|
||||
_dropdown.UnregisterValueChangedCallback(OnDropdownChanged);
|
||||
_dropdown.OnSelectionChanged -= OnDropdownChanged;
|
||||
_dropdown = null;
|
||||
}
|
||||
|
||||
@@ -114,9 +135,9 @@ namespace UVC.UIToolkit
|
||||
|
||||
public override void RefreshUI()
|
||||
{
|
||||
if (_dropdown != null && _dropdown.value != Value)
|
||||
if (_dropdown != null && _dropdown.SelectedValue != Value)
|
||||
{
|
||||
_dropdown.SetValueWithoutNotify(Value);
|
||||
_dropdown.SetSelectedValue(Value, notify: false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +147,7 @@ namespace UVC.UIToolkit
|
||||
|
||||
if (_dropdown != null)
|
||||
{
|
||||
_dropdown.SetEnabled(!IsReadOnly);
|
||||
_dropdown.IsEnabled = !IsReadOnly;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
@@ -140,7 +161,7 @@ namespace UVC.UIToolkit
|
||||
_choices.Add(choice);
|
||||
if (_dropdown != null)
|
||||
{
|
||||
_dropdown.choices = _choices;
|
||||
_dropdown.AddOption(choice);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,7 +172,7 @@ namespace UVC.UIToolkit
|
||||
bool removed = _choices.Remove(choice);
|
||||
if (removed && _dropdown != null)
|
||||
{
|
||||
_dropdown.choices = _choices;
|
||||
_dropdown.SetOptions(_choices);
|
||||
|
||||
// 현재 선택 값이 제거되면 첫 번째 항목 선택
|
||||
if (Value == choice && _choices.Count > 0)
|
||||
@@ -164,9 +185,9 @@ namespace UVC.UIToolkit
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
private void OnDropdownChanged(ChangeEvent<string> evt)
|
||||
private void OnDropdownChanged(int index, string newValue)
|
||||
{
|
||||
Value = evt.newValue;
|
||||
Value = newValue;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UVC.UIToolkit
|
||||
{
|
||||
/// <summary>
|
||||
/// 열거형 속성 아이템
|
||||
/// DropdownField를 사용한 열거형 선택
|
||||
/// UTKEnumDropDown을 사용한 열거형 선택
|
||||
/// </summary>
|
||||
public class UTKEnumPropertyItem : UTKPropertyItemBase<Enum>
|
||||
{
|
||||
#region Fields
|
||||
private DropdownField? _dropdown;
|
||||
private UTKEnumDropDown? _enumDropdown;
|
||||
private Type _enumType;
|
||||
private List<string> _choices = new();
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
@@ -30,12 +27,28 @@ namespace UVC.UIToolkit
|
||||
: base(id, name, initialValue ?? throw new ArgumentNullException(nameof(initialValue)))
|
||||
{
|
||||
_enumType = initialValue.GetType();
|
||||
_choices = Enum.GetNames(_enumType).ToList();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Override Methods
|
||||
public override VisualElement CreateUI()
|
||||
{
|
||||
var container = CreateUIFromUxml("UTKEnumPropertyItem");
|
||||
if (container == null)
|
||||
{
|
||||
return CreateUIFallback();
|
||||
}
|
||||
|
||||
_enumDropdown = container.Q<UTKEnumDropDown>("enum-dropdown");
|
||||
if (_enumDropdown != null)
|
||||
{
|
||||
_enumDropdown.Init(Value);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement CreateUIFallback()
|
||||
{
|
||||
var container = CreateContainer();
|
||||
|
||||
@@ -45,9 +58,10 @@ namespace UVC.UIToolkit
|
||||
var valueContainer = new VisualElement();
|
||||
valueContainer.AddToClassList("utk-property-item__value");
|
||||
|
||||
_dropdown = new DropdownField(_choices, _choices.IndexOf(Value.ToString()));
|
||||
_dropdown.name = "dropdown-field";
|
||||
valueContainer.Add(_dropdown);
|
||||
_enumDropdown = new UTKEnumDropDown();
|
||||
_enumDropdown.name = "enum-dropdown";
|
||||
_enumDropdown.Init(Value);
|
||||
valueContainer.Add(_enumDropdown);
|
||||
|
||||
container.Add(valueContainer);
|
||||
|
||||
@@ -58,23 +72,22 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.BindUI(element);
|
||||
|
||||
_dropdown = element.Q<DropdownField>("dropdown-field");
|
||||
_enumDropdown = element.Q<UTKEnumDropDown>("enum-dropdown");
|
||||
|
||||
if (_dropdown != null)
|
||||
if (_enumDropdown != null)
|
||||
{
|
||||
_dropdown.choices = _choices;
|
||||
_dropdown.index = _choices.IndexOf(Value.ToString());
|
||||
_dropdown.SetEnabled(!IsReadOnly);
|
||||
_dropdown.RegisterValueChangedCallback(OnDropdownChanged);
|
||||
_enumDropdown.Value = Value;
|
||||
_enumDropdown.IsEnabled = !IsReadOnly;
|
||||
_enumDropdown.OnValueChanged += OnEnumDropdownChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public override void UnbindUI(VisualElement element)
|
||||
{
|
||||
if (_dropdown != null)
|
||||
if (_enumDropdown != null)
|
||||
{
|
||||
_dropdown.UnregisterValueChangedCallback(OnDropdownChanged);
|
||||
_dropdown = null;
|
||||
_enumDropdown.OnValueChanged -= OnEnumDropdownChanged;
|
||||
_enumDropdown = null;
|
||||
}
|
||||
|
||||
base.UnbindUI(element);
|
||||
@@ -82,13 +95,9 @@ namespace UVC.UIToolkit
|
||||
|
||||
public override void RefreshUI()
|
||||
{
|
||||
if (_dropdown != null)
|
||||
if (_enumDropdown != null && _enumDropdown.Value != Value)
|
||||
{
|
||||
var currentValue = Value.ToString();
|
||||
if (_dropdown.value != currentValue)
|
||||
{
|
||||
_dropdown.SetValueWithoutNotify(currentValue);
|
||||
}
|
||||
_enumDropdown.Value = Value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,19 +105,19 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.UpdateReadOnlyState();
|
||||
|
||||
if (_dropdown != null)
|
||||
if (_enumDropdown != null)
|
||||
{
|
||||
_dropdown.SetEnabled(!IsReadOnly);
|
||||
_enumDropdown.IsEnabled = !IsReadOnly;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
private void OnDropdownChanged(ChangeEvent<string> evt)
|
||||
private void OnEnumDropdownChanged(Enum? newValue)
|
||||
{
|
||||
if (Enum.TryParse(_enumType, evt.newValue, out object? result) && result is Enum enumValue)
|
||||
if (newValue != null)
|
||||
{
|
||||
Value = enumValue;
|
||||
Value = newValue;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -11,8 +11,8 @@ namespace UVC.UIToolkit
|
||||
public class UTKFloatPropertyItem : UTKPropertyItemBase<float>
|
||||
{
|
||||
#region Fields
|
||||
private FloatField? _floatField;
|
||||
private Slider? _slider;
|
||||
private UTKFloatField? _floatField;
|
||||
private UTKSlider? _slider;
|
||||
private bool _useSlider;
|
||||
private float _minValue;
|
||||
private float _maxValue = 1f;
|
||||
@@ -74,6 +74,33 @@ namespace UVC.UIToolkit
|
||||
|
||||
#region Override Methods
|
||||
public override VisualElement CreateUI()
|
||||
{
|
||||
string uxmlName = _useSlider ? "UTKFloatPropertyItemSlider" : "UTKFloatPropertyItem";
|
||||
var container = CreateUIFromUxml(uxmlName);
|
||||
if (container == null)
|
||||
{
|
||||
return CreateUIFallback();
|
||||
}
|
||||
|
||||
_floatField = container.Q<UTKFloatField>("value-field");
|
||||
_slider = container.Q<UTKSlider>("slider-field");
|
||||
|
||||
if (_floatField != null)
|
||||
{
|
||||
_floatField.Value = Value;
|
||||
}
|
||||
|
||||
if (_slider != null)
|
||||
{
|
||||
_slider.lowValue = _minValue;
|
||||
_slider.highValue = _maxValue;
|
||||
_slider.Value = Value;
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement CreateUIFallback()
|
||||
{
|
||||
var container = CreateContainer();
|
||||
|
||||
@@ -90,23 +117,22 @@ namespace UVC.UIToolkit
|
||||
|
||||
if (_useSlider)
|
||||
{
|
||||
_slider = new Slider(_minValue, _maxValue);
|
||||
_slider = new UTKSlider("", _minValue, _maxValue, Value);
|
||||
_slider.name = "slider-field";
|
||||
_slider.value = Value;
|
||||
_slider.AddToClassList("utk-property-item__slider");
|
||||
valueContainer.Add(_slider);
|
||||
|
||||
_floatField = new FloatField();
|
||||
_floatField = new UTKFloatField();
|
||||
_floatField.name = "value-field";
|
||||
_floatField.value = Value;
|
||||
_floatField.Value = Value;
|
||||
_floatField.AddToClassList("utk-property-item__number-field");
|
||||
valueContainer.Add(_floatField);
|
||||
}
|
||||
else
|
||||
{
|
||||
_floatField = new FloatField();
|
||||
_floatField = new UTKFloatField();
|
||||
_floatField.name = "value-field";
|
||||
_floatField.value = Value;
|
||||
_floatField.Value = Value;
|
||||
valueContainer.Add(_floatField);
|
||||
}
|
||||
|
||||
@@ -119,23 +145,23 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.BindUI(element);
|
||||
|
||||
_floatField = element.Q<FloatField>("value-field");
|
||||
_slider = element.Q<Slider>("slider-field");
|
||||
_floatField = element.Q<UTKFloatField>("value-field");
|
||||
_slider = element.Q<UTKSlider>("slider-field");
|
||||
|
||||
if (_floatField != null)
|
||||
{
|
||||
_floatField.value = Value;
|
||||
_floatField.SetEnabled(!IsReadOnly);
|
||||
_floatField.RegisterValueChangedCallback(OnFloatFieldChanged);
|
||||
_floatField.Value = Value;
|
||||
_floatField.IsEnabled = !IsReadOnly;
|
||||
_floatField.OnValueChanged += OnFloatFieldChanged;
|
||||
}
|
||||
|
||||
if (_slider != null)
|
||||
{
|
||||
_slider.lowValue = _minValue;
|
||||
_slider.highValue = _maxValue;
|
||||
_slider.value = Value;
|
||||
_slider.SetEnabled(!IsReadOnly);
|
||||
_slider.RegisterValueChangedCallback(OnSliderChanged);
|
||||
_slider.Value = Value;
|
||||
_slider.IsEnabled = !IsReadOnly;
|
||||
_slider.OnValueChanged += OnSliderChanged;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,13 +169,13 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
if (_floatField != null)
|
||||
{
|
||||
_floatField.UnregisterValueChangedCallback(OnFloatFieldChanged);
|
||||
_floatField.OnValueChanged -= OnFloatFieldChanged;
|
||||
_floatField = null;
|
||||
}
|
||||
|
||||
if (_slider != null)
|
||||
{
|
||||
_slider.UnregisterValueChangedCallback(OnSliderChanged);
|
||||
_slider.OnValueChanged -= OnSliderChanged;
|
||||
_slider = null;
|
||||
}
|
||||
|
||||
@@ -158,12 +184,12 @@ namespace UVC.UIToolkit
|
||||
|
||||
public override void RefreshUI()
|
||||
{
|
||||
if (_floatField != null && !Mathf.Approximately(_floatField.value, Value))
|
||||
if (_floatField != null && !Mathf.Approximately(_floatField.Value, Value))
|
||||
{
|
||||
_floatField.SetValueWithoutNotify(Value);
|
||||
}
|
||||
|
||||
if (_slider != null && !Mathf.Approximately(_slider.value, Value))
|
||||
if (_slider != null && !Mathf.Approximately(_slider.Value, Value))
|
||||
{
|
||||
_slider.SetValueWithoutNotify(Value);
|
||||
}
|
||||
@@ -173,17 +199,20 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.UpdateReadOnlyState();
|
||||
|
||||
_floatField?.SetEnabled(!IsReadOnly);
|
||||
_slider?.SetEnabled(!IsReadOnly);
|
||||
if (_floatField != null)
|
||||
{
|
||||
_floatField.IsEnabled = !IsReadOnly;
|
||||
}
|
||||
if (_slider != null) _slider.IsEnabled = !IsReadOnly;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
private void OnFloatFieldChanged(ChangeEvent<float> evt)
|
||||
private void OnFloatFieldChanged(float newValue)
|
||||
{
|
||||
float clampedValue = _useSlider ? Mathf.Clamp(evt.newValue, _minValue, _maxValue) : evt.newValue;
|
||||
float clampedValue = _useSlider ? Mathf.Clamp(newValue, _minValue, _maxValue) : newValue;
|
||||
|
||||
if (_slider != null && !Mathf.Approximately(_slider.value, clampedValue))
|
||||
if (_slider != null && !Mathf.Approximately(_slider.Value, clampedValue))
|
||||
{
|
||||
_slider.SetValueWithoutNotify(clampedValue);
|
||||
}
|
||||
@@ -191,15 +220,15 @@ namespace UVC.UIToolkit
|
||||
DebounceValueChange(clampedValue, 100).Forget();
|
||||
}
|
||||
|
||||
private void OnSliderChanged(ChangeEvent<float> evt)
|
||||
private void OnSliderChanged(float newValue)
|
||||
{
|
||||
if (_floatField != null && !Mathf.Approximately(_floatField.value, evt.newValue))
|
||||
if (_floatField != null && !Mathf.Approximately(_floatField.Value, newValue))
|
||||
{
|
||||
_floatField.SetValueWithoutNotify(evt.newValue);
|
||||
_floatField.SetValueWithoutNotify(newValue);
|
||||
}
|
||||
|
||||
// 슬라이더는 빠른 반응이 필요하므로 짧은 디바운스
|
||||
DebounceValueChange(evt.newValue, 50).Forget();
|
||||
DebounceValueChange(newValue, 50).Forget();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ namespace UVC.UIToolkit
|
||||
public class UTKFloatRangePropertyItem : UTKPropertyItemBase<UTKFloatRange>
|
||||
{
|
||||
#region Fields
|
||||
private FloatField? _minField;
|
||||
private FloatField? _maxField;
|
||||
private UTKFloatField? _minField;
|
||||
private UTKFloatField? _maxField;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
@@ -33,6 +33,30 @@ namespace UVC.UIToolkit
|
||||
|
||||
#region Override Methods
|
||||
public override VisualElement CreateUI()
|
||||
{
|
||||
var container = CreateUIFromUxml("UTKFloatRangePropertyItem");
|
||||
if (container == null)
|
||||
{
|
||||
return CreateUIFallback();
|
||||
}
|
||||
|
||||
_minField = container.Q<UTKFloatField>("min-field");
|
||||
_maxField = container.Q<UTKFloatField>("max-field");
|
||||
|
||||
if (_minField != null)
|
||||
{
|
||||
_minField.Value = Value.Min;
|
||||
}
|
||||
|
||||
if (_maxField != null)
|
||||
{
|
||||
_maxField.Value = Value.Max;
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement CreateUIFallback()
|
||||
{
|
||||
var container = CreateContainer();
|
||||
|
||||
@@ -43,19 +67,19 @@ namespace UVC.UIToolkit
|
||||
valueContainer.AddToClassList("utk-property-item__value");
|
||||
valueContainer.style.flexDirection = FlexDirection.Row;
|
||||
|
||||
_minField = new FloatField();
|
||||
_minField = new UTKFloatField();
|
||||
_minField.name = "min-field";
|
||||
_minField.value = Value.Min;
|
||||
_minField.Value = Value.Min;
|
||||
_minField.style.flexGrow = 1;
|
||||
valueContainer.Add(_minField);
|
||||
|
||||
var separator = new Label("~");
|
||||
var separator = new UTKLabel("~", UTKLabel.LabelSize.Body2);
|
||||
separator.AddToClassList("utk-property-item__range-separator");
|
||||
valueContainer.Add(separator);
|
||||
|
||||
_maxField = new FloatField();
|
||||
_maxField = new UTKFloatField();
|
||||
_maxField.name = "max-field";
|
||||
_maxField.value = Value.Max;
|
||||
_maxField.Value = Value.Max;
|
||||
_maxField.style.flexGrow = 1;
|
||||
valueContainer.Add(_maxField);
|
||||
|
||||
@@ -68,21 +92,21 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.BindUI(element);
|
||||
|
||||
_minField = element.Q<FloatField>("min-field");
|
||||
_maxField = element.Q<FloatField>("max-field");
|
||||
_minField = element.Q<UTKFloatField>("min-field");
|
||||
_maxField = element.Q<UTKFloatField>("max-field");
|
||||
|
||||
if (_minField != null)
|
||||
{
|
||||
_minField.value = Value.Min;
|
||||
_minField.SetEnabled(!IsReadOnly);
|
||||
_minField.RegisterValueChangedCallback(OnMinChanged);
|
||||
_minField.Value = Value.Min;
|
||||
_minField.IsEnabled = !IsReadOnly;
|
||||
_minField.OnValueChanged += OnMinChanged;
|
||||
}
|
||||
|
||||
if (_maxField != null)
|
||||
{
|
||||
_maxField.value = Value.Max;
|
||||
_maxField.SetEnabled(!IsReadOnly);
|
||||
_maxField.RegisterValueChangedCallback(OnMaxChanged);
|
||||
_maxField.Value = Value.Max;
|
||||
_maxField.IsEnabled = !IsReadOnly;
|
||||
_maxField.OnValueChanged += OnMaxChanged;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,13 +114,13 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
if (_minField != null)
|
||||
{
|
||||
_minField.UnregisterValueChangedCallback(OnMinChanged);
|
||||
_minField.OnValueChanged -= OnMinChanged;
|
||||
_minField = null;
|
||||
}
|
||||
|
||||
if (_maxField != null)
|
||||
{
|
||||
_maxField.UnregisterValueChangedCallback(OnMaxChanged);
|
||||
_maxField.OnValueChanged -= OnMaxChanged;
|
||||
_maxField = null;
|
||||
}
|
||||
|
||||
@@ -105,12 +129,12 @@ namespace UVC.UIToolkit
|
||||
|
||||
public override void RefreshUI()
|
||||
{
|
||||
if (_minField != null && !Mathf.Approximately(_minField.value, Value.Min))
|
||||
if (_minField != null && !Mathf.Approximately(_minField.Value, Value.Min))
|
||||
{
|
||||
_minField.SetValueWithoutNotify(Value.Min);
|
||||
}
|
||||
|
||||
if (_maxField != null && !Mathf.Approximately(_maxField.value, Value.Max))
|
||||
if (_maxField != null && !Mathf.Approximately(_maxField.Value, Value.Max))
|
||||
{
|
||||
_maxField.SetValueWithoutNotify(Value.Max);
|
||||
}
|
||||
@@ -120,21 +144,21 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.UpdateReadOnlyState();
|
||||
|
||||
_minField?.SetEnabled(!IsReadOnly);
|
||||
_maxField?.SetEnabled(!IsReadOnly);
|
||||
if (_minField != null) _minField.IsEnabled = !IsReadOnly;
|
||||
if (_maxField != null) _maxField.IsEnabled = !IsReadOnly;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
private void OnMinChanged(ChangeEvent<float> evt)
|
||||
private void OnMinChanged(float newMin)
|
||||
{
|
||||
var newValue = new UTKFloatRange(evt.newValue, Value.Max);
|
||||
var newValue = new UTKFloatRange(newMin, Value.Max);
|
||||
DebounceValueChange(newValue, 100).Forget();
|
||||
}
|
||||
|
||||
private void OnMaxChanged(ChangeEvent<float> evt)
|
||||
private void OnMaxChanged(float newMax)
|
||||
{
|
||||
var newValue = new UTKFloatRange(Value.Min, evt.newValue);
|
||||
var newValue = new UTKFloatRange(Value.Min, newMax);
|
||||
DebounceValueChange(newValue, 100).Forget();
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -11,8 +11,8 @@ namespace UVC.UIToolkit
|
||||
public class UTKIntPropertyItem : UTKPropertyItemBase<int>
|
||||
{
|
||||
#region Fields
|
||||
private IntegerField? _intField;
|
||||
private SliderInt? _slider;
|
||||
private UTKIntegerField? _intField;
|
||||
private UTKSliderInt? _slider;
|
||||
private bool _useSlider;
|
||||
private int _minValue;
|
||||
private int _maxValue = 100;
|
||||
@@ -74,6 +74,33 @@ namespace UVC.UIToolkit
|
||||
|
||||
#region Override Methods
|
||||
public override VisualElement CreateUI()
|
||||
{
|
||||
string uxmlName = _useSlider ? "UTKIntPropertyItemSlider" : "UTKIntPropertyItem";
|
||||
var container = CreateUIFromUxml(uxmlName);
|
||||
if (container == null)
|
||||
{
|
||||
return CreateUIFallback();
|
||||
}
|
||||
|
||||
_intField = container.Q<UTKIntegerField>("value-field");
|
||||
_slider = container.Q<UTKSliderInt>("slider-field");
|
||||
|
||||
if (_intField != null)
|
||||
{
|
||||
_intField.Value = Value;
|
||||
}
|
||||
|
||||
if (_slider != null)
|
||||
{
|
||||
_slider.lowValue = _minValue;
|
||||
_slider.highValue = _maxValue;
|
||||
_slider.Value = Value;
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement CreateUIFallback()
|
||||
{
|
||||
var container = CreateContainer();
|
||||
|
||||
@@ -90,23 +117,22 @@ namespace UVC.UIToolkit
|
||||
|
||||
if (_useSlider)
|
||||
{
|
||||
_slider = new SliderInt(_minValue, _maxValue);
|
||||
_slider = new UTKSliderInt("", _minValue, _maxValue, Value);
|
||||
_slider.name = "slider-field";
|
||||
_slider.value = Value;
|
||||
_slider.AddToClassList("utk-property-item__slider");
|
||||
valueContainer.Add(_slider);
|
||||
|
||||
_intField = new IntegerField();
|
||||
_intField = new UTKIntegerField();
|
||||
_intField.name = "value-field";
|
||||
_intField.value = Value;
|
||||
_intField.Value = Value;
|
||||
_intField.AddToClassList("utk-property-item__number-field");
|
||||
valueContainer.Add(_intField);
|
||||
}
|
||||
else
|
||||
{
|
||||
_intField = new IntegerField();
|
||||
_intField = new UTKIntegerField();
|
||||
_intField.name = "value-field";
|
||||
_intField.value = Value;
|
||||
_intField.Value = Value;
|
||||
valueContainer.Add(_intField);
|
||||
}
|
||||
|
||||
@@ -119,23 +145,23 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.BindUI(element);
|
||||
|
||||
_intField = element.Q<IntegerField>("value-field");
|
||||
_slider = element.Q<SliderInt>("slider-field");
|
||||
_intField = element.Q<UTKIntegerField>("value-field");
|
||||
_slider = element.Q<UTKSliderInt>("slider-field");
|
||||
|
||||
if (_intField != null)
|
||||
{
|
||||
_intField.value = Value;
|
||||
_intField.SetEnabled(!IsReadOnly);
|
||||
_intField.RegisterValueChangedCallback(OnIntFieldChanged);
|
||||
_intField.Value = Value;
|
||||
_intField.IsEnabled = !IsReadOnly;
|
||||
_intField.OnValueChanged += OnIntFieldChanged;
|
||||
}
|
||||
|
||||
if (_slider != null)
|
||||
{
|
||||
_slider.lowValue = _minValue;
|
||||
_slider.highValue = _maxValue;
|
||||
_slider.value = Value;
|
||||
_slider.SetEnabled(!IsReadOnly);
|
||||
_slider.RegisterValueChangedCallback(OnSliderChanged);
|
||||
_slider.Value = Value;
|
||||
_slider.IsEnabled = !IsReadOnly;
|
||||
_slider.OnValueChanged += OnSliderChanged;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,13 +169,13 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
if (_intField != null)
|
||||
{
|
||||
_intField.UnregisterValueChangedCallback(OnIntFieldChanged);
|
||||
_intField.OnValueChanged -= OnIntFieldChanged;
|
||||
_intField = null;
|
||||
}
|
||||
|
||||
if (_slider != null)
|
||||
{
|
||||
_slider.UnregisterValueChangedCallback(OnSliderChanged);
|
||||
_slider.OnValueChanged -= OnSliderChanged;
|
||||
_slider = null;
|
||||
}
|
||||
|
||||
@@ -158,12 +184,12 @@ namespace UVC.UIToolkit
|
||||
|
||||
public override void RefreshUI()
|
||||
{
|
||||
if (_intField != null && _intField.value != Value)
|
||||
if (_intField != null && _intField.Value != Value)
|
||||
{
|
||||
_intField.SetValueWithoutNotify(Value);
|
||||
}
|
||||
|
||||
if (_slider != null && _slider.value != Value)
|
||||
if (_slider != null && _slider.Value != Value)
|
||||
{
|
||||
_slider.SetValueWithoutNotify(Value);
|
||||
}
|
||||
@@ -173,17 +199,20 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.UpdateReadOnlyState();
|
||||
|
||||
_intField?.SetEnabled(!IsReadOnly);
|
||||
_slider?.SetEnabled(!IsReadOnly);
|
||||
if (_intField != null)
|
||||
{
|
||||
_intField.IsEnabled = !IsReadOnly;
|
||||
}
|
||||
if (_slider != null) _slider.IsEnabled = !IsReadOnly;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
private void OnIntFieldChanged(ChangeEvent<int> evt)
|
||||
private void OnIntFieldChanged(int newValue)
|
||||
{
|
||||
int clampedValue = _useSlider ? Mathf.Clamp(evt.newValue, _minValue, _maxValue) : evt.newValue;
|
||||
int clampedValue = _useSlider ? Mathf.Clamp(newValue, _minValue, _maxValue) : newValue;
|
||||
|
||||
if (_slider != null && _slider.value != clampedValue)
|
||||
if (_slider != null && _slider.Value != clampedValue)
|
||||
{
|
||||
_slider.SetValueWithoutNotify(clampedValue);
|
||||
}
|
||||
@@ -191,15 +220,15 @@ namespace UVC.UIToolkit
|
||||
DebounceValueChange(clampedValue, 100).Forget();
|
||||
}
|
||||
|
||||
private void OnSliderChanged(ChangeEvent<int> evt)
|
||||
private void OnSliderChanged(int newValue)
|
||||
{
|
||||
if (_intField != null && _intField.value != evt.newValue)
|
||||
if (_intField != null && _intField.Value != newValue)
|
||||
{
|
||||
_intField.SetValueWithoutNotify(evt.newValue);
|
||||
_intField.SetValueWithoutNotify(newValue);
|
||||
}
|
||||
|
||||
// 슬라이더는 빠른 반응이 필요하므로 짧은 디바운스
|
||||
DebounceValueChange(evt.newValue, 50).Forget();
|
||||
DebounceValueChange(newValue, 50).Forget();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace UVC.UIToolkit
|
||||
public class UTKIntRangePropertyItem : UTKPropertyItemBase<UTKIntRange>
|
||||
{
|
||||
#region Fields
|
||||
private IntegerField? _minField;
|
||||
private IntegerField? _maxField;
|
||||
private UTKIntegerField? _minField;
|
||||
private UTKIntegerField? _maxField;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
@@ -32,6 +32,30 @@ namespace UVC.UIToolkit
|
||||
|
||||
#region Override Methods
|
||||
public override VisualElement CreateUI()
|
||||
{
|
||||
var container = CreateUIFromUxml("UTKIntRangePropertyItem");
|
||||
if (container == null)
|
||||
{
|
||||
return CreateUIFallback();
|
||||
}
|
||||
|
||||
_minField = container.Q<UTKIntegerField>("min-field");
|
||||
_maxField = container.Q<UTKIntegerField>("max-field");
|
||||
|
||||
if (_minField != null)
|
||||
{
|
||||
_minField.Value = Value.Min;
|
||||
}
|
||||
|
||||
if (_maxField != null)
|
||||
{
|
||||
_maxField.Value = Value.Max;
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement CreateUIFallback()
|
||||
{
|
||||
var container = CreateContainer();
|
||||
|
||||
@@ -42,19 +66,19 @@ namespace UVC.UIToolkit
|
||||
valueContainer.AddToClassList("utk-property-item__value");
|
||||
valueContainer.style.flexDirection = FlexDirection.Row;
|
||||
|
||||
_minField = new IntegerField();
|
||||
_minField = new UTKIntegerField();
|
||||
_minField.name = "min-field";
|
||||
_minField.value = Value.Min;
|
||||
_minField.Value = Value.Min;
|
||||
_minField.style.flexGrow = 1;
|
||||
valueContainer.Add(_minField);
|
||||
|
||||
var separator = new Label("~");
|
||||
var separator = new UTKLabel("~", UTKLabel.LabelSize.Body2);
|
||||
separator.AddToClassList("utk-property-item__range-separator");
|
||||
valueContainer.Add(separator);
|
||||
|
||||
_maxField = new IntegerField();
|
||||
_maxField = new UTKIntegerField();
|
||||
_maxField.name = "max-field";
|
||||
_maxField.value = Value.Max;
|
||||
_maxField.Value = Value.Max;
|
||||
_maxField.style.flexGrow = 1;
|
||||
valueContainer.Add(_maxField);
|
||||
|
||||
@@ -67,21 +91,21 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.BindUI(element);
|
||||
|
||||
_minField = element.Q<IntegerField>("min-field");
|
||||
_maxField = element.Q<IntegerField>("max-field");
|
||||
_minField = element.Q<UTKIntegerField>("min-field");
|
||||
_maxField = element.Q<UTKIntegerField>("max-field");
|
||||
|
||||
if (_minField != null)
|
||||
{
|
||||
_minField.value = Value.Min;
|
||||
_minField.SetEnabled(!IsReadOnly);
|
||||
_minField.RegisterValueChangedCallback(OnMinChanged);
|
||||
_minField.Value = Value.Min;
|
||||
_minField.IsEnabled = !IsReadOnly;
|
||||
_minField.OnValueChanged += OnMinChanged;
|
||||
}
|
||||
|
||||
if (_maxField != null)
|
||||
{
|
||||
_maxField.value = Value.Max;
|
||||
_maxField.SetEnabled(!IsReadOnly);
|
||||
_maxField.RegisterValueChangedCallback(OnMaxChanged);
|
||||
_maxField.Value = Value.Max;
|
||||
_maxField.IsEnabled = !IsReadOnly;
|
||||
_maxField.OnValueChanged += OnMaxChanged;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,13 +113,13 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
if (_minField != null)
|
||||
{
|
||||
_minField.UnregisterValueChangedCallback(OnMinChanged);
|
||||
_minField.OnValueChanged -= OnMinChanged;
|
||||
_minField = null;
|
||||
}
|
||||
|
||||
if (_maxField != null)
|
||||
{
|
||||
_maxField.UnregisterValueChangedCallback(OnMaxChanged);
|
||||
_maxField.OnValueChanged -= OnMaxChanged;
|
||||
_maxField = null;
|
||||
}
|
||||
|
||||
@@ -104,12 +128,12 @@ namespace UVC.UIToolkit
|
||||
|
||||
public override void RefreshUI()
|
||||
{
|
||||
if (_minField != null && _minField.value != Value.Min)
|
||||
if (_minField != null && _minField.Value != Value.Min)
|
||||
{
|
||||
_minField.SetValueWithoutNotify(Value.Min);
|
||||
}
|
||||
|
||||
if (_maxField != null && _maxField.value != Value.Max)
|
||||
if (_maxField != null && _maxField.Value != Value.Max)
|
||||
{
|
||||
_maxField.SetValueWithoutNotify(Value.Max);
|
||||
}
|
||||
@@ -119,21 +143,21 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.UpdateReadOnlyState();
|
||||
|
||||
_minField?.SetEnabled(!IsReadOnly);
|
||||
_maxField?.SetEnabled(!IsReadOnly);
|
||||
if (_minField != null) _minField.IsEnabled = !IsReadOnly;
|
||||
if (_maxField != null) _maxField.IsEnabled = !IsReadOnly;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
private void OnMinChanged(ChangeEvent<int> evt)
|
||||
private void OnMinChanged(int newMin)
|
||||
{
|
||||
var newValue = new UTKIntRange(evt.newValue, Value.Max);
|
||||
var newValue = new UTKIntRange(newMin, Value.Max);
|
||||
DebounceValueChange(newValue, 100).Forget();
|
||||
}
|
||||
|
||||
private void OnMaxChanged(ChangeEvent<int> evt)
|
||||
private void OnMaxChanged(int newMax)
|
||||
{
|
||||
var newValue = new UTKIntRange(Value.Min, evt.newValue);
|
||||
var newValue = new UTKIntRange(Value.Min, newMax);
|
||||
DebounceValueChange(newValue, 100).Forget();
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -8,12 +8,13 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
/// <summary>
|
||||
/// 라디오 그룹 속성 아이템
|
||||
/// RadioButton 그룹으로 단일 선택
|
||||
/// UTKRadioButton 그룹으로 단일 선택
|
||||
/// </summary>
|
||||
public class UTKRadioPropertyItem : UTKPropertyItemBase<int>
|
||||
{
|
||||
#region Fields
|
||||
private RadioButtonGroup? _radioGroup;
|
||||
private VisualElement? _radioContainer;
|
||||
private List<UTKRadioButton> _radioButtons = new();
|
||||
private List<string> _choices;
|
||||
#endregion
|
||||
|
||||
@@ -27,10 +28,7 @@ namespace UVC.UIToolkit
|
||||
set
|
||||
{
|
||||
_choices = value ?? new List<string>();
|
||||
if (_radioGroup != null)
|
||||
{
|
||||
_radioGroup.choices = _choices;
|
||||
}
|
||||
RebuildRadioButtons();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +61,23 @@ namespace UVC.UIToolkit
|
||||
|
||||
#region Override Methods
|
||||
public override VisualElement CreateUI()
|
||||
{
|
||||
var container = CreateUIFromUxml("UTKRadioPropertyItem");
|
||||
if (container == null)
|
||||
{
|
||||
return CreateUIFallback();
|
||||
}
|
||||
|
||||
_radioContainer = container.Q<VisualElement>("radio-container");
|
||||
if (_radioContainer != null)
|
||||
{
|
||||
CreateRadioButtons();
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement CreateUIFallback()
|
||||
{
|
||||
var container = CreateContainer();
|
||||
|
||||
@@ -72,11 +87,12 @@ namespace UVC.UIToolkit
|
||||
var valueContainer = new VisualElement();
|
||||
valueContainer.AddToClassList("utk-property-item__value");
|
||||
|
||||
_radioGroup = new RadioButtonGroup();
|
||||
_radioGroup.name = "radio-group";
|
||||
_radioGroup.choices = _choices;
|
||||
_radioGroup.value = Value;
|
||||
valueContainer.Add(_radioGroup);
|
||||
_radioContainer = new VisualElement();
|
||||
_radioContainer.name = "radio-container";
|
||||
_radioContainer.AddToClassList("utk-property-item__radio-container");
|
||||
valueContainer.Add(_radioContainer);
|
||||
|
||||
CreateRadioButtons();
|
||||
|
||||
container.Add(valueContainer);
|
||||
|
||||
@@ -87,43 +103,57 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.BindUI(element);
|
||||
|
||||
_radioGroup = element.Q<RadioButtonGroup>("radio-group");
|
||||
_radioContainer = element.Q<VisualElement>("radio-container");
|
||||
|
||||
if (_radioGroup != null)
|
||||
if (_radioContainer != null)
|
||||
{
|
||||
_radioGroup.choices = _choices;
|
||||
_radioGroup.value = Value;
|
||||
_radioGroup.SetEnabled(!IsReadOnly);
|
||||
_radioGroup.RegisterValueChangedCallback(OnRadioChanged);
|
||||
// 기존 라디오 버튼 찾기 또는 새로 생성
|
||||
_radioButtons.Clear();
|
||||
var existingButtons = _radioContainer.Query<UTKRadioButton>().ToList();
|
||||
|
||||
if (existingButtons.Count == _choices.Count)
|
||||
{
|
||||
// 기존 버튼 재사용
|
||||
for (int i = 0; i < existingButtons.Count; i++)
|
||||
{
|
||||
var radio = existingButtons[i];
|
||||
_radioButtons.Add(radio);
|
||||
radio.IsEnabled = !IsReadOnly;
|
||||
|
||||
int index = i;
|
||||
radio.OnValueChanged += (isChecked) => OnRadioChanged(index, isChecked);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 새로 생성
|
||||
CreateRadioButtons();
|
||||
}
|
||||
|
||||
UpdateSelection();
|
||||
}
|
||||
}
|
||||
|
||||
public override void UnbindUI(VisualElement element)
|
||||
{
|
||||
if (_radioGroup != null)
|
||||
{
|
||||
_radioGroup.UnregisterValueChangedCallback(OnRadioChanged);
|
||||
_radioGroup = null;
|
||||
}
|
||||
_radioButtons.Clear();
|
||||
_radioContainer = null;
|
||||
|
||||
base.UnbindUI(element);
|
||||
}
|
||||
|
||||
public override void RefreshUI()
|
||||
{
|
||||
if (_radioGroup != null && _radioGroup.value != Value)
|
||||
{
|
||||
_radioGroup.SetValueWithoutNotify(Value);
|
||||
}
|
||||
UpdateSelection();
|
||||
}
|
||||
|
||||
protected override void UpdateReadOnlyState()
|
||||
{
|
||||
base.UpdateReadOnlyState();
|
||||
|
||||
if (_radioGroup != null)
|
||||
foreach (var radio in _radioButtons)
|
||||
{
|
||||
_radioGroup.SetEnabled(!IsReadOnly);
|
||||
radio.IsEnabled = !IsReadOnly;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
@@ -141,9 +171,60 @@ namespace UVC.UIToolkit
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
private void OnRadioChanged(ChangeEvent<int> evt)
|
||||
private void CreateRadioButtons()
|
||||
{
|
||||
Value = evt.newValue;
|
||||
if (_radioContainer == null) return;
|
||||
|
||||
_radioContainer.Clear();
|
||||
_radioButtons.Clear();
|
||||
|
||||
for (int i = 0; i < _choices.Count; i++)
|
||||
{
|
||||
var radio = new UTKRadioButton(_choices[i]);
|
||||
radio.name = $"radio-{i}";
|
||||
radio.AddToClassList("utk-property-item__radio");
|
||||
|
||||
int index = i;
|
||||
radio.OnValueChanged += (isChecked) => OnRadioChanged(index, isChecked);
|
||||
|
||||
_radioButtons.Add(radio);
|
||||
_radioContainer.Add(radio);
|
||||
}
|
||||
|
||||
UpdateSelection();
|
||||
}
|
||||
|
||||
private void RebuildRadioButtons()
|
||||
{
|
||||
if (_radioContainer != null)
|
||||
{
|
||||
CreateRadioButtons();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSelection()
|
||||
{
|
||||
for (int i = 0; i < _radioButtons.Count; i++)
|
||||
{
|
||||
_radioButtons[i].SetChecked(i == Value, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRadioChanged(int index, bool isChecked)
|
||||
{
|
||||
if (isChecked && index != Value)
|
||||
{
|
||||
// 다른 라디오 버튼 해제
|
||||
for (int i = 0; i < _radioButtons.Count; i++)
|
||||
{
|
||||
if (i != index)
|
||||
{
|
||||
_radioButtons[i].SetChecked(false, false);
|
||||
}
|
||||
}
|
||||
|
||||
Value = index;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace UVC.UIToolkit
|
||||
public class UTKStringPropertyItem : UTKPropertyItemBase<string>
|
||||
{
|
||||
#region Fields
|
||||
private TextField? _textField;
|
||||
private UTKInputField? _inputField;
|
||||
private bool _isMultiline;
|
||||
private int _maxLength;
|
||||
#endregion
|
||||
@@ -25,9 +25,9 @@ namespace UVC.UIToolkit
|
||||
set
|
||||
{
|
||||
_isMultiline = value;
|
||||
if (_textField != null)
|
||||
if (_inputField != null)
|
||||
{
|
||||
_textField.multiline = value;
|
||||
_inputField.multiline = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,9 +39,9 @@ namespace UVC.UIToolkit
|
||||
set
|
||||
{
|
||||
_maxLength = value;
|
||||
if (_textField != null)
|
||||
if (_inputField != null)
|
||||
{
|
||||
_textField.maxLength = value;
|
||||
_inputField.maxLength = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,28 @@ namespace UVC.UIToolkit
|
||||
|
||||
#region Override Methods
|
||||
public override VisualElement CreateUI()
|
||||
{
|
||||
var container = CreateUIFromUxml("UTKStringPropertyItem");
|
||||
if (container == null)
|
||||
{
|
||||
return CreateUIFallback();
|
||||
}
|
||||
|
||||
_inputField = container.Q<UTKInputField>("value-field");
|
||||
if (_inputField != null)
|
||||
{
|
||||
_inputField.Value = Value;
|
||||
_inputField.multiline = _isMultiline;
|
||||
if (_maxLength > 0)
|
||||
{
|
||||
_inputField.maxLength = _maxLength;
|
||||
}
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement CreateUIFallback()
|
||||
{
|
||||
var container = CreateContainer();
|
||||
|
||||
@@ -65,16 +87,16 @@ namespace UVC.UIToolkit
|
||||
var valueContainer = new VisualElement();
|
||||
valueContainer.AddToClassList("utk-property-item__value");
|
||||
|
||||
_textField = new TextField();
|
||||
_textField.name = "value-field";
|
||||
_textField.value = Value;
|
||||
_textField.multiline = _isMultiline;
|
||||
_inputField = new UTKInputField();
|
||||
_inputField.name = "value-field";
|
||||
_inputField.Value = Value;
|
||||
_inputField.multiline = _isMultiline;
|
||||
if (_maxLength > 0)
|
||||
{
|
||||
_textField.maxLength = _maxLength;
|
||||
_inputField.maxLength = _maxLength;
|
||||
}
|
||||
|
||||
valueContainer.Add(_textField);
|
||||
valueContainer.Add(_inputField);
|
||||
container.Add(valueContainer);
|
||||
|
||||
return container;
|
||||
@@ -84,21 +106,21 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.BindUI(element);
|
||||
|
||||
_textField = element.Q<TextField>("value-field");
|
||||
if (_textField != null)
|
||||
_inputField = element.Q<UTKInputField>("value-field");
|
||||
if (_inputField != null)
|
||||
{
|
||||
_textField.value = Value;
|
||||
_textField.SetEnabled(!IsReadOnly);
|
||||
_textField.RegisterValueChangedCallback(OnTextChanged);
|
||||
_inputField.Value = Value;
|
||||
_inputField.SetEnabled(!IsReadOnly);
|
||||
_inputField.OnValueChanged += OnTextChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public override void UnbindUI(VisualElement element)
|
||||
{
|
||||
if (_textField != null)
|
||||
if (_inputField != null)
|
||||
{
|
||||
_textField.UnregisterValueChangedCallback(OnTextChanged);
|
||||
_textField = null;
|
||||
_inputField.OnValueChanged -= OnTextChanged;
|
||||
_inputField = null;
|
||||
}
|
||||
|
||||
base.UnbindUI(element);
|
||||
@@ -106,9 +128,9 @@ namespace UVC.UIToolkit
|
||||
|
||||
public override void RefreshUI()
|
||||
{
|
||||
if (_textField != null && _textField.value != Value)
|
||||
if (_inputField != null && _inputField.Value != Value)
|
||||
{
|
||||
_textField.SetValueWithoutNotify(Value);
|
||||
_inputField.SetValue(Value, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,17 +138,17 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.UpdateReadOnlyState();
|
||||
|
||||
if (_textField != null)
|
||||
if (_inputField != null)
|
||||
{
|
||||
_textField.SetEnabled(!IsReadOnly);
|
||||
_inputField.SetEnabled(!IsReadOnly);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
private void OnTextChanged(ChangeEvent<string> evt)
|
||||
private void OnTextChanged(string newValue)
|
||||
{
|
||||
DebounceValueChange(evt.newValue).Forget();
|
||||
DebounceValueChange(newValue).Forget();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -6,13 +6,12 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
/// <summary>
|
||||
/// 2D 벡터 속성 아이템
|
||||
/// X, Y 두 개의 FloatField 사용
|
||||
/// UTKVector2Field 사용
|
||||
/// </summary>
|
||||
public class UTKVector2PropertyItem : UTKPropertyItemBase<Vector2>
|
||||
{
|
||||
#region Fields
|
||||
private FloatField? _xField;
|
||||
private FloatField? _yField;
|
||||
private UTKVector2Field? _vectorField;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
@@ -28,6 +27,24 @@ namespace UVC.UIToolkit
|
||||
|
||||
#region Override Methods
|
||||
public override VisualElement CreateUI()
|
||||
{
|
||||
var container = CreateUIFromUxml("UTKVector2PropertyItem");
|
||||
if (container == null)
|
||||
{
|
||||
return CreateUIFallback();
|
||||
}
|
||||
|
||||
_vectorField = container.Q<UTKVector2Field>("value-field");
|
||||
if (_vectorField != null)
|
||||
{
|
||||
_vectorField.Value = Value;
|
||||
_vectorField.label = "";
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement CreateUIFallback()
|
||||
{
|
||||
var container = CreateContainer();
|
||||
|
||||
@@ -37,41 +54,12 @@ namespace UVC.UIToolkit
|
||||
var valueContainer = new VisualElement();
|
||||
valueContainer.AddToClassList("utk-property-item__value");
|
||||
|
||||
// X 필드
|
||||
var xContainer = new VisualElement();
|
||||
xContainer.AddToClassList("utk-property-item__vector-field");
|
||||
xContainer.style.flexDirection = FlexDirection.Row;
|
||||
|
||||
var xLabel = new Label("X");
|
||||
xLabel.AddToClassList("utk-property-item__vector-label");
|
||||
xLabel.AddToClassList("utk-vector-label--x");
|
||||
|
||||
_xField = new FloatField();
|
||||
_xField.name = "x-field";
|
||||
_xField.value = Value.x;
|
||||
_xField.style.flexGrow = 1;
|
||||
|
||||
xContainer.Add(xLabel);
|
||||
xContainer.Add(_xField);
|
||||
valueContainer.Add(xContainer);
|
||||
|
||||
// Y 필드
|
||||
var yContainer = new VisualElement();
|
||||
yContainer.AddToClassList("utk-property-item__vector-field");
|
||||
yContainer.style.flexDirection = FlexDirection.Row;
|
||||
|
||||
var yLabel = new Label("Y");
|
||||
yLabel.AddToClassList("utk-property-item__vector-label");
|
||||
yLabel.AddToClassList("utk-vector-label--y");
|
||||
|
||||
_yField = new FloatField();
|
||||
_yField.name = "y-field";
|
||||
_yField.value = Value.y;
|
||||
_yField.style.flexGrow = 1;
|
||||
|
||||
yContainer.Add(yLabel);
|
||||
yContainer.Add(_yField);
|
||||
valueContainer.Add(yContainer);
|
||||
_vectorField = new UTKVector2Field();
|
||||
_vectorField.name = "value-field";
|
||||
_vectorField.label = "";
|
||||
_vectorField.Value = Value;
|
||||
_vectorField.AddToClassList("utk-property-item__vector2-field");
|
||||
valueContainer.Add(_vectorField);
|
||||
|
||||
container.Add(valueContainer);
|
||||
|
||||
@@ -82,36 +70,23 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.BindUI(element);
|
||||
|
||||
_xField = element.Q<FloatField>("x-field");
|
||||
_yField = element.Q<FloatField>("y-field");
|
||||
_vectorField = element.Q<UTKVector2Field>("value-field");
|
||||
|
||||
if (_xField != null)
|
||||
if (_vectorField != null)
|
||||
{
|
||||
_xField.value = Value.x;
|
||||
_xField.SetEnabled(!IsReadOnly);
|
||||
_xField.RegisterValueChangedCallback(OnXChanged);
|
||||
}
|
||||
|
||||
if (_yField != null)
|
||||
{
|
||||
_yField.value = Value.y;
|
||||
_yField.SetEnabled(!IsReadOnly);
|
||||
_yField.RegisterValueChangedCallback(OnYChanged);
|
||||
_vectorField.Value = Value;
|
||||
_vectorField.label = "";
|
||||
_vectorField.IsEnabled = !IsReadOnly;
|
||||
_vectorField.OnValueChanged += OnVectorChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public override void UnbindUI(VisualElement element)
|
||||
{
|
||||
if (_xField != null)
|
||||
if (_vectorField != null)
|
||||
{
|
||||
_xField.UnregisterValueChangedCallback(OnXChanged);
|
||||
_xField = null;
|
||||
}
|
||||
|
||||
if (_yField != null)
|
||||
{
|
||||
_yField.UnregisterValueChangedCallback(OnYChanged);
|
||||
_yField = null;
|
||||
_vectorField.OnValueChanged -= OnVectorChanged;
|
||||
_vectorField = null;
|
||||
}
|
||||
|
||||
base.UnbindUI(element);
|
||||
@@ -119,14 +94,9 @@ namespace UVC.UIToolkit
|
||||
|
||||
public override void RefreshUI()
|
||||
{
|
||||
if (_xField != null && !Mathf.Approximately(_xField.value, Value.x))
|
||||
if (_vectorField != null && _vectorField.Value != Value)
|
||||
{
|
||||
_xField.SetValueWithoutNotify(Value.x);
|
||||
}
|
||||
|
||||
if (_yField != null && !Mathf.Approximately(_yField.value, Value.y))
|
||||
{
|
||||
_yField.SetValueWithoutNotify(Value.y);
|
||||
_vectorField.SetValueWithoutNotify(Value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,21 +104,16 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.UpdateReadOnlyState();
|
||||
|
||||
_xField?.SetEnabled(!IsReadOnly);
|
||||
_yField?.SetEnabled(!IsReadOnly);
|
||||
if (_vectorField != null)
|
||||
{
|
||||
_vectorField.IsEnabled = !IsReadOnly;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
private void OnXChanged(ChangeEvent<float> evt)
|
||||
private void OnVectorChanged(Vector2 newValue)
|
||||
{
|
||||
var newValue = new Vector2(evt.newValue, Value.y);
|
||||
DebounceValueChange(newValue, 100).Forget();
|
||||
}
|
||||
|
||||
private void OnYChanged(ChangeEvent<float> evt)
|
||||
{
|
||||
var newValue = new Vector2(Value.x, evt.newValue);
|
||||
DebounceValueChange(newValue, 100).Forget();
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -6,14 +6,12 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
/// <summary>
|
||||
/// 3D 벡터 속성 아이템
|
||||
/// X, Y, Z 세 개의 FloatField 사용
|
||||
/// UTKVector3Field 사용
|
||||
/// </summary>
|
||||
public class UTKVector3PropertyItem : UTKPropertyItemBase<Vector3>
|
||||
{
|
||||
#region Fields
|
||||
private FloatField? _xField;
|
||||
private FloatField? _yField;
|
||||
private FloatField? _zField;
|
||||
private UTKVector3Field? _vectorField;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
@@ -29,6 +27,24 @@ namespace UVC.UIToolkit
|
||||
|
||||
#region Override Methods
|
||||
public override VisualElement CreateUI()
|
||||
{
|
||||
var container = CreateUIFromUxml("UTKVector3PropertyItem");
|
||||
if (container == null)
|
||||
{
|
||||
return CreateUIFallback();
|
||||
}
|
||||
|
||||
_vectorField = container.Q<UTKVector3Field>("value-field");
|
||||
if (_vectorField != null)
|
||||
{
|
||||
_vectorField.Value = Value;
|
||||
_vectorField.label = "";
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private VisualElement CreateUIFallback()
|
||||
{
|
||||
var container = CreateContainer();
|
||||
|
||||
@@ -38,59 +54,12 @@ namespace UVC.UIToolkit
|
||||
var valueContainer = new VisualElement();
|
||||
valueContainer.AddToClassList("utk-property-item__value");
|
||||
|
||||
// X 필드
|
||||
var xContainer = new VisualElement();
|
||||
xContainer.AddToClassList("utk-property-item__vector-field");
|
||||
xContainer.style.flexDirection = FlexDirection.Row;
|
||||
|
||||
var xLabel = new Label("X");
|
||||
xLabel.AddToClassList("utk-property-item__vector-label");
|
||||
xLabel.AddToClassList("utk-vector-label--x");
|
||||
|
||||
_xField = new FloatField();
|
||||
_xField.name = "x-field";
|
||||
_xField.value = Value.x;
|
||||
_xField.style.flexGrow = 1;
|
||||
|
||||
xContainer.Add(xLabel);
|
||||
xContainer.Add(_xField);
|
||||
valueContainer.Add(xContainer);
|
||||
|
||||
// Y 필드
|
||||
var yContainer = new VisualElement();
|
||||
yContainer.AddToClassList("utk-property-item__vector-field");
|
||||
yContainer.style.flexDirection = FlexDirection.Row;
|
||||
|
||||
var yLabel = new Label("Y");
|
||||
yLabel.AddToClassList("utk-property-item__vector-label");
|
||||
yLabel.AddToClassList("utk-vector-label--y");
|
||||
|
||||
_yField = new FloatField();
|
||||
_yField.name = "y-field";
|
||||
_yField.value = Value.y;
|
||||
_yField.style.flexGrow = 1;
|
||||
|
||||
yContainer.Add(yLabel);
|
||||
yContainer.Add(_yField);
|
||||
valueContainer.Add(yContainer);
|
||||
|
||||
// Z 필드
|
||||
var zContainer = new VisualElement();
|
||||
zContainer.AddToClassList("utk-property-item__vector-field");
|
||||
zContainer.style.flexDirection = FlexDirection.Row;
|
||||
|
||||
var zLabel = new Label("Z");
|
||||
zLabel.AddToClassList("utk-property-item__vector-label");
|
||||
zLabel.AddToClassList("utk-vector-label--z");
|
||||
|
||||
_zField = new FloatField();
|
||||
_zField.name = "z-field";
|
||||
_zField.value = Value.z;
|
||||
_zField.style.flexGrow = 1;
|
||||
|
||||
zContainer.Add(zLabel);
|
||||
zContainer.Add(_zField);
|
||||
valueContainer.Add(zContainer);
|
||||
_vectorField = new UTKVector3Field();
|
||||
_vectorField.name = "value-field";
|
||||
_vectorField.label = "";
|
||||
_vectorField.Value = Value;
|
||||
_vectorField.AddToClassList("utk-property-item__vector3-field");
|
||||
valueContainer.Add(_vectorField);
|
||||
|
||||
container.Add(valueContainer);
|
||||
|
||||
@@ -101,50 +70,23 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.BindUI(element);
|
||||
|
||||
_xField = element.Q<FloatField>("x-field");
|
||||
_yField = element.Q<FloatField>("y-field");
|
||||
_zField = element.Q<FloatField>("z-field");
|
||||
_vectorField = element.Q<UTKVector3Field>("value-field");
|
||||
|
||||
if (_xField != null)
|
||||
if (_vectorField != null)
|
||||
{
|
||||
_xField.value = Value.x;
|
||||
_xField.SetEnabled(!IsReadOnly);
|
||||
_xField.RegisterValueChangedCallback(OnXChanged);
|
||||
}
|
||||
|
||||
if (_yField != null)
|
||||
{
|
||||
_yField.value = Value.y;
|
||||
_yField.SetEnabled(!IsReadOnly);
|
||||
_yField.RegisterValueChangedCallback(OnYChanged);
|
||||
}
|
||||
|
||||
if (_zField != null)
|
||||
{
|
||||
_zField.value = Value.z;
|
||||
_zField.SetEnabled(!IsReadOnly);
|
||||
_zField.RegisterValueChangedCallback(OnZChanged);
|
||||
_vectorField.Value = Value;
|
||||
_vectorField.label = "";
|
||||
_vectorField.IsEnabled = !IsReadOnly;
|
||||
_vectorField.OnValueChanged += OnVectorChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public override void UnbindUI(VisualElement element)
|
||||
{
|
||||
if (_xField != null)
|
||||
if (_vectorField != null)
|
||||
{
|
||||
_xField.UnregisterValueChangedCallback(OnXChanged);
|
||||
_xField = null;
|
||||
}
|
||||
|
||||
if (_yField != null)
|
||||
{
|
||||
_yField.UnregisterValueChangedCallback(OnYChanged);
|
||||
_yField = null;
|
||||
}
|
||||
|
||||
if (_zField != null)
|
||||
{
|
||||
_zField.UnregisterValueChangedCallback(OnZChanged);
|
||||
_zField = null;
|
||||
_vectorField.OnValueChanged -= OnVectorChanged;
|
||||
_vectorField = null;
|
||||
}
|
||||
|
||||
base.UnbindUI(element);
|
||||
@@ -152,19 +94,9 @@ namespace UVC.UIToolkit
|
||||
|
||||
public override void RefreshUI()
|
||||
{
|
||||
if (_xField != null && !Mathf.Approximately(_xField.value, Value.x))
|
||||
if (_vectorField != null && _vectorField.Value != Value)
|
||||
{
|
||||
_xField.SetValueWithoutNotify(Value.x);
|
||||
}
|
||||
|
||||
if (_yField != null && !Mathf.Approximately(_yField.value, Value.y))
|
||||
{
|
||||
_yField.SetValueWithoutNotify(Value.y);
|
||||
}
|
||||
|
||||
if (_zField != null && !Mathf.Approximately(_zField.value, Value.z))
|
||||
{
|
||||
_zField.SetValueWithoutNotify(Value.z);
|
||||
_vectorField.SetValueWithoutNotify(Value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,28 +104,16 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
base.UpdateReadOnlyState();
|
||||
|
||||
_xField?.SetEnabled(!IsReadOnly);
|
||||
_yField?.SetEnabled(!IsReadOnly);
|
||||
_zField?.SetEnabled(!IsReadOnly);
|
||||
if (_vectorField != null)
|
||||
{
|
||||
_vectorField.IsEnabled = !IsReadOnly;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
private void OnXChanged(ChangeEvent<float> evt)
|
||||
private void OnVectorChanged(Vector3 newValue)
|
||||
{
|
||||
var newValue = new Vector3(evt.newValue, Value.y, Value.z);
|
||||
DebounceValueChange(newValue, 100).Forget();
|
||||
}
|
||||
|
||||
private void OnYChanged(ChangeEvent<float> evt)
|
||||
{
|
||||
var newValue = new Vector3(Value.x, evt.newValue, Value.z);
|
||||
DebounceValueChange(newValue, 100).Forget();
|
||||
}
|
||||
|
||||
private void OnZChanged(ChangeEvent<float> evt)
|
||||
{
|
||||
var newValue = new Vector3(Value.x, Value.y, evt.newValue);
|
||||
DebounceValueChange(newValue, 100).Forget();
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace UVC.UIToolkit
|
||||
#region Fields
|
||||
private bool _disposed;
|
||||
private TreeView? _treeView;
|
||||
private TextField? _searchField;
|
||||
private UTKInputField? _searchField;
|
||||
|
||||
private readonly List<IUTKPropertyEntry> _entries = new();
|
||||
private readonly Dictionary<string, IUTKPropertyGroup> _groupIndex = new();
|
||||
@@ -70,23 +70,12 @@ namespace UVC.UIToolkit
|
||||
var searchContainer = new VisualElement { name = "search-container" };
|
||||
searchContainer.AddToClassList("utk-property-list__search-container");
|
||||
|
||||
_searchField = new TextField { name = "search-field" };
|
||||
_searchField = new UTKInputField { name = "search-field" };
|
||||
_searchField.Placeholder = "Search...";
|
||||
_searchField.AddToClassList("utk-property-list__search-field");
|
||||
_searchField.RegisterValueChangedCallback(OnSearchChanged);
|
||||
|
||||
var searchPlaceholder = new Label("Search...") { name = "search-placeholder" };
|
||||
searchPlaceholder.AddToClassList("utk-property-list__search-placeholder");
|
||||
searchPlaceholder.pickingMode = PickingMode.Ignore;
|
||||
|
||||
_searchField.RegisterCallback<FocusInEvent>(_ => searchPlaceholder.style.display = DisplayStyle.None);
|
||||
_searchField.RegisterCallback<FocusOutEvent>(_ =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(_searchField.value))
|
||||
searchPlaceholder.style.display = DisplayStyle.Flex;
|
||||
});
|
||||
_searchField.OnValueChanged += OnSearchChanged;
|
||||
|
||||
searchContainer.Add(_searchField);
|
||||
searchContainer.Add(searchPlaceholder);
|
||||
Add(searchContainer);
|
||||
|
||||
// TreeView
|
||||
@@ -507,13 +496,15 @@ namespace UVC.UIToolkit
|
||||
var groupElement = new VisualElement();
|
||||
groupElement.AddToClassList("utk-property-group__header");
|
||||
|
||||
var expandIcon = new Label(group.IsExpanded ? "▼" : "▶");
|
||||
var expandIcon = new UTKLabel(group.IsExpanded ? UTKMaterialIcons.ExpandMore : UTKMaterialIcons.ChevronRight, 16);
|
||||
expandIcon.AddToClassList("utk-property-group__expand-icon");
|
||||
|
||||
var title = new Label(group.GroupName);
|
||||
var title = new UTKLabel(group.GroupName, UTKLabel.LabelSize.Label1);
|
||||
title.IsBold = true;
|
||||
title.AddToClassList("utk-property-group__title");
|
||||
|
||||
var count = new Label($"({group.ItemCount})");
|
||||
var count = new UTKLabel($"({group.ItemCount})", UTKLabel.LabelSize.Caption);
|
||||
count.Variant = UTKLabel.LabelVariant.Secondary;
|
||||
count.AddToClassList("utk-property-group__count");
|
||||
|
||||
groupElement.Add(expandIcon);
|
||||
@@ -523,7 +514,7 @@ namespace UVC.UIToolkit
|
||||
groupElement.RegisterCallback<ClickEvent>(_ =>
|
||||
{
|
||||
ToggleGroupExpanded(group.GroupId);
|
||||
expandIcon.text = group.IsExpanded ? "▼" : "▶";
|
||||
expandIcon.SetMaterialIcon(group.IsExpanded ? UTKMaterialIcons.ExpandMore : UTKMaterialIcons.ChevronRight, 16);
|
||||
OnGroupExpandedChanged?.Invoke(group, group.IsExpanded);
|
||||
});
|
||||
|
||||
@@ -573,9 +564,9 @@ namespace UVC.UIToolkit
|
||||
OnPropertyValueChanged?.Invoke(args);
|
||||
}
|
||||
|
||||
private void OnSearchChanged(ChangeEvent<string> evt)
|
||||
private void OnSearchChanged(string newValue)
|
||||
{
|
||||
_searchText = evt.newValue ?? string.Empty;
|
||||
_searchText = newValue ?? string.Empty;
|
||||
RefreshTreeView();
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
@@ -14,15 +15,16 @@ namespace UVC.UIToolkit
|
||||
public partial class UTKPropertyWindow : VisualElement, IDisposable
|
||||
{
|
||||
#region Constants
|
||||
private const string USS_PATH = "UIToolkit/Property/UTKPropertyWindow";
|
||||
private const string UXML_PATH = "UIToolkit/Property/UTKPropertyWindow";
|
||||
private const string USS_PATH = "UIToolkit/Property/UTKPropertyWindowUss";
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private bool _disposed;
|
||||
private UTKPropertyList? _propertyList;
|
||||
private VisualElement? _header;
|
||||
private Label? _titleLabel;
|
||||
private Button? _closeButton;
|
||||
private UTKLabel? _titleLabel;
|
||||
private UTKButton? _closeButton;
|
||||
private string _title = "Properties";
|
||||
private bool _showCloseButton = true;
|
||||
private bool _isDragging;
|
||||
@@ -40,7 +42,7 @@ namespace UVC.UIToolkit
|
||||
_title = value;
|
||||
if (_titleLabel != null)
|
||||
{
|
||||
_titleLabel.text = value;
|
||||
_titleLabel.Text = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,6 +95,7 @@ namespace UVC.UIToolkit
|
||||
public UTKPropertyWindow()
|
||||
{
|
||||
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
||||
SubscribeToThemeChanges();
|
||||
|
||||
var styleSheet = Resources.Load<StyleSheet>(USS_PATH);
|
||||
if (styleSheet != null)
|
||||
@@ -114,29 +117,99 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
AddToClassList("utk-property-window");
|
||||
|
||||
var asset = Resources.Load<VisualTreeAsset>(UXML_PATH);
|
||||
if (asset != null)
|
||||
{
|
||||
CreateUIFromUxml(asset);
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateUIFallback();
|
||||
}
|
||||
|
||||
// 드래그 이벤트
|
||||
if (_header != null)
|
||||
{
|
||||
_header.RegisterCallback<PointerDownEvent>(OnHeaderPointerDown);
|
||||
_header.RegisterCallback<PointerMoveEvent>(OnHeaderPointerMove);
|
||||
_header.RegisterCallback<PointerUpEvent>(OnHeaderPointerUp);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateUIFromUxml(VisualTreeAsset asset)
|
||||
{
|
||||
var root = asset.Instantiate();
|
||||
var windowRoot = root.Q<VisualElement>("window-root");
|
||||
|
||||
if (windowRoot != null)
|
||||
{
|
||||
// windowRoot의 자식들을 현재 요소로 이동
|
||||
foreach (var child in windowRoot.Children().ToArray())
|
||||
{
|
||||
Add(child);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// window-root가 없으면 전체 추가
|
||||
Add(root);
|
||||
}
|
||||
|
||||
// 요소 참조 가져오기
|
||||
_header = this.Q<VisualElement>("header");
|
||||
_titleLabel = this.Q<UTKLabel>("title");
|
||||
_closeButton = this.Q<UTKButton>("close-btn");
|
||||
_propertyList = this.Q<UTKPropertyList>("content");
|
||||
|
||||
// 타이틀 설정
|
||||
if (_titleLabel != null)
|
||||
{
|
||||
_titleLabel.Text = _title;
|
||||
_titleLabel.IsBold = true;
|
||||
_titleLabel.Size = UTKLabel.LabelSize.Label1;
|
||||
}
|
||||
|
||||
// 닫기 버튼 설정
|
||||
if (_closeButton != null)
|
||||
{
|
||||
_closeButton.SetMaterialIcon(UTKMaterialIcons.Close, 14);
|
||||
_closeButton.IconOnly = true;
|
||||
_closeButton.OnClicked += () => OnCloseClicked?.Invoke();
|
||||
_closeButton.style.display = _showCloseButton ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
|
||||
// PropertyList가 없으면 생성
|
||||
if (_propertyList == null)
|
||||
{
|
||||
_propertyList = new UTKPropertyList();
|
||||
_propertyList.name = "content";
|
||||
_propertyList.AddToClassList("utk-property-window__content");
|
||||
Add(_propertyList);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateUIFallback()
|
||||
{
|
||||
// 헤더
|
||||
_header = new VisualElement();
|
||||
_header.name = "header";
|
||||
_header.AddToClassList("utk-property-window__header");
|
||||
|
||||
_titleLabel = new Label(_title);
|
||||
_titleLabel = new UTKLabel(_title, UTKLabel.LabelSize.Label1);
|
||||
_titleLabel.name = "title";
|
||||
_titleLabel.IsBold = true;
|
||||
_titleLabel.AddToClassList("utk-property-window__title");
|
||||
_header.Add(_titleLabel);
|
||||
|
||||
_closeButton = new Button { text = "✕" };
|
||||
_closeButton = new UTKButton("", UTKMaterialIcons.Close, UTKButton.ButtonVariant.Text, 14);
|
||||
_closeButton.name = "close-btn";
|
||||
_closeButton.IconOnly = true;
|
||||
_closeButton.AddToClassList("utk-property-window__close-btn");
|
||||
_closeButton.clicked += () => OnCloseClicked?.Invoke();
|
||||
_closeButton.OnClicked += () => OnCloseClicked?.Invoke();
|
||||
_header.Add(_closeButton);
|
||||
|
||||
Add(_header);
|
||||
|
||||
// 드래그 이벤트
|
||||
_header.RegisterCallback<PointerDownEvent>(OnHeaderPointerDown);
|
||||
_header.RegisterCallback<PointerMoveEvent>(OnHeaderPointerMove);
|
||||
_header.RegisterCallback<PointerUpEvent>(OnHeaderPointerUp);
|
||||
|
||||
// PropertyList
|
||||
_propertyList = new UTKPropertyList();
|
||||
_propertyList.AddToClassList("utk-property-window__content");
|
||||
@@ -242,12 +315,33 @@ namespace UVC.UIToolkit
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 테마 (Theme)
|
||||
|
||||
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;
|
||||
|
||||
// 드래그 이벤트 해제
|
||||
if (_header != null)
|
||||
{
|
||||
|
||||
@@ -102,6 +102,13 @@ namespace UVC.UIToolkit
|
||||
EnableInClassList("utk-slider--disabled", !value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>현재 값</summary>
|
||||
public float Value
|
||||
{
|
||||
get => value;
|
||||
set => this.value = value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
192
Assets/Scripts/UVC/UIToolkit/Slider/UTKSliderInt.cs
Normal file
192
Assets/Scripts/UVC/UIToolkit/Slider/UTKSliderInt.cs
Normal file
@@ -0,0 +1,192 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UVC.UIToolkit
|
||||
{
|
||||
/// <summary>
|
||||
/// 정수 슬라이더 컴포넌트.
|
||||
/// Unity SliderInt를 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// 드래그 또는 클릭으로 범위 내의 정수 값을 선택할 수 있습니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>정수 슬라이더(SliderInt)란?</b></para>
|
||||
/// <para>
|
||||
/// 정수 슬라이더는 사용자가 지정된 범위 내에서 정수 값을 선택할 수 있는 UI 컨트롤입니다.
|
||||
/// 레벨, 수량, 페이지 번호 등 이산적인 값 조절에 사용되며, 시각적으로 현재 값의 위치를 확인할 수 있습니다.
|
||||
/// </para>
|
||||
///
|
||||
/// <para><b>주요 속성:</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description><c>lowValue</c> - 슬라이더의 최소값 (왼쪽 끝)</description></item>
|
||||
/// <item><description><c>highValue</c> - 슬라이더의 최대값 (오른쪽 끝)</description></item>
|
||||
/// <item><description><c>value</c> - 현재 선택된 정수 값</description></item>
|
||||
/// <item><description><c>showInputField</c> - 숫자 입력 필드 표시 여부 (Unity 기본 속성)</description></item>
|
||||
/// <item><description><c>direction</c> - 슬라이더 방향 (Horizontal/Vertical)</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>실제 활용 예시:</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>게임 설정 - 난이도 레벨, 라운드 수, 플레이어 수</description></item>
|
||||
/// <item><description>페이지네이션 - 페이지 번호 선택</description></item>
|
||||
/// <item><description>수량 조절 - 아이템 개수, 재고 수량</description></item>
|
||||
/// <item><description>설정 값 - 그리드 크기, 해상도 단계</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 기본 정수 슬라이더 생성
|
||||
/// var slider = new UTKSliderInt();
|
||||
/// slider.label = "레벨";
|
||||
/// slider.lowValue = 1;
|
||||
/// slider.highValue = 10;
|
||||
/// slider.value = 5;
|
||||
///
|
||||
/// // 값 변경 이벤트
|
||||
/// slider.OnValueChanged += (value) => {
|
||||
/// Debug.Log($"선택된 레벨: {value}");
|
||||
/// SetDifficultyLevel(value);
|
||||
/// };
|
||||
///
|
||||
/// // 생성자로 한 번에 설정
|
||||
/// var quantitySlider = new UTKSliderInt("수량", 1, 100, 10);
|
||||
///
|
||||
/// // 페이지 선택 슬라이더
|
||||
/// var pageSlider = new UTKSliderInt("페이지", 1, totalPages, 1);
|
||||
/// pageSlider.OnValueChanged += (page) => {
|
||||
/// LoadPage(page);
|
||||
/// };
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <!-- 기본 정수 슬라이더 -->
|
||||
/// <utk:UTKSliderInt label="레벨" low-value="1" high-value="10" value="5" />
|
||||
///
|
||||
/// <!-- 값 입력 필드 표시 -->
|
||||
/// <utk:UTKSliderInt label="수량" low-value="0" high-value="100" value="50"
|
||||
/// show-input-field="true" />
|
||||
///
|
||||
/// <!-- 비활성화 상태 -->
|
||||
/// <utk:UTKSliderInt label="잠금" is-enabled="false" value="25" />
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKSliderInt : SliderInt, IDisposable
|
||||
{
|
||||
#region Constants
|
||||
private const string USS_PATH = "UIToolkit/Slider/UTKSlider";
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private bool _disposed;
|
||||
private bool _isEnabled = true;
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
/// <summary>값 변경 이벤트</summary>
|
||||
public event Action<int>? OnValueChanged;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>활성화 상태</summary>
|
||||
[UxmlAttribute("is-enabled")]
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => _isEnabled;
|
||||
set
|
||||
{
|
||||
_isEnabled = value;
|
||||
SetEnabled(value);
|
||||
EnableInClassList("utk-slider--disabled", !value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>현재 값</summary>
|
||||
public int Value
|
||||
{
|
||||
get => value;
|
||||
set => this.value = value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
public UTKSliderInt() : base()
|
||||
{
|
||||
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
||||
|
||||
var uss = Resources.Load<StyleSheet>(USS_PATH);
|
||||
if (uss != null)
|
||||
{
|
||||
styleSheets.Add(uss);
|
||||
}
|
||||
|
||||
SetupStyles();
|
||||
SetupEvents();
|
||||
SubscribeToThemeChanges();
|
||||
}
|
||||
|
||||
public UTKSliderInt(string label, int minValue = 0, int maxValue = 100, int defaultValue = 0) : this()
|
||||
{
|
||||
this.label = label;
|
||||
this.lowValue = minValue;
|
||||
this.highValue = maxValue;
|
||||
this.value = defaultValue;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Setup
|
||||
private void SetupStyles()
|
||||
{
|
||||
AddToClassList("utk-slider");
|
||||
AddToClassList("utk-slider-int");
|
||||
}
|
||||
|
||||
private void SetupEvents()
|
||||
{
|
||||
this.RegisterValueChangedCallback(OnSliderValueChanged);
|
||||
}
|
||||
|
||||
private void SubscribeToThemeChanges()
|
||||
{
|
||||
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
|
||||
RegisterCallback<DetachFromPanelEvent>(_ =>
|
||||
{
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
});
|
||||
}
|
||||
|
||||
private void OnThemeChanged(UTKTheme theme)
|
||||
{
|
||||
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
/// <summary>이벤트 발생 없이 값 설정</summary>
|
||||
public new void SetValueWithoutNotify(int newValue)
|
||||
{
|
||||
base.SetValueWithoutNotify(newValue);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
private void OnSliderValueChanged(ChangeEvent<int> evt)
|
||||
{
|
||||
OnValueChanged?.Invoke(evt.newValue);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
OnValueChanged = null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/UVC/UIToolkit/Slider/UTKSliderInt.cs.meta
Normal file
2
Assets/Scripts/UVC/UIToolkit/Slider/UTKSliderInt.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2865ae5ce26e204eb34404cb7587fd9
|
||||
@@ -7,9 +7,148 @@ using UnityEngine.UIElements;
|
||||
namespace UVC.UIToolkit
|
||||
{
|
||||
/// <summary>
|
||||
/// UIToolkit 테마 관리자
|
||||
/// 런타임에서 Light/Dark 테마 전환을 지원합니다.
|
||||
/// UIToolkit 테마 관리자.
|
||||
/// 런타임에서 Light/Dark 테마 전환을 지원하며, 모든 UTK 컴포넌트에 일관된 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>테마 매니저란?</b></para>
|
||||
/// <para>
|
||||
/// UTKThemeManager는 애플리케이션 전체의 UI 테마(색상, 스타일)를 중앙에서 관리합니다.
|
||||
/// 싱글톤 패턴으로 구현되어 어디서든 <c>UTKThemeManager.Instance</c>로 접근 가능합니다.
|
||||
/// </para>
|
||||
///
|
||||
/// <para><b>스타일시트 로드 순서:</b></para>
|
||||
/// <list type="number">
|
||||
/// <item><description>UTKVariables.uss - 레이아웃 변수 (spacing, radius, font-size)</description></item>
|
||||
/// <item><description>UTKThemeDark/Light.uss - 테마별 색상 변수</description></item>
|
||||
/// <item><description>UTKComponents.uss - 공통 컴포넌트 스타일</description></item>
|
||||
/// <item><description>UTKDefaultStyle.uss - Unity 기본 요소 오버라이드 (선택)</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>주요 기능:</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description><c>RegisterRoot()</c> - UIDocument 루트에 테마 등록</description></item>
|
||||
/// <item><description><c>SetTheme()</c> - 테마 변경 (Dark/Light)</description></item>
|
||||
/// <item><description><c>ToggleTheme()</c> - 테마 토글</description></item>
|
||||
/// <item><description><c>ApplyThemeToElement()</c> - 개별 요소에 테마 적용</description></item>
|
||||
/// <item><description><c>OnThemeChanged</c> - 테마 변경 이벤트 구독</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>사용 시점:</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>UIDocument 초기화 시 <c>RegisterRoot()</c> 호출</description></item>
|
||||
/// <item><description>UTK 컴포넌트 내부에서 자동으로 <c>ApplyThemeToElement()</c> 호출</description></item>
|
||||
/// <item><description>설정 화면에서 테마 전환 버튼 클릭 시 <c>ToggleTheme()</c> 호출</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <para><b>기본 사용법 - UIDocument에 테마 등록:</b></para>
|
||||
/// <code>
|
||||
/// public class MyUIController : MonoBehaviour
|
||||
/// {
|
||||
/// [SerializeField] private UIDocument _uiDocument;
|
||||
///
|
||||
/// void Start()
|
||||
/// {
|
||||
/// // UIDocument 루트에 테마 스타일시트 등록
|
||||
/// var root = _uiDocument.rootVisualElement;
|
||||
/// UTKThemeManager.Instance.RegisterRoot(root);
|
||||
/// }
|
||||
///
|
||||
/// void OnDestroy()
|
||||
/// {
|
||||
/// // 정리 (선택사항)
|
||||
/// var root = _uiDocument.rootVisualElement;
|
||||
/// UTKThemeManager.Instance.UnregisterRoot(root);
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <para><b>테마 전환:</b></para>
|
||||
/// <code>
|
||||
/// // 특정 테마로 변경
|
||||
/// UTKThemeManager.Instance.SetTheme(UTKTheme.Light);
|
||||
///
|
||||
/// // 토글 (Dark ↔ Light)
|
||||
/// UTKThemeManager.Instance.ToggleTheme();
|
||||
///
|
||||
/// // 현재 테마 확인
|
||||
/// if (UTKThemeManager.Instance.IsDarkTheme)
|
||||
/// {
|
||||
/// Debug.Log("다크 테마 사용 중");
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <para><b>테마 변경 이벤트 구독:</b></para>
|
||||
/// <code>
|
||||
/// void OnEnable()
|
||||
/// {
|
||||
/// UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
|
||||
/// }
|
||||
///
|
||||
/// void OnDisable()
|
||||
/// {
|
||||
/// UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
/// }
|
||||
///
|
||||
/// private void OnThemeChanged(UTKTheme newTheme)
|
||||
/// {
|
||||
/// Debug.Log($"테마 변경됨: {newTheme}");
|
||||
/// // 테마에 따른 추가 로직 (아이콘 변경 등)
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <para><b>커스텀 스타일 값 가져오기:</b></para>
|
||||
/// <code>
|
||||
/// // USS 변수에서 색상 값 읽기
|
||||
/// var primaryColor = new CustomStyleProperty<Color>("--color-btn-primary");
|
||||
/// var color = UTKThemeManager.GetColor(myElement, primaryColor, Color.blue);
|
||||
///
|
||||
/// // USS 변수에서 float 값 읽기
|
||||
/// var spacing = new CustomStyleProperty<float>("--space-m");
|
||||
/// var value = UTKThemeManager.GetFloat(myElement, spacing, 8f);
|
||||
/// </code>
|
||||
///
|
||||
/// <para><b>UTK 컴포넌트에서 사용 (내부 구현):</b></para>
|
||||
/// <code>
|
||||
/// [UxmlElement]
|
||||
/// public partial class MyCustomComponent : VisualElement
|
||||
/// {
|
||||
/// public MyCustomComponent()
|
||||
/// {
|
||||
/// // 컴포넌트에 테마 스타일 적용
|
||||
/// UTKThemeManager.Instance.ApplyThemeToElement(this);
|
||||
///
|
||||
/// // 테마 변경 구독
|
||||
/// UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
|
||||
///
|
||||
/// // 패널에서 분리될 때 구독 해제
|
||||
/// RegisterCallback<DetachFromPanelEvent>(_ =>
|
||||
/// {
|
||||
/// UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
/// });
|
||||
/// }
|
||||
///
|
||||
/// private void OnThemeChanged(UTKTheme theme)
|
||||
/// {
|
||||
/// UTKThemeManager.Instance.ApplyThemeToElement(this);
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <para><b>테마 전환 버튼 구현:</b></para>
|
||||
/// <code>
|
||||
/// var themeToggle = new UTKButton("테마 전환", UTKButtonVariant.Secondary);
|
||||
/// themeToggle.clicked += () =>
|
||||
/// {
|
||||
/// UTKThemeManager.Instance.ToggleTheme();
|
||||
/// var icon = UTKThemeManager.Instance.IsDarkTheme
|
||||
/// ? UTKMaterialIcons.DarkMode
|
||||
/// : UTKMaterialIcons.LightMode;
|
||||
/// themeToggle.SetMaterialIcon(icon);
|
||||
/// };
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class UTKThemeManager
|
||||
{
|
||||
#region Singleton
|
||||
@@ -263,11 +402,20 @@ namespace UVC.UIToolkit
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 지원하는 테마 종류
|
||||
/// 지원하는 테마 종류.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>각 테마는 해당 USS 파일의 색상 변수를 사용합니다:</para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description><c>Dark</c> - UTKThemeDark.uss (어두운 배경, 밝은 텍스트)</description></item>
|
||||
/// <item><description><c>Light</c> - UTKThemeLight.uss (밝은 배경, 어두운 텍스트)</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public enum UTKTheme
|
||||
{
|
||||
/// <summary>다크 테마 (기본값)</summary>
|
||||
Dark,
|
||||
/// <summary>라이트 테마</summary>
|
||||
Light
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user