261 lines
9.0 KiB
C#
261 lines
9.0 KiB
C#
#nullable enable
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using UVC.UI.Commands;
|
|
|
|
namespace UVC.UIToolkit
|
|
{
|
|
/// <summary>
|
|
/// UIToolkit 메뉴 시스템에서 개별 메뉴 아이템을 나타내는 데이터 클래스입니다.
|
|
/// IDisposable을 구현하여 Command 등의 리소스를 안전하게 정리합니다.
|
|
/// </summary>
|
|
/// <example>
|
|
/// <code>
|
|
/// // 일반 메뉴 아이템 생성
|
|
/// 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();
|
|
/// </code>
|
|
/// </example>
|
|
public class UTKMenuItemData : IDisposable
|
|
{
|
|
#region Properties
|
|
|
|
/// <summary>메뉴 아이템의 고유 식별자</summary>
|
|
public string ItemId { get; private set; }
|
|
|
|
/// <summary>UI에 표시될 이름 (다국어 키)</summary>
|
|
public string DisplayName { get; private set; }
|
|
|
|
/// <summary>실행될 명령</summary>
|
|
public ICommand? Command { get; private set; }
|
|
|
|
/// <summary>Command 실행 시 전달될 파라미터</summary>
|
|
public object? CommandParameter { get; set; }
|
|
|
|
/// <summary>하위 메뉴 아이템 리스트</summary>
|
|
public List<UTKMenuItemData> SubMenuItems { get; private set; }
|
|
|
|
/// <summary>구분선 여부</summary>
|
|
public bool IsSeparator { get; private set; }
|
|
|
|
/// <summary>활성화 상태</summary>
|
|
public bool IsEnabled { get; set; }
|
|
|
|
/// <summary>단축키 문자열</summary>
|
|
public string? Shortcut { get; set; }
|
|
|
|
/// <summary>메뉴 깊이 (0: 최상위)</summary>
|
|
public int Depth { get; internal set; }
|
|
|
|
/// <summary>부모 메뉴 아이템</summary>
|
|
public UTKMenuItemData? Parent { get; internal set; }
|
|
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
/// <summary>
|
|
/// UTKMenuItemData의 새 인스턴스를 초기화합니다.
|
|
/// </summary>
|
|
/// <param name="itemId">메뉴 아이템의 고유 ID</param>
|
|
/// <param name="displayName">표시 이름 (다국어 키)</param>
|
|
/// <param name="command">실행할 명령 (선택 사항)</param>
|
|
/// <param name="commandParameter">Command 파라미터 (선택 사항)</param>
|
|
/// <param name="subMenuItems">하위 메뉴 아이템 목록 (선택 사항)</param>
|
|
/// <param name="isSeparator">구분선 여부 (기본값: false)</param>
|
|
/// <param name="isEnabled">활성화 상태 (기본값: true)</param>
|
|
/// <param name="shortcut">단축키 문자열 (선택 사항)</param>
|
|
/// <exception cref="ArgumentNullException">itemId 또는 displayName이 null인 경우</exception>
|
|
public UTKMenuItemData(
|
|
string itemId,
|
|
string displayName,
|
|
ICommand? command = null,
|
|
object? commandParameter = null,
|
|
List<UTKMenuItemData>? 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<UTKMenuItemData>();
|
|
IsSeparator = isSeparator;
|
|
IsEnabled = isEnabled;
|
|
Depth = 0;
|
|
Shortcut = shortcut;
|
|
|
|
// 하위 메뉴 아이템의 깊이와 부모 관계 설정
|
|
SetupDepthAndParent();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
/// <summary>
|
|
/// 하위 메뉴 아이템을 추가합니다.
|
|
/// </summary>
|
|
/// <param name="subItem">추가할 하위 메뉴 아이템</param>
|
|
/// <exception cref="ArgumentNullException">subItem이 null인 경우</exception>
|
|
/// <exception cref="InvalidOperationException">구분선에 하위 메뉴를 추가하려는 경우</exception>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 구분선을 생성하는 팩토리 메서드입니다.
|
|
/// </summary>
|
|
/// <param name="itemId">구분선의 고유 ID (null일 경우 GUID로 자동 생성)</param>
|
|
/// <returns>구분선 역할을 하는 새로운 UTKMenuItemData 객체</returns>
|
|
public static UTKMenuItemData CreateSeparator(string? itemId = null)
|
|
{
|
|
return new UTKMenuItemData(
|
|
itemId ?? $"separator_{Guid.NewGuid()}",
|
|
string.Empty,
|
|
null,
|
|
null,
|
|
null,
|
|
true
|
|
);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 특정 ID의 하위 메뉴 아이템이 존재하는지 확인합니다.
|
|
/// </summary>
|
|
/// <param name="itemId">확인할 메뉴 아이템 ID</param>
|
|
/// <returns>하위 메뉴에 해당 ID가 존재하면 true, 그렇지 않으면 false</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 모든 하위 메뉴 항목의 깊이와 부모 관계를 구성합니다.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 이 메서드는 하위 메뉴 항목 컬렉션을 반복하며 깊이와 부모 속성을 설정합니다.
|
|
/// 깊이는 현재 항목의 깊이에 따라 증가하고, 부모는 현재 항목으로 설정됩니다.
|
|
/// 하위 메뉴 항목에 자체 하위 메뉴가 포함된 경우, 재귀적으로 호출됩니다.
|
|
/// </remarks>
|
|
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;
|
|
|
|
/// <summary>
|
|
/// 리소스를 정리합니다. Command가 IDisposable인 경우 함께 정리합니다.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 리소스를 정리합니다.
|
|
/// </summary>
|
|
/// <param name="disposing">관리되는 리소스를 정리할지 여부</param>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 소멸자
|
|
/// </summary>
|
|
~UTKMenuItemData()
|
|
{
|
|
Dispose(false);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|