#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
}
}