#nullable enable using System; using System.Collections.Generic; using UVC.UI.Commands; namespace UVC.UIToolkit { /// /// UIToolkit 메뉴 시스템에서 개별 메뉴 아이템을 나타내는 데이터 클래스입니다. /// IDisposable을 구현하여 Command 등의 리소스를 안전하게 정리합니다. /// /// /// /// // 일반 메뉴 아이템 생성 /// var menuItem = new UTKMenuItemData( /// "file_open", /// "menu_file_open", /// new OpenFileCommand(), /// shortcut: "Ctrl+O" /// ); /// /// // 하위 메뉴가 있는 아이템 생성 /// var fileMenu = new UTKMenuItemData("file", "menu_file"); /// fileMenu.AddSubMenuItem(menuItem); /// /// // 구분선 생성 /// var separator = UTKMenuItemData.CreateSeparator(); /// /// // 사용 후 정리 /// menuItem.Dispose(); /// fileMenu.Dispose(); /// /// public class UTKMenuItemData : IDisposable { #region Properties /// 메뉴 아이템의 고유 식별자 public string ItemId { get; private set; } /// UI에 표시될 이름 (다국어 키) public string DisplayName { get; private set; } /// 실행될 명령 public ICommand? Command { get; private set; } /// Command 실행 시 전달될 파라미터 public object? CommandParameter { get; set; } /// 하위 메뉴 아이템 리스트 public List SubMenuItems { get; private set; } /// 구분선 여부 public bool IsSeparator { get; private set; } /// 활성화 상태 public bool IsEnabled { get; set; } /// 단축키 문자열 public string? Shortcut { get; set; } /// 메뉴 깊이 (0: 최상위) public int Depth { get; internal set; } /// 부모 메뉴 아이템 public UTKMenuItemData? Parent { get; internal set; } #endregion #region Constructor /// /// UTKMenuItemData의 새 인스턴스를 초기화합니다. /// /// 메뉴 아이템의 고유 ID /// 표시 이름 (다국어 키) /// 실행할 명령 (선택 사항) /// Command 파라미터 (선택 사항) /// 하위 메뉴 아이템 목록 (선택 사항) /// 구분선 여부 (기본값: false) /// 활성화 상태 (기본값: true) /// 단축키 문자열 (선택 사항) /// itemId 또는 displayName이 null인 경우 public UTKMenuItemData( string itemId, string displayName, ICommand? command = null, object? commandParameter = null, List? subMenuItems = null, bool isSeparator = false, bool isEnabled = true, string? shortcut = null) { if (string.IsNullOrEmpty(itemId)) throw new ArgumentNullException(nameof(itemId), "ItemId는 null이거나 빈 문자열일 수 없습니다."); ItemId = itemId; DisplayName = displayName ?? string.Empty; Command = command; CommandParameter = commandParameter; SubMenuItems = subMenuItems ?? new List(); IsSeparator = isSeparator; IsEnabled = isEnabled; Depth = 0; Shortcut = shortcut; // 하위 메뉴 아이템의 깊이와 부모 관계 설정 SetupDepthAndParent(); } #endregion #region Methods /// /// 하위 메뉴 아이템을 추가합니다. /// /// 추가할 하위 메뉴 아이템 /// subItem이 null인 경우 /// 구분선에 하위 메뉴를 추가하려는 경우 public void AddSubMenuItem(UTKMenuItemData subItem) { if (_disposed) throw new ObjectDisposedException(nameof(UTKMenuItemData), "이미 정리된 객체에 하위 메뉴를 추가할 수 없습니다."); if (subItem == null) throw new ArgumentNullException(nameof(subItem), "추가할 하위 메뉴 아이템이 null입니다."); if (IsSeparator) throw new InvalidOperationException("구분선에는 하위 메뉴를 추가할 수 없습니다."); // 깊이와 부모 관계 설정 subItem.Depth = this.Depth + 1; subItem.Parent = this; SubMenuItems.Add(subItem); } /// /// 구분선을 생성하는 팩토리 메서드입니다. /// /// 구분선의 고유 ID (null일 경우 GUID로 자동 생성) /// 구분선 역할을 하는 새로운 UTKMenuItemData 객체 public static UTKMenuItemData CreateSeparator(string? itemId = null) { return new UTKMenuItemData( itemId ?? $"separator_{Guid.NewGuid()}", string.Empty, null, null, null, true ); } /// /// 특정 ID의 하위 메뉴 아이템이 존재하는지 확인합니다. /// /// 확인할 메뉴 아이템 ID /// 하위 메뉴에 해당 ID가 존재하면 true, 그렇지 않으면 false public bool HasSubMenuItem(string itemId) { if (string.IsNullOrEmpty(itemId)) return false; // 성능 최적화: StringComparison.Ordinal 사용 foreach (var item in SubMenuItems) { if (string.Equals(item.ItemId, itemId, StringComparison.Ordinal)) return true; } return false; } /// /// 모든 하위 메뉴 항목의 깊이와 부모 관계를 구성합니다. /// /// /// 이 메서드는 하위 메뉴 항목 컬렉션을 반복하며 깊이와 부모 속성을 설정합니다. /// 깊이는 현재 항목의 깊이에 따라 증가하고, 부모는 현재 항목으로 설정됩니다. /// 하위 메뉴 항목에 자체 하위 메뉴가 포함된 경우, 재귀적으로 호출됩니다. /// private void SetupDepthAndParent() { for (int i = 0; i < SubMenuItems.Count; i++) { SubMenuItems[i].Depth = this.Depth + 1; SubMenuItems[i].Parent = this; if (SubMenuItems[i].SubMenuItems.Count > 0) { SubMenuItems[i].SetupDepthAndParent(); } } } #endregion #region IDisposable private bool _disposed; /// /// 리소스를 정리합니다. Command가 IDisposable인 경우 함께 정리합니다. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// 리소스를 정리합니다. /// /// 관리되는 리소스를 정리할지 여부 protected virtual void Dispose(bool disposing) { if (_disposed) return; if (disposing) { // 하위 메뉴 아이템 재귀적으로 정리 if (SubMenuItems != null) { foreach (var subItem in SubMenuItems) { subItem?.Dispose(); } SubMenuItems.Clear(); } // Command가 IDisposable이면 정리 if (Command is IDisposable disposableCommand) { disposableCommand.Dispose(); } // 참조 정리 Command = null; CommandParameter = null; Parent = null; } _disposed = true; } /// /// 소멸자 /// ~UTKMenuItemData() { Dispose(false); } #endregion } }