UTKTopMenu 기능 추가
This commit is contained in:
@@ -41,13 +41,15 @@
|
|||||||
.menu-item__arrow {
|
.menu-item__arrow {
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
margin-left: 4px;
|
margin-left: auto;
|
||||||
border-left-width: 4px;
|
border-left-width: 5px;
|
||||||
border-right-width: 4px;
|
border-right-width: 5px;
|
||||||
border-top-width: 4px;
|
border-top-width: 5px;
|
||||||
border-bottom-width: 4px;
|
border-bottom-width: 5px;
|
||||||
border-left-color: transparent;
|
border-left-color: var(--color-text-primary);
|
||||||
border-right-color: transparent;
|
border-right-color: transparent;
|
||||||
border-top-color: var(--color-text-primary);
|
border-top-color: transparent;
|
||||||
border-bottom-color: transparent;
|
border-bottom-color: transparent;
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,13 +34,15 @@
|
|||||||
.menu-item__arrow {
|
.menu-item__arrow {
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
margin-left: 4px;
|
margin-left: auto;
|
||||||
border-left-width: 4px;
|
border-left-width: 5px;
|
||||||
border-right-width: 4px;
|
border-right-width: 5px;
|
||||||
border-top-width: 4px;
|
border-top-width: 5px;
|
||||||
border-bottom-width: 4px;
|
border-bottom-width: 5px;
|
||||||
border-left-color: transparent;
|
border-left-color: var(--color-text-primary);
|
||||||
border-right-color: transparent;
|
border-right-color: transparent;
|
||||||
border-top-color: var(--color-text-primary);
|
border-top-color: transparent;
|
||||||
border-bottom-color: transparent;
|
border-bottom-color: transparent;
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,16 @@
|
|||||||
border-bottom-color: var(--color-border);
|
border-bottom-color: var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 세로 정렬 메뉴 */
|
||||||
|
.top-menu__items--vertical {
|
||||||
|
flex-direction: column;
|
||||||
|
height: auto;
|
||||||
|
width: auto;
|
||||||
|
background-color: var(--color-bg-primary);
|
||||||
|
border-right-width: 1px;
|
||||||
|
border-right-color: var(--color-border);
|
||||||
|
}
|
||||||
|
|
||||||
/* 하위 메뉴 컨테이너 스타일 */
|
/* 하위 메뉴 컨테이너 스타일 */
|
||||||
.submenu-container {
|
.submenu-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using Cysharp.Threading.Tasks;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UIElements;
|
using UnityEngine.UIElements;
|
||||||
using UVC.UIToolkit;
|
using UVC.UIToolkit;
|
||||||
using UVC.UIToolkit.Menu;
|
|
||||||
using UVC.UI.Commands;
|
using UVC.UI.Commands;
|
||||||
using UVC.Log;
|
using UVC.Log;
|
||||||
|
|
||||||
@@ -24,12 +23,15 @@ namespace UVC.Sample.UIToolkit
|
|||||||
|
|
||||||
private UTKToggle? _themeToggle;
|
private UTKToggle? _themeToggle;
|
||||||
private VisualElement? _root;
|
private VisualElement? _root;
|
||||||
private UTKTopMenuView? _menuView;
|
private UTKTopMenu? _menuView;
|
||||||
private UTKTopMenuModel? _menuModel;
|
private UTKTopMenuModel? _menuModel;
|
||||||
|
|
||||||
private UTKTopMenuView? _menuView2;
|
private UTKTopMenu? _menuView2;
|
||||||
private UTKTopMenuModel? _menuModel2;
|
private UTKTopMenuModel? _menuModel2;
|
||||||
|
|
||||||
|
private UTKTopMenu? _menuView3;
|
||||||
|
private UTKTopMenuModel? _menuModel3;
|
||||||
|
|
||||||
// 상태 관리용
|
// 상태 관리용
|
||||||
private bool _canUndo = false;
|
private bool _canUndo = false;
|
||||||
private bool _canRedo = false;
|
private bool _canRedo = false;
|
||||||
@@ -72,12 +74,12 @@ namespace UVC.Sample.UIToolkit
|
|||||||
if (_root == null) return;
|
if (_root == null) return;
|
||||||
|
|
||||||
// 1. UTKTopMenuView 생성
|
// 1. UTKTopMenuView 생성
|
||||||
_menuView = new UTKTopMenuView();
|
_menuView = new UTKTopMenu();
|
||||||
_menuView.style.position = Position.Absolute;
|
_menuView.style.position = Position.Absolute;
|
||||||
_menuView.style.top = 0;
|
_menuView.style.top = 0;
|
||||||
_menuView.style.left = 50;
|
_menuView.style.left = 50;
|
||||||
|
|
||||||
_menuView2 = new UTKTopMenuView();
|
_menuView2 = new UTKTopMenu();
|
||||||
_menuView2.style.position = Position.Absolute;
|
_menuView2.style.position = Position.Absolute;
|
||||||
_menuView2.style.top = 0;
|
_menuView2.style.top = 0;
|
||||||
_menuView2.style.left = 0;
|
_menuView2.style.left = 0;
|
||||||
@@ -85,10 +87,23 @@ namespace UVC.Sample.UIToolkit
|
|||||||
_root.Add(_menuView2);
|
_root.Add(_menuView2);
|
||||||
_root.Add(_menuView);
|
_root.Add(_menuView);
|
||||||
|
|
||||||
|
// 세로 정렬 메뉴 (Vertical Orientation + ItemSpacing)
|
||||||
|
_menuView3 = new UTKTopMenu();
|
||||||
|
_menuView3.Orientation = UTKMenuOrientation.Vertical;
|
||||||
|
_menuView3.ItemSpacing = 4f;
|
||||||
|
_menuView3.SubMenuOffsetX = -10f;
|
||||||
|
_menuView3.SubMenuOffsetY = 4f;
|
||||||
|
_menuView3.style.position = Position.Absolute;
|
||||||
|
_menuView3.style.top = 50;
|
||||||
|
_menuView3.style.left = 0;
|
||||||
|
_menuView3.style.width = 120;
|
||||||
|
_root.Add(_menuView3);
|
||||||
|
|
||||||
// 2. UTKTopMenuModel 생성 및 메뉴 아이템 추가
|
// 2. UTKTopMenuModel 생성 및 메뉴 아이템 추가
|
||||||
_menuModel = new UTKTopMenuModel();
|
_menuModel = new UTKTopMenuModel();
|
||||||
_menuModel2 = new UTKTopMenuModel();
|
_menuModel2 = new UTKTopMenuModel();
|
||||||
|
_menuModel3 = new UTKTopMenuModel();
|
||||||
|
|
||||||
CreateMenuItems();
|
CreateMenuItems();
|
||||||
|
|
||||||
// 3. View에 메뉴 생성
|
// 3. View에 메뉴 생성
|
||||||
@@ -102,9 +117,15 @@ namespace UVC.Sample.UIToolkit
|
|||||||
_menuView2.CreateMenuItems(_menuModel2.MenuItems, _menuView2.MenuContainer);
|
_menuView2.CreateMenuItems(_menuModel2.MenuItems, _menuView2.MenuContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_menuView3.MenuContainer != null)
|
||||||
|
{
|
||||||
|
_menuView3.CreateMenuItems(_menuModel3.MenuItems, _menuView3.MenuContainer);
|
||||||
|
}
|
||||||
|
|
||||||
// 4. 이벤트 구독
|
// 4. 이벤트 구독
|
||||||
_menuView.OnMenuItemClicked += HandleMenuItemClicked;
|
_menuView.OnMenuItemClicked += HandleMenuItemClicked;
|
||||||
_menuView2.OnMenuItemClicked += HandleMenuItemClicked2;
|
_menuView2.OnMenuItemClicked += HandleMenuItemClicked2;
|
||||||
|
_menuView3.OnMenuItemClicked += HandleMenuItemClicked3;
|
||||||
|
|
||||||
// 5. 상태 테스트 버튼 생성
|
// 5. 상태 테스트 버튼 생성
|
||||||
CreateTestButtons();
|
CreateTestButtons();
|
||||||
@@ -117,7 +138,7 @@ namespace UVC.Sample.UIToolkit
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void CreateMenuItems()
|
private void CreateMenuItems()
|
||||||
{
|
{
|
||||||
if (_menuModel == null || _menuModel2 == null) return;
|
if (_menuModel == null || _menuModel2 == null || _menuModel3 == null) return;
|
||||||
|
|
||||||
// 파일 메뉴
|
// 파일 메뉴
|
||||||
var fileMenu = new UTKMenuItemData("file", "파일");
|
var fileMenu = new UTKMenuItemData("file", "파일");
|
||||||
@@ -264,7 +285,28 @@ namespace UVC.Sample.UIToolkit
|
|||||||
|
|
||||||
_menuModel2.AddMenuItem(imageMenu);
|
_menuModel2.AddMenuItem(imageMenu);
|
||||||
|
|
||||||
ULog.Debug($"메뉴 아이템 생성 완료: {_menuModel.MenuItems.Count}개 최상위 메뉴");
|
// === 세로 메뉴 (menuView3) ===
|
||||||
|
var vertFileMenu = new UTKMenuItemData("v_file", "파일");
|
||||||
|
vertFileMenu.AddSubMenuItem(new UTKMenuItemData("v_file_new", "새 파일", new DebugLogCommand("세로 메뉴: 새 파일"), shortcut: "Ctrl+N"));
|
||||||
|
vertFileMenu.AddSubMenuItem(new UTKMenuItemData("v_file_open", "열기", new DebugLogCommand("세로 메뉴: 열기"), shortcut: "Ctrl+O"));
|
||||||
|
vertFileMenu.AddSubMenuItem(UTKMenuItemData.CreateSeparator());
|
||||||
|
vertFileMenu.AddSubMenuItem(new UTKMenuItemData("v_file_save", "저장", new DebugLogCommand("세로 메뉴: 저장"), shortcut: "Ctrl+S"));
|
||||||
|
_menuModel3.AddMenuItem(vertFileMenu);
|
||||||
|
|
||||||
|
var vertEditMenu = new UTKMenuItemData("v_edit", "편집");
|
||||||
|
vertEditMenu.AddSubMenuItem(new UTKMenuItemData("v_edit_undo", "실행 취소", new DebugLogCommand("세로 메뉴: 실행 취소"), shortcut: "Ctrl+Z"));
|
||||||
|
vertEditMenu.AddSubMenuItem(new UTKMenuItemData("v_edit_redo", "다시 실행", new DebugLogCommand("세로 메뉴: 다시 실행"), shortcut: "Ctrl+Y"));
|
||||||
|
_menuModel3.AddMenuItem(vertEditMenu);
|
||||||
|
|
||||||
|
var vertViewMenu = new UTKMenuItemData("v_view", "보기");
|
||||||
|
vertViewMenu.AddSubMenuItem(new UTKMenuItemData("v_view_fullscreen", "전체 화면", new DebugLogCommand("세로 메뉴: 전체 화면"), shortcut: "F11"));
|
||||||
|
_menuModel3.AddMenuItem(vertViewMenu);
|
||||||
|
|
||||||
|
var vertHelpMenu = new UTKMenuItemData("v_help", "도움말");
|
||||||
|
vertHelpMenu.AddSubMenuItem(new UTKMenuItemData("v_help_about", "정보", new DebugLogCommand("세로 메뉴: 정보")));
|
||||||
|
_menuModel3.AddMenuItem(vertHelpMenu);
|
||||||
|
|
||||||
|
ULog.Debug($"메뉴 아이템 생성 완료: {_menuModel.MenuItems.Count}개 최상위 메뉴, 세로 메뉴: {_menuModel3.MenuItems.Count}개");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -278,7 +320,7 @@ namespace UVC.Sample.UIToolkit
|
|||||||
var buttonContainer = new VisualElement();
|
var buttonContainer = new VisualElement();
|
||||||
buttonContainer.style.position = Position.Absolute;
|
buttonContainer.style.position = Position.Absolute;
|
||||||
buttonContainer.style.top = 60;
|
buttonContainer.style.top = 60;
|
||||||
buttonContainer.style.left = 10;
|
buttonContainer.style.left = 300;
|
||||||
buttonContainer.style.flexDirection = FlexDirection.Column;
|
buttonContainer.style.flexDirection = FlexDirection.Column;
|
||||||
buttonContainer.style.width = 250;
|
buttonContainer.style.width = 250;
|
||||||
_root.Insert(0, buttonContainer);
|
_root.Insert(0, buttonContainer);
|
||||||
@@ -335,8 +377,25 @@ namespace UVC.Sample.UIToolkit
|
|||||||
{
|
{
|
||||||
text = "Save 단축키 변경"
|
text = "Save 단축키 변경"
|
||||||
};
|
};
|
||||||
|
changeShortcutBtn.style.marginBottom = 5;
|
||||||
buttonContainer.Add(changeShortcutBtn);
|
buttonContainer.Add(changeShortcutBtn);
|
||||||
|
|
||||||
|
// ExecuteCommand 테스트
|
||||||
|
var executeCommandBtn = new Button(() => TestExecuteCommand())
|
||||||
|
{
|
||||||
|
text = "ExecuteCommand 테스트"
|
||||||
|
};
|
||||||
|
executeCommandBtn.style.marginBottom = 5;
|
||||||
|
buttonContainer.Add(executeCommandBtn);
|
||||||
|
|
||||||
|
// TryGetMenuItemData 테스트
|
||||||
|
var getDataBtn = new Button(() => TestTryGetMenuItemData())
|
||||||
|
{
|
||||||
|
text = "TryGetMenuItemData 테스트"
|
||||||
|
};
|
||||||
|
getDataBtn.style.marginBottom = 5;
|
||||||
|
buttonContainer.Add(getDataBtn);
|
||||||
|
|
||||||
// 상태 표시 레이블
|
// 상태 표시 레이블
|
||||||
var statusLabel = new Label();
|
var statusLabel = new Label();
|
||||||
statusLabel.style.marginTop = 20;
|
statusLabel.style.marginTop = 20;
|
||||||
@@ -501,6 +560,51 @@ namespace UVC.Sample.UIToolkit
|
|||||||
label.schedule.Execute(() => UpdateStatusLabel(label)).Every(100);
|
label.schedule.Execute(() => UpdateStatusLabel(label)).Every(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ExecuteCommand 테스트: ItemId로 Command를 직접 실행합니다.
|
||||||
|
/// </summary>
|
||||||
|
private void TestExecuteCommand()
|
||||||
|
{
|
||||||
|
if (_menuView == null) return;
|
||||||
|
|
||||||
|
// file_new는 항상 활성화 → true 반환
|
||||||
|
bool result1 = _menuView.ExecuteCommand("file_new");
|
||||||
|
ULog.Debug($"ExecuteCommand(\"file_new\"): {result1}");
|
||||||
|
|
||||||
|
// file_save는 비활성화 시 false 반환
|
||||||
|
bool result2 = _menuView.ExecuteCommand("file_save");
|
||||||
|
ULog.Debug($"ExecuteCommand(\"file_save\"): {result2} (비활성화 상태이면 false)");
|
||||||
|
|
||||||
|
// 존재하지 않는 ID → false 반환
|
||||||
|
bool result3 = _menuView.ExecuteCommand("not_exist");
|
||||||
|
ULog.Debug($"ExecuteCommand(\"not_exist\"): {result3}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// TryGetMenuItemData 테스트: ItemId로 메뉴 데이터를 조회합니다.
|
||||||
|
/// </summary>
|
||||||
|
private void TestTryGetMenuItemData()
|
||||||
|
{
|
||||||
|
if (_menuView == null) return;
|
||||||
|
|
||||||
|
if (_menuView.TryGetMenuItemData("file_save", out var data))
|
||||||
|
{
|
||||||
|
ULog.Debug($"TryGetMenuItemData(\"file_save\"): " +
|
||||||
|
$"DisplayName={data?.DisplayName}, " +
|
||||||
|
$"IsEnabled={data?.IsEnabled}, " +
|
||||||
|
$"Shortcut={data?.Shortcut}, " +
|
||||||
|
$"HasCommand={data?.Command != null}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ULog.Debug("TryGetMenuItemData(\"file_save\"): 찾을 수 없음");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 존재하지 않는 ID
|
||||||
|
bool found = _menuView.TryGetMenuItemData("not_exist", out _);
|
||||||
|
ULog.Debug($"TryGetMenuItemData(\"not_exist\"): {found}");
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 메뉴 아이템 클릭 핸들러입니다.
|
/// 메뉴 아이템 클릭 핸들러입니다.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -530,6 +634,21 @@ namespace UVC.Sample.UIToolkit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 세로 메뉴 클릭 핸들러입니다.
|
||||||
|
/// </summary>
|
||||||
|
private void HandleMenuItemClicked3(UTKMenuItemData itemData)
|
||||||
|
{
|
||||||
|
if (itemData == null) return;
|
||||||
|
|
||||||
|
ULog.Debug($"세로 메뉴 클릭: {itemData.ItemId} - {itemData.DisplayName}");
|
||||||
|
|
||||||
|
if (itemData.Command != null)
|
||||||
|
{
|
||||||
|
itemData.Command.Execute(itemData.CommandParameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void OnDestroy()
|
private void OnDestroy()
|
||||||
{
|
{
|
||||||
// 이벤트 구독 해제
|
// 이벤트 구독 해제
|
||||||
@@ -539,8 +658,22 @@ namespace UVC.Sample.UIToolkit
|
|||||||
_menuView.Dispose();
|
_menuView.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_menuView2 != null)
|
||||||
|
{
|
||||||
|
_menuView2.OnMenuItemClicked -= HandleMenuItemClicked2;
|
||||||
|
_menuView2.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_menuView3 != null)
|
||||||
|
{
|
||||||
|
_menuView3.OnMenuItemClicked -= HandleMenuItemClicked3;
|
||||||
|
_menuView3.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
// 모델 정리
|
// 모델 정리
|
||||||
_menuModel?.Dispose();
|
_menuModel?.Dispose();
|
||||||
|
_menuModel2?.Dispose();
|
||||||
|
_menuModel3?.Dispose();
|
||||||
|
|
||||||
ULog.Debug("UTKMenuSample 정리 완료");
|
ULog.Debug("UTKMenuSample 정리 완료");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,15 @@ UIToolkit 기반의 Top Menu 시스템입니다. Unity 6의 최신 UIToolkit 기
|
|||||||
- 이벤트 구독/해제 대칭 (RegisterCallback/UnregisterCallback)
|
- 이벤트 구독/해제 대칭 (RegisterCallback/UnregisterCallback)
|
||||||
- Dictionary 캐싱으로 검색 최적화 (O(1))
|
- Dictionary 캐싱으로 검색 최적화 (O(1))
|
||||||
|
|
||||||
|
✅ **레이아웃 설정**
|
||||||
|
- 메뉴 아이템 간격 조절 (ItemSpacing)
|
||||||
|
- 가로/세로 정렬 전환 (Orientation: Horizontal / Vertical)
|
||||||
|
- UXML 어트리뷰트 지원 (`item-spacing`, `orientation`)
|
||||||
|
|
||||||
|
✅ **프로그래밍 방식 제어**
|
||||||
|
- ItemId로 Command 직접 실행 (`ExecuteCommand`)
|
||||||
|
- ItemId로 메뉴 데이터 조회 (`TryGetMenuItemData`)
|
||||||
|
|
||||||
✅ **고성능 최적화**
|
✅ **고성능 최적화**
|
||||||
- **Lazy Loading**: 서브메뉴는 첫 클릭 시에만 생성 (메모리 절약)
|
- **Lazy Loading**: 서브메뉴는 첫 클릭 시에만 생성 (메모리 절약)
|
||||||
- **리소스 캐싱**: UXML/USS 리소스 1회만 로드 (반복 로드 방지)
|
- **리소스 캐싱**: UXML/USS 리소스 1회만 로드 (반복 로드 방지)
|
||||||
@@ -49,7 +58,7 @@ Assets/Scripts/UVC/UIToolkit/Menu/
|
|||||||
├── UTKMenuItemData.cs # 메뉴 데이터 (텍스트 메뉴)
|
├── UTKMenuItemData.cs # 메뉴 데이터 (텍스트 메뉴)
|
||||||
├── UTKMenuImageItemData.cs # 이미지 메뉴 데이터 (Material Icons 지원)
|
├── UTKMenuImageItemData.cs # 이미지 메뉴 데이터 (Material Icons 지원)
|
||||||
├── UTKTopMenuModel.cs # 데이터 모델 (Dictionary 캐싱)
|
├── UTKTopMenuModel.cs # 데이터 모델 (Dictionary 캐싱)
|
||||||
├── UTKTopMenuView.cs # View (VisualElement 기반, Lazy Loading)
|
├── UTKTopMenu.cs # View (VisualElement 기반, Lazy Loading)
|
||||||
├── UTKMenuItemBase.cs # 메뉴 아이템 베이스 클래스
|
├── UTKMenuItemBase.cs # 메뉴 아이템 베이스 클래스
|
||||||
├── UTKTopMenuItem.cs # 텍스트 메뉴 아이템 컴포넌트
|
├── UTKTopMenuItem.cs # 텍스트 메뉴 아이템 컴포넌트
|
||||||
├── UTKTopMenuImageItem.cs # 이미지 메뉴 아이템 컴포넌트
|
├── UTKTopMenuImageItem.cs # 이미지 메뉴 아이템 컴포넌트
|
||||||
@@ -77,8 +86,8 @@ Assets/Resources/UIToolkit/Menu/
|
|||||||
var menuObject = new GameObject("TopMenu");
|
var menuObject = new GameObject("TopMenu");
|
||||||
var uiDocument = menuObject.AddComponent<UIDocument>();
|
var uiDocument = menuObject.AddComponent<UIDocument>();
|
||||||
|
|
||||||
// 2. UTKTopMenuView를 UIDocument의 루트에 추가
|
// 2. UTKTopMenu를 UIDocument의 루트에 추가
|
||||||
var menuView = new UTKTopMenuView();
|
var menuView = new UTKTopMenu();
|
||||||
uiDocument.rootVisualElement.Add(menuView);
|
uiDocument.rootVisualElement.Add(menuView);
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -142,7 +151,7 @@ using UnityEngine.UIElements;
|
|||||||
|
|
||||||
public class MenuSetup : MonoBehaviour
|
public class MenuSetup : MonoBehaviour
|
||||||
{
|
{
|
||||||
private UTKTopMenuView? _menuView;
|
private UTKTopMenu? _menuView;
|
||||||
private UTKTopMenuModel? _model;
|
private UTKTopMenuModel? _model;
|
||||||
|
|
||||||
void Start()
|
void Start()
|
||||||
@@ -178,7 +187,7 @@ public class MenuSetup : MonoBehaviour
|
|||||||
_model.AddMenuItem(helpMenu);
|
_model.AddMenuItem(helpMenu);
|
||||||
|
|
||||||
// View 생성 및 연결
|
// View 생성 및 연결
|
||||||
_menuView = new UTKTopMenuView();
|
_menuView = new UTKTopMenu();
|
||||||
uiDocument.rootVisualElement.Add(_menuView);
|
uiDocument.rootVisualElement.Add(_menuView);
|
||||||
_menuView.CreateMenuItems(_model.GetMenuItems(), _menuView.MenuContainer, 0);
|
_menuView.CreateMenuItems(_model.GetMenuItems(), _menuView.MenuContainer, 0);
|
||||||
|
|
||||||
@@ -285,6 +294,55 @@ if (_menuView != null)
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 메뉴 아이템 간격 조절
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 메뉴 아이템 간 간격 설정
|
||||||
|
if (_menuView != null)
|
||||||
|
{
|
||||||
|
_menuView.ItemSpacing = 8f; // 아이템 간 8px 간격
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 세로 메뉴 (Vertical Orientation)
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 세로 메뉴로 전환
|
||||||
|
var sideMenu = new UTKTopMenu();
|
||||||
|
sideMenu.Orientation = UTKMenuOrientation.Vertical; // 세로 정렬
|
||||||
|
sideMenu.ItemSpacing = 4f; // 아이템 간 4px 간격
|
||||||
|
|
||||||
|
// 서브메뉴는 아이템 오른쪽에 표시됨
|
||||||
|
sideMenu.SubMenuOffsetX = 5f;
|
||||||
|
```
|
||||||
|
|
||||||
|
### UXML에서 레이아웃 설정
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- 가로 메뉴 (기본값) -->
|
||||||
|
<utk:UTKTopMenu item-spacing="8" orientation="Horizontal" />
|
||||||
|
|
||||||
|
<!-- 세로 메뉴 -->
|
||||||
|
<utk:UTKTopMenu item-spacing="4" orientation="Vertical" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### ItemId로 Command 실행
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ItemId로 메뉴 아이템의 Command를 직접 실행
|
||||||
|
bool executed = _menuView.ExecuteCommand("file_save");
|
||||||
|
if (!executed)
|
||||||
|
{
|
||||||
|
Debug.Log("Command 실행 실패: 비활성화되었거나 Command가 없습니다.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 메뉴 데이터 조회
|
||||||
|
if (_menuView.TryGetMenuItemData("file_save", out var itemData))
|
||||||
|
{
|
||||||
|
Debug.Log($"Enabled: {itemData?.IsEnabled}, Shortcut: {itemData?.Shortcut}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## API 문서
|
## API 문서
|
||||||
@@ -301,15 +359,17 @@ if (_menuView != null)
|
|||||||
| `FindMenuItem(itemId)` | ID로 메뉴 아이템 검색 (O(1)) | string | UTKMenuItemData? |
|
| `FindMenuItem(itemId)` | ID로 메뉴 아이템 검색 (O(1)) | string | UTKMenuItemData? |
|
||||||
| `Dispose()` | 리소스 정리 | - | void |
|
| `Dispose()` | 리소스 정리 | - | void |
|
||||||
|
|
||||||
### UTKTopMenuView
|
### UTKTopMenu
|
||||||
|
|
||||||
#### Public Properties
|
#### Public Properties
|
||||||
|
|
||||||
| 속성 | 타입 | 설명 |
|
| 속성 | 타입 | UXML 어트리뷰트 | 설명 |
|
||||||
|------|------|------|
|
|------|------|----------------|------|
|
||||||
| `MenuContainer` | VisualElement? | 메뉴 아이템이 배치될 컨테이너 |
|
| `MenuContainer` | VisualElement? | - | 메뉴 아이템이 배치될 컨테이너 |
|
||||||
| `SubMenuOffsetX` | float | 최상위 메뉴의 서브메뉴 X축 offset (픽셀) |
|
| `SubMenuOffsetX` | float | - | 최상위 메뉴의 서브메뉴 X축 offset (픽셀) |
|
||||||
| `SubMenuOffsetY` | float | 최상위 메뉴의 서브메뉴 Y축 offset (픽셀) |
|
| `SubMenuOffsetY` | float | - | 최상위 메뉴의 서브메뉴 Y축 offset (픽셀) |
|
||||||
|
| `ItemSpacing` | float | `item-spacing` | 최상위 메뉴 아이템 간 간격 (픽셀, 기본값: 0) |
|
||||||
|
| `Orientation` | UTKMenuOrientation | `orientation` | 정렬 방향 (Horizontal / Vertical, 기본값: Horizontal) |
|
||||||
|
|
||||||
#### Public Methods
|
#### Public Methods
|
||||||
|
|
||||||
@@ -318,6 +378,8 @@ if (_menuView != null)
|
|||||||
| `CreateMenuItems(items, container, depth)` | 메뉴 아이템 생성 (Lazy Loading) |
|
| `CreateMenuItems(items, container, depth)` | 메뉴 아이템 생성 (Lazy Loading) |
|
||||||
| `ClearMenuItems()` | 모든 메뉴 아이템 제거 |
|
| `ClearMenuItems()` | 모든 메뉴 아이템 제거 |
|
||||||
| `CloseAllOpenSubMenus()` | 모든 열린 서브메뉴 닫기 |
|
| `CloseAllOpenSubMenus()` | 모든 열린 서브메뉴 닫기 |
|
||||||
|
| `TryGetMenuItemData(itemId, out itemData)` | ItemId로 메뉴 아이템 데이터 조회 (O(1)) |
|
||||||
|
| `ExecuteCommand(itemId)` | ItemId로 Command 실행 (비활성화/Command 없으면 false) |
|
||||||
| `Dispose()` | 리소스 정리 |
|
| `Dispose()` | 리소스 정리 |
|
||||||
|
|
||||||
#### Public Events
|
#### Public Events
|
||||||
@@ -472,7 +534,7 @@ void OnDisable()
|
|||||||
1. Unity Profiler 열기 (Window > Analysis > Profiler)
|
1. Unity Profiler 열기 (Window > Analysis > Profiler)
|
||||||
2. Memory 섹션 선택
|
2. Memory 섹션 선택
|
||||||
3. 씬 전환 10회 후 메모리 증가 확인
|
3. 씬 전환 10회 후 메모리 증가 확인
|
||||||
4. Detailed View에서 UTKMenuItemData, UTKTopMenuView 검색
|
4. Detailed View에서 UTKMenuItemData, UTKTopMenu 검색
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -481,7 +543,7 @@ void OnDisable()
|
|||||||
### Lazy Loading (서브메뉴 지연 생성)
|
### Lazy Loading (서브메뉴 지연 생성)
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// UTKTopMenuView는 서브메뉴를 첫 클릭 시에만 생성합니다.
|
// UTKTopMenu는 서브메뉴를 첫 클릭 시에만 생성합니다.
|
||||||
// 초기 메모리 사용량을 대폭 감소시킵니다.
|
// 초기 메모리 사용량을 대폭 감소시킵니다.
|
||||||
|
|
||||||
// Before (모든 서브메뉴 사전 생성):
|
// Before (모든 서브메뉴 사전 생성):
|
||||||
@@ -497,10 +559,10 @@ void OnDisable()
|
|||||||
### 리소스 캐싱
|
### 리소스 캐싱
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// UTKTopMenuView는 UXML/USS 리소스를 클래스 레벨에서 캐싱합니다.
|
// UTKTopMenu는 UXML/USS 리소스를 클래스 레벨에서 캐싱합니다.
|
||||||
// 서브메뉴 아이템을 여러 개 생성해도 Resources.Load는 1회만 호출됩니다.
|
// 서브메뉴 아이템을 여러 개 생성해도 Resources.Load는 1회만 호출됩니다.
|
||||||
|
|
||||||
// 내부 구현 (UTKTopMenuView.cs):
|
// 내부 구현 (UTKTopMenu.cs):
|
||||||
// private VisualTreeAsset? _cachedSubMenuItemAsset;
|
// private VisualTreeAsset? _cachedSubMenuItemAsset;
|
||||||
// private StyleSheet? _cachedSubMenuItemUss;
|
// private StyleSheet? _cachedSubMenuItemUss;
|
||||||
//
|
//
|
||||||
@@ -622,7 +684,7 @@ subMenuContainer.style.display = DisplayStyle.Flex; // 표시
|
|||||||
```csharp
|
```csharp
|
||||||
if (view == null)
|
if (view == null)
|
||||||
{
|
{
|
||||||
Debug.LogError("UTKTopMenuView를 찾을 수 없습니다.");
|
Debug.LogError("UTKTopMenu를 찾을 수 없습니다.");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ namespace UVC.UIToolkit
|
|||||||
ItemId = data.ItemId;
|
ItemId = data.ItemId;
|
||||||
DisplayName = data.DisplayName;
|
DisplayName = data.DisplayName;
|
||||||
IsEnabled = data.IsEnabled;
|
IsEnabled = data.IsEnabled;
|
||||||
Shortcut = data.Shortcut;
|
Shortcut = data.Shortcut ?? "";
|
||||||
|
|
||||||
UpdateUI();
|
UpdateUI();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,17 @@ using UVC.Locale;
|
|||||||
|
|
||||||
namespace UVC.UIToolkit
|
namespace UVC.UIToolkit
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 메뉴의 정렬 방향을 나타냅니다.
|
||||||
|
/// </summary>
|
||||||
|
public enum UTKMenuOrientation
|
||||||
|
{
|
||||||
|
/// <summary>가로 정렬 (기본값)</summary>
|
||||||
|
Horizontal,
|
||||||
|
/// <summary>세로 정렬</summary>
|
||||||
|
Vertical
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// UIToolkit 기반 Top Menu의 View 레이어입니다.
|
/// UIToolkit 기반 Top Menu의 View 레이어입니다.
|
||||||
/// 메뉴 아이템의 시각적 표현과 사용자 상호작용을 처리합니다.
|
/// 메뉴 아이템의 시각적 표현과 사용자 상호작용을 처리합니다.
|
||||||
@@ -20,6 +31,10 @@ namespace UVC.UIToolkit
|
|||||||
/// <item>테마 변경 지원 (UTKThemeManager 연동)</item>
|
/// <item>테마 변경 지원 (UTKThemeManager 연동)</item>
|
||||||
/// <item>외부 클릭으로 서브메뉴 자동 닫기</item>
|
/// <item>외부 클릭으로 서브메뉴 자동 닫기</item>
|
||||||
/// <item>서브메뉴 위치 조정 (SubMenuOffsetX/Y)</item>
|
/// <item>서브메뉴 위치 조정 (SubMenuOffsetX/Y)</item>
|
||||||
|
/// <item>메뉴 아이템 간격 조절 (ItemSpacing)</item>
|
||||||
|
/// <item>가로/세로 정렬 전환 (Orientation)</item>
|
||||||
|
/// <item>ItemId로 Command 실행 (ExecuteCommand)</item>
|
||||||
|
/// <item>ItemId로 메뉴 데이터 조회 (TryGetMenuItemData)</item>
|
||||||
/// <item>성능 최적화 (리소스 캐싱, Dictionary 추적)</item>
|
/// <item>성능 최적화 (리소스 캐싱, Dictionary 추적)</item>
|
||||||
/// </list>
|
/// </list>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -28,7 +43,7 @@ namespace UVC.UIToolkit
|
|||||||
/// // === 기본 사용법 ===
|
/// // === 기본 사용법 ===
|
||||||
///
|
///
|
||||||
/// // 1. View 생성 및 UIDocument에 추가
|
/// // 1. View 생성 및 UIDocument에 추가
|
||||||
/// var menuView = new UTKTopMenuView();
|
/// var menuView = new UTKTopMenu();
|
||||||
/// uiDocument.rootVisualElement.Add(menuView);
|
/// uiDocument.rootVisualElement.Add(menuView);
|
||||||
///
|
///
|
||||||
/// // 2. 메뉴 데이터 생성
|
/// // 2. 메뉴 데이터 생성
|
||||||
@@ -78,6 +93,21 @@ namespace UVC.UIToolkit
|
|||||||
/// menuView.SubMenuOffsetX = 10; // 오른쪽으로 10px 이동
|
/// menuView.SubMenuOffsetX = 10; // 오른쪽으로 10px 이동
|
||||||
/// menuView.SubMenuOffsetY = 5; // 아래쪽으로 5px 이동
|
/// menuView.SubMenuOffsetY = 5; // 아래쪽으로 5px 이동
|
||||||
///
|
///
|
||||||
|
/// // 8. 메뉴 아이템 간격 조절 (선택적)
|
||||||
|
/// menuView.ItemSpacing = 8; // 아이템 간 8px 간격
|
||||||
|
///
|
||||||
|
/// // 9. 세로 정렬 (선택적)
|
||||||
|
/// menuView.Orientation = UTKMenuOrientation.Vertical; // 세로 메뉴로 전환
|
||||||
|
///
|
||||||
|
/// // 10. ItemId로 Command 직접 실행 (선택적)
|
||||||
|
/// bool executed = menuView.ExecuteCommand("file_save"); // true: 실행됨, false: 비활성화/Command 없음
|
||||||
|
///
|
||||||
|
/// // 11. ItemId로 메뉴 데이터 조회 (선택적)
|
||||||
|
/// if (menuView.TryGetMenuItemData("file_save", out var data))
|
||||||
|
/// {
|
||||||
|
/// Debug.Log($"Enabled: {data?.IsEnabled}, Shortcut: {data?.Shortcut}");
|
||||||
|
/// }
|
||||||
|
///
|
||||||
///
|
///
|
||||||
/// // === 이벤트 처리 ===
|
/// // === 이벤트 처리 ===
|
||||||
///
|
///
|
||||||
@@ -150,6 +180,14 @@ namespace UVC.UIToolkit
|
|||||||
/// <item>Dispose 시 모든 리소스 및 참조 정리</item>
|
/// <item>Dispose 시 모든 리소스 및 참조 정리</item>
|
||||||
/// </list>
|
/// </list>
|
||||||
///
|
///
|
||||||
|
/// <para><strong>레이아웃 설정:</strong></para>
|
||||||
|
/// <list type="bullet">
|
||||||
|
/// <item><strong>ItemSpacing:</strong> 최상위 메뉴 아이템 간 간격 (픽셀). 기본값 0</item>
|
||||||
|
/// <item><strong>Orientation:</strong> 가로(Horizontal, 기본값) 또는 세로(Vertical) 정렬 전환</item>
|
||||||
|
/// <item>세로 정렬 시 서브메뉴는 아이템 오른쪽에 표시됨</item>
|
||||||
|
/// <item>UXML에서 item-spacing, orientation 어트리뷰트로 설정 가능</item>
|
||||||
|
/// </list>
|
||||||
|
///
|
||||||
/// <para><strong>주의사항:</strong></para>
|
/// <para><strong>주의사항:</strong></para>
|
||||||
/// <list type="bullet">
|
/// <list type="bullet">
|
||||||
/// <item>CreateMenuItems 호출 전에 MenuContainer가 null이 아닌지 확인</item>
|
/// <item>CreateMenuItems 호출 전에 MenuContainer가 null이 아닌지 확인</item>
|
||||||
@@ -158,7 +196,7 @@ namespace UVC.UIToolkit
|
|||||||
/// </list>
|
/// </list>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[UxmlElement]
|
[UxmlElement]
|
||||||
public partial class UTKTopMenuView : VisualElement, IDisposable
|
public partial class UTKTopMenu : VisualElement, IDisposable
|
||||||
{
|
{
|
||||||
#region Constants
|
#region Constants
|
||||||
|
|
||||||
@@ -192,6 +230,10 @@ namespace UVC.UIToolkit
|
|||||||
private float _subMenuOffsetX = 0f;
|
private float _subMenuOffsetX = 0f;
|
||||||
private float _subMenuOffsetY = 0f;
|
private float _subMenuOffsetY = 0f;
|
||||||
|
|
||||||
|
// 메뉴 아이템 간격 및 정렬 방향
|
||||||
|
private float _itemSpacing = 0f;
|
||||||
|
private UTKMenuOrientation _orientation = UTKMenuOrientation.Horizontal;
|
||||||
|
|
||||||
// 리소스 캐싱 (성능 개선)
|
// 리소스 캐싱 (성능 개선)
|
||||||
private VisualTreeAsset? _cachedSubMenuItemAsset;
|
private VisualTreeAsset? _cachedSubMenuItemAsset;
|
||||||
private StyleSheet? _cachedSubMenuItemUss;
|
private StyleSheet? _cachedSubMenuItemUss;
|
||||||
@@ -223,6 +265,36 @@ namespace UVC.UIToolkit
|
|||||||
set => _subMenuOffsetY = value;
|
set => _subMenuOffsetY = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 최상위 메뉴 아이템 간 간격 (픽셀 단위).
|
||||||
|
/// 0 이상의 값을 지정하면 각 아이템 사이에 margin이 적용됩니다.
|
||||||
|
/// </summary>
|
||||||
|
[UxmlAttribute("item-spacing")]
|
||||||
|
public float ItemSpacing
|
||||||
|
{
|
||||||
|
get => _itemSpacing;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_itemSpacing = value;
|
||||||
|
ApplyItemSpacing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 최상위 메뉴의 정렬 방향입니다.
|
||||||
|
/// Horizontal(기본값)은 가로 정렬, Vertical은 세로 정렬입니다.
|
||||||
|
/// </summary>
|
||||||
|
[UxmlAttribute("orientation")]
|
||||||
|
public UTKMenuOrientation Orientation
|
||||||
|
{
|
||||||
|
get => _orientation;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_orientation = value;
|
||||||
|
ApplyOrientation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
@@ -235,9 +307,9 @@ namespace UVC.UIToolkit
|
|||||||
#region Constructor
|
#region Constructor
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// UTKTopMenuView의 새 인스턴스를 초기화합니다.
|
/// UTKTopMenu의 새 인스턴스를 초기화합니다.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public UTKTopMenuView() : base()
|
public UTKTopMenu() : base()
|
||||||
{
|
{
|
||||||
// 1. 테마 적용
|
// 1. 테마 적용
|
||||||
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
||||||
@@ -540,6 +612,12 @@ namespace UVC.UIToolkit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 최상위 메뉴인 경우 간격 적용
|
||||||
|
if (depth == 0)
|
||||||
|
{
|
||||||
|
ApplyItemSpacing();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -628,6 +706,37 @@ namespace UVC.UIToolkit
|
|||||||
return _menuItemElements.TryGetValue(itemId, out menuItemElement);
|
return _menuItemElements.TryGetValue(itemId, out menuItemElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ItemId로 메뉴 아이템 데이터를 가져옵니다.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="itemId">메뉴 아이템 ID</param>
|
||||||
|
/// <param name="itemData">찾은 메뉴 아이템 데이터</param>
|
||||||
|
/// <returns>찾았으면 true, 그렇지 않으면 false</returns>
|
||||||
|
public bool TryGetMenuItemData(string itemId, out UTKMenuItemData? itemData)
|
||||||
|
{
|
||||||
|
return _menuItemDataMap.TryGetValue(itemId, out itemData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ItemId로 메뉴 아이템의 Command를 실행합니다.
|
||||||
|
/// Command가 없거나 비활성화된 아이템이면 false를 반환합니다.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="itemId">실행할 메뉴 아이템 ID</param>
|
||||||
|
/// <returns>Command가 실행되었으면 true, 그렇지 않으면 false</returns>
|
||||||
|
public bool ExecuteCommand(string itemId)
|
||||||
|
{
|
||||||
|
if (!_menuItemDataMap.TryGetValue(itemId, out var itemData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!itemData.IsEnabled || itemData.Command == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
itemData.Command.Execute(itemData.CommandParameter);
|
||||||
|
OnMenuItemClicked?.Invoke(itemData);
|
||||||
|
CloseAllOpenSubMenus();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 모든 열린 하위 메뉴를 닫습니다.
|
/// 모든 열린 하위 메뉴를 닫습니다.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -658,6 +767,83 @@ namespace UVC.UIToolkit
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Private Methods - 레이아웃
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 메뉴 아이템 간 간격을 적용합니다.
|
||||||
|
/// </summary>
|
||||||
|
private void ApplyItemSpacing()
|
||||||
|
{
|
||||||
|
if (_menuContainer == null) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < _menuContainer.childCount; i++)
|
||||||
|
{
|
||||||
|
var child = _menuContainer[i];
|
||||||
|
if (_orientation == UTKMenuOrientation.Horizontal)
|
||||||
|
{
|
||||||
|
child.style.marginLeft = i > 0 ? _itemSpacing : 0;
|
||||||
|
child.style.marginTop = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
child.style.marginTop = i > 0 ? _itemSpacing : 0;
|
||||||
|
child.style.marginLeft = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 메뉴 컨테이너의 정렬 방향을 적용합니다.
|
||||||
|
/// </summary>
|
||||||
|
private void ApplyOrientation()
|
||||||
|
{
|
||||||
|
if (_menuContainer == null) return;
|
||||||
|
|
||||||
|
if (_orientation == UTKMenuOrientation.Horizontal)
|
||||||
|
{
|
||||||
|
_menuContainer.style.flexDirection = FlexDirection.Row;
|
||||||
|
_menuContainer.RemoveFromClassList("top-menu__items--vertical");
|
||||||
|
_menuContainer.AddToClassList("top-menu__items");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_menuContainer.style.flexDirection = FlexDirection.Column;
|
||||||
|
_menuContainer.RemoveFromClassList("top-menu__items");
|
||||||
|
_menuContainer.AddToClassList("top-menu__items--vertical");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 간격도 방향에 맞게 재적용
|
||||||
|
ApplyItemSpacing();
|
||||||
|
|
||||||
|
// 최상위 메뉴 아이템의 화살표 표시 갱신
|
||||||
|
ApplyTopMenuArrows();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 최상위 메뉴 아이템의 화살표 표시를 정렬 방향에 맞게 갱신합니다.
|
||||||
|
/// </summary>
|
||||||
|
private void ApplyTopMenuArrows()
|
||||||
|
{
|
||||||
|
if (_menuContainer == null) return;
|
||||||
|
|
||||||
|
foreach (var kvp in _menuItemDataMap)
|
||||||
|
{
|
||||||
|
var itemData = kvp.Value;
|
||||||
|
// 최상위 메뉴만 대상 (Depth == 0)
|
||||||
|
if (itemData.Depth != 0) continue;
|
||||||
|
|
||||||
|
if (_menuItemElements.TryGetValue(kvp.Key, out var element) && element is UTKMenuItemBase menuItem)
|
||||||
|
{
|
||||||
|
bool showArrow = _orientation == UTKMenuOrientation.Vertical
|
||||||
|
&& itemData.SubMenuItems != null
|
||||||
|
&& itemData.SubMenuItems.Count > 0;
|
||||||
|
menuItem.ShowArrow(showArrow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Private Methods - 메뉴 아이템 생성
|
#region Private Methods - 메뉴 아이템 생성
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -678,7 +864,12 @@ namespace UVC.UIToolkit
|
|||||||
}
|
}
|
||||||
|
|
||||||
menuItem.SetData(itemData);
|
menuItem.SetData(itemData);
|
||||||
menuItem.ShowArrow(false); // 최상위 메뉴는 화살표 표시하지 않음
|
|
||||||
|
// 세로 정렬 시 서브메뉴가 있으면 화살표 표시
|
||||||
|
bool showArrow = _orientation == UTKMenuOrientation.Vertical
|
||||||
|
&& itemData.SubMenuItems != null
|
||||||
|
&& itemData.SubMenuItems.Count > 0;
|
||||||
|
menuItem.ShowArrow(showArrow);
|
||||||
|
|
||||||
// 클릭 이벤트 등록
|
// 클릭 이벤트 등록
|
||||||
menuItem.OnClicked += (data) =>
|
menuItem.OnClicked += (data) =>
|
||||||
@@ -960,9 +1151,18 @@ namespace UVC.UIToolkit
|
|||||||
|
|
||||||
if (depth == 0)
|
if (depth == 0)
|
||||||
{
|
{
|
||||||
// 1차 메뉴: 아래쪽에 표시 (offset 적용)
|
if (_orientation == UTKMenuOrientation.Horizontal)
|
||||||
subMenuContainer.style.left = menuItemRect.x + _subMenuOffsetX;
|
{
|
||||||
subMenuContainer.style.top = menuItemRect.yMax + _subMenuOffsetY;
|
// 가로 정렬: 아래쪽에 표시 (offset 적용)
|
||||||
|
subMenuContainer.style.left = menuItemRect.x + _subMenuOffsetX;
|
||||||
|
subMenuContainer.style.top = menuItemRect.yMax + _subMenuOffsetY;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 세로 정렬: 오른쪽에 표시 (offset 적용)
|
||||||
|
subMenuContainer.style.left = menuItemRect.xMax + _subMenuOffsetX;
|
||||||
|
subMenuContainer.style.top = menuItemRect.y + _subMenuOffsetY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 2be8c935a6302b648a5c4ce5e6168a98
|
|
||||||
Reference in New Issue
Block a user