toolbar 코드 개선
This commit is contained in:
40
Assets/Scripts/UVC/UI/ToolBar/Model/IToolbarItem.cs
Normal file
40
Assets/Scripts/UVC/UI/ToolBar/Model/IToolbarItem.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
namespace UVC.UI.Toolbar.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// 툴바에 추가될 수 있는 모든 항목(예: 버튼, 구분선, 드롭다운 메뉴 등)의
|
||||
/// 기본 인터페이스입니다.
|
||||
/// 이 인터페이스를 구현하는 클래스는 툴바 시스템의 일부로 인식되어
|
||||
/// ToolbarModel에 추가되고 ToolbarView에 의해 렌더링될 수 있습니다.
|
||||
///
|
||||
/// 역할:
|
||||
/// - 툴바 항목의 통일성: 다양한 UI 요소들을 툴바 시스템 내에서 동일한 타입(IToolbarItem)으로 취급할 수 있게 합니다.
|
||||
/// - 확장성: 새로운 종류의 툴바 항목을 추가할 때, 이 인터페이스를 구현함으로써
|
||||
/// 기존 툴바 시스템과 쉽게 통합될 수 있도록 합니다.
|
||||
///
|
||||
/// 사용 예시:
|
||||
/// - ToolbarButtonBase: 모든 버튼 타입의 기본 클래스로, IToolbarItem을 구현합니다.
|
||||
/// - ToolbarSeparator: 툴바 내 항목들을 시각적으로 구분하는 구분선 클래스로, IToolbarItem을 구현합니다.
|
||||
///
|
||||
/// 개발자가 새로운 툴바 항목(예: 색상 선택기, 슬라이더 등)을 만들고 싶다면,
|
||||
/// 해당 클래스가 IToolbarItem 인터페이스를 구현하도록 해야 합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // 새로운 툴바 항목으로 'MyCustomToolbarItem'을 정의하는 경우
|
||||
/// public class MyCustomToolbarItem : IToolbarItem
|
||||
/// {
|
||||
/// // 이 항목에 필요한 속성 및 메서드 정의
|
||||
/// public string CustomData { get; set; }
|
||||
/// public void PerformAction() { /* ... */ }
|
||||
/// }
|
||||
///
|
||||
/// // ToolbarModel에 추가하는 방법
|
||||
/// ToolbarModel toolbarModel = new ToolbarModel();
|
||||
/// MyCustomToolbarItem customItem = new MyCustomToolbarItem { CustomData = "Example" };
|
||||
/// toolbarModel.AddItem(customItem);
|
||||
/// // ToolbarView는 이 customItem을 렌더링하기 위한 로직이 추가적으로 필요할 수 있습니다.
|
||||
/// // (예: MyCustomToolbarItem 타입에 맞는 프리팹과 렌더링 로직)
|
||||
/// </code>
|
||||
/// </example>
|
||||
public interface IToolbarItem { }
|
||||
}
|
||||
2
Assets/Scripts/UVC/UI/ToolBar/Model/IToolbarItem.cs.meta
Normal file
2
Assets/Scripts/UVC/UI/ToolBar/Model/IToolbarItem.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b28bebc44774c6f4f95cb5e612baef9f
|
||||
230
Assets/Scripts/UVC/UI/ToolBar/Model/ToolbarButtonBase.cs
Normal file
230
Assets/Scripts/UVC/UI/ToolBar/Model/ToolbarButtonBase.cs
Normal file
@@ -0,0 +1,230 @@
|
||||
using System;
|
||||
using UVC.UI.Commands;
|
||||
|
||||
namespace UVC.UI.Toolbar.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// 툴바에 사용되는 모든 버튼(표준 버튼, 토글 버튼, 확장 버튼 등)의 기본 추상 클래스입니다.
|
||||
/// IToolbarItem 인터페이스를 구현하여 툴바 시스템의 일부가 됩니다.
|
||||
/// 버튼이 가져야 할 공통적인 속성(예: 텍스트, 아이콘 경로, 활성화 상태, 툴팁 키)과
|
||||
/// 기본적인 동작(예: 클릭 시 커맨드 실행, 상태 변경 알림)을 정의합니다.
|
||||
///
|
||||
/// 이 클래스를 상속받아 특정 유형의 버튼(예: ToolbarStandardButton, ToolbarToggleButton)을 구현할 수 있습니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // ToolbarButtonBase를 상속받는 사용자 정의 버튼 예시
|
||||
/// public class MyCustomButton : ToolbarButtonBase
|
||||
/// {
|
||||
/// public string CustomProperty { get; set; }
|
||||
///
|
||||
/// public MyCustomButton(string text, string iconPath, ICommand command, string tooltip = null)
|
||||
/// {
|
||||
/// Text = text; // 부모 클래스의 Text 속성 사용
|
||||
/// IconSpritePath = iconPath; // 부모 클래스의 IconSpritePath 속성 사용
|
||||
/// ClickCommand = command; // 부모 클래스의 ClickCommand 속성 사용
|
||||
/// Tooltip = tooltip; // 부모 클래스의 Tooltip 속성 사용
|
||||
/// IsEnabled = true; // 기본적으로 활성화 상태로 설정
|
||||
/// }
|
||||
///
|
||||
/// public override void ExecuteClick(object parameter = null)
|
||||
/// {
|
||||
/// if (!IsEnabled) return; // 비활성화 상태면 아무것도 하지 않음
|
||||
///
|
||||
/// Debug.Log($"MyCustomButton '{Text}' clicked!");
|
||||
/// // 기본 클릭 로직 실행 (예: ClickCommand 실행)
|
||||
/// base.ExecuteClick(parameter);
|
||||
///
|
||||
/// // 이 버튼만의 추가적인 로직 수행
|
||||
/// // PerformCustomAction();
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // 사용 예
|
||||
/// // ICommand saveCommand = new ActionCommand(() => { Debug.Log("Save action triggered!"); });
|
||||
/// // MyCustomButton saveButton = new MyCustomButton("Save", "icons/save_icon", saveCommand, "Save the current file");
|
||||
/// // toolbarModel.AddItem(saveButton);
|
||||
/// </code>
|
||||
/// </example>
|
||||
public abstract class ToolbarButtonBase : IToolbarItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 버튼의 상태(예: Text, IconSpritePath, IsEnabled)가 변경되었을 때 발생하는 이벤트입니다.
|
||||
/// View 레이어(예: ToolbarView)는 이 이벤트를 구독하여 버튼의 시각적 표현을 업데이트합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 속성(Text, IconSpritePath, IsEnabled)의 setter 내부에서 자동으로 호출됩니다.
|
||||
/// 여러 상태를 한 번에 변경한 후 수동으로 알리고 싶다면 NotifyStateChanged() 메서드를 사용할 수 있습니다.
|
||||
/// </remarks>
|
||||
public event Action OnStateChanged;
|
||||
|
||||
protected string _text;
|
||||
/// <summary>
|
||||
/// 버튼에 표시될 텍스트 또는 텍스트의 다국어 키입니다.
|
||||
/// 값이 변경되면 OnStateChanged 이벤트가 발생합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// button.Text = "새 작업"; // 직접 텍스트 설정
|
||||
/// button.Text = "toolbar_button_new_task_key"; // 다국어 키 설정 (LocalizationManager가 이 키를 실제 텍스트로 변환)
|
||||
/// </code>
|
||||
/// </example>
|
||||
public string Text
|
||||
{
|
||||
get => _text;
|
||||
set
|
||||
{
|
||||
if (_text != value)
|
||||
{
|
||||
_text = value;
|
||||
OnStateChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected string _iconSpritePath;
|
||||
/// <summary>
|
||||
/// 버튼에 표시될 아이콘의 Resources 폴더 내 경로입니다 (확장자 제외).
|
||||
/// 값이 변경되면 OnStateChanged 이벤트가 발생합니다.
|
||||
/// 아이콘이 없는 경우 null 또는 빈 문자열로 설정할 수 있습니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// button.IconSpritePath = "ToolbarIcons/OpenIcon"; // Resources/ToolbarIcons/OpenIcon.png (또는 다른 지원 형식)
|
||||
/// </code>
|
||||
/// </example>
|
||||
public string IconSpritePath
|
||||
{
|
||||
get => _iconSpritePath;
|
||||
set
|
||||
{
|
||||
if (_iconSpritePath != value)
|
||||
{
|
||||
_iconSpritePath = value;
|
||||
OnStateChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected bool _isEnabled = true;
|
||||
/// <summary>
|
||||
/// 버튼의 활성화 상태를 나타냅니다. true이면 사용자와 상호작용할 수 있고, false이면 비활성화되어 상호작용할 수 없습니다.
|
||||
/// 값이 변경되면 OnStateChanged 이벤트가 발생합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// button.IsEnabled = false; // 버튼 비활성화
|
||||
/// if (button.IsEnabled) { /* 버튼 사용 가능 로직 */ }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => _isEnabled;
|
||||
set
|
||||
{
|
||||
if (_isEnabled != value)
|
||||
{
|
||||
_isEnabled = value;
|
||||
OnStateChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected string _tooltip;
|
||||
/// <summary>
|
||||
/// 버튼에 마우스를 올렸을 때 표시될 툴팁의 텍스트 또는 다국어 키입니다.
|
||||
/// Tooltip 변경 시에는 기본적으로 OnStateChanged 이벤트가 발생하지 않지만,
|
||||
/// 필요에 따라 View에서 이 값을 직접 참조하여 툴팁을 업데이트할 수 있습니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// button.Tooltip = "tooltip_save_button"; // 다국어 키 사용
|
||||
/// button.Tooltip = "클릭하여 문서를 저장합니다."; // 직접 텍스트 사용
|
||||
/// </code>
|
||||
/// </example>
|
||||
public string Tooltip
|
||||
{
|
||||
get => _tooltip;
|
||||
set
|
||||
{
|
||||
if (_tooltip != value)
|
||||
{
|
||||
_tooltip = value;
|
||||
// Tooltip 변경 시 OnStateChanged를 호출할 필요는 일반적으로 없으나,
|
||||
// 만약 UI가 Tooltip 자체를 표시하는 등의 로직이 있다면 필요할 수 있습니다.
|
||||
// 여기서는 툴팁 내용이 동적으로 변경되는 경우가 적다고 가정하고 생략합니다.
|
||||
// 필요하다면 OnStateChanged?.Invoke(); 추가
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 버튼이 클릭되었을 때 실행될 동작을 정의하는 커맨드 객체입니다.
|
||||
/// ICommand 인터페이스를 구현한 객체를 할당합니다 (예: ActionCommand).
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // ActionCommand를 사용하여 간단한 동작 정의
|
||||
/// button.ClickCommand = new ActionCommand(() =>
|
||||
/// {
|
||||
/// Debug.Log($"'{button.Text}' 버튼이 클릭되었습니다.");
|
||||
/// // 특정 기능 수행
|
||||
/// });
|
||||
///
|
||||
/// // 파라미터가 있는 커맨드
|
||||
/// button.ClickCommand = new ActionCommand<string>((fileName) =>
|
||||
/// {
|
||||
/// Debug.Log($"파일 열기: {fileName}");
|
||||
/// });
|
||||
/// // ExecuteClick 메서드 호출 시 파라미터 전달 필요
|
||||
/// // button.ExecuteClick("MyDocument.txt");
|
||||
/// </code>
|
||||
/// </example>
|
||||
public ICommand ClickCommand { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 버튼 클릭 로직을 실행합니다.
|
||||
/// 이 메서드는 일반적으로 UI 시스템(예: Unity UI의 Button.onClick 이벤트)에 의해 호출되도록 설계됩니다.
|
||||
/// 버튼이 활성화(IsEnabled == true)되어 있고 ClickCommand가 할당되어 있다면, 해당 커맨드를 실행합니다.
|
||||
/// 파생 클래스에서 이 메서드를 재정의하여 특정 버튼 타입에 맞는 추가적인 클릭 동작을 구현할 수 있습니다.
|
||||
/// </summary>
|
||||
/// <param name="parameter">ClickCommand에 전달할 선택적 파라미터입니다.</param>
|
||||
public virtual void ExecuteClick(object parameter = null)
|
||||
{
|
||||
if (IsEnabled && ClickCommand != null)
|
||||
{
|
||||
ClickCommand.Execute(parameter); // 커맨드에 파라미터 전달
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnStateChanged 이벤트를 외부에서 강제로 발생시킵니다.
|
||||
/// 여러 속성을 변경한 후 한 번에 UI 업데이트를 트리거하거나,
|
||||
/// 내부 상태 변경이 속성 변경을 통하지 않고 발생했을 때 유용합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// public void UpdateButtonAppearance(string newText, string newIcon)
|
||||
/// {
|
||||
/// _text = newText; // 직접 필드 값 변경 (setter의 OnStateChanged 호출 안됨)
|
||||
/// _iconSpritePath = newIcon; // 직접 필드 값 변경
|
||||
/// NotifyStateChanged(); // 수동으로 상태 변경 알림
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public void NotifyStateChanged()
|
||||
{
|
||||
OnStateChanged?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 이 버튼 모델에 연결된 모든 이벤트 핸들러를 정리(구독 해제)합니다.
|
||||
/// 주로 View가 파괴되거나 UI가 재생성될 때 호출되어 메모리 누수를 방지합니다.
|
||||
/// 파생 클래스에서는 이 메서드를 재정의하여 해당 클래스에 특화된 이벤트를 추가로 정리해야 합니다.
|
||||
/// </summary>
|
||||
public virtual void ClearEventHandlers()
|
||||
{
|
||||
OnStateChanged = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d8d1e1abfa138645b33ce2f969df756
|
||||
161
Assets/Scripts/UVC/UI/ToolBar/Model/ToolbarExpandableButton.cs
Normal file
161
Assets/Scripts/UVC/UI/ToolBar/Model/ToolbarExpandableButton.cs
Normal file
@@ -0,0 +1,161 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
// using UnityEngine.UI; // Image 사용을 위해 필요할 수 있으나, 모델 클래스에서는 직접적인 UI 참조를 최소화하는 것이 좋음
|
||||
|
||||
namespace UVC.UI.Toolbar.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// 클릭 시 하위 버튼 목록(서브 메뉴)을 표시하거나 숨길 수 있는 확장형 버튼입니다.
|
||||
/// ToolbarButtonBase를 상속받아 기본적인 버튼 속성(텍스트, 아이콘, 커맨드 등)을 가집니다.
|
||||
/// 주 버튼을 클릭하면 연결된 서브 메뉴가 표시되며, 서브 메뉴의 버튼을 선택하면
|
||||
/// 주 버튼의 텍스트나 아이콘이 선택된 하위 버튼의 것으로 변경될 수 있습니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 이 모델 클래스는 데이터와 상태를 관리합니다. 실제 서브 메뉴의 표시/숨김 및 UI 렌더링은
|
||||
/// ToolbarView와 같은 View 클래스에서 처리됩니다.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Toolbar 등에서 확장 버튼 생성 및 설정 예시
|
||||
/// var brushToolButton = mainToolbar.AddExpandableButton(
|
||||
/// "brush_tool_main", // 주 버튼 텍스트 (또는 다국어 키)
|
||||
/// "icons/brush_default", // 주 버튼 기본 아이콘 경로
|
||||
/// new ActionCommand(() => Debug.Log("브러시 주 버튼 클릭됨 (하위 메뉴 토글은 View에서 처리)")),
|
||||
/// "tooltip_brush_tool" // 주 버튼 툴팁 키
|
||||
/// );
|
||||
///
|
||||
/// // 하위 버튼1: 작은 브러시
|
||||
/// var smallBrush = new ToolbarStandardButton
|
||||
/// {
|
||||
/// Text = "brush_small",
|
||||
/// IconSpritePath = "icons/brush_small_icon",
|
||||
/// Tooltip = "tooltip_brush_small",
|
||||
/// ClickCommand = new ActionCommand(() => Debug.Log("작은 브러시 선택됨"))
|
||||
/// };
|
||||
/// brushToolButton.SubButtons.Add(smallBrush);
|
||||
///
|
||||
/// // 하위 버튼2: 중간 브러시
|
||||
/// var mediumBrush = new ToolbarStandardButton
|
||||
/// {
|
||||
/// Text = "brush_medium",
|
||||
/// IconSpritePath = "icons/brush_medium_icon",
|
||||
/// Tooltip = "tooltip_brush_medium",
|
||||
/// ClickCommand = new ActionCommand(() => Debug.Log("중간 브러시 선택됨"))
|
||||
/// };
|
||||
/// brushToolButton.SubButtons.Add(mediumBrush);
|
||||
///
|
||||
/// // 하위 버튼 선택 시 콜백 설정 (선택 사항)
|
||||
/// brushToolButton.OnSubButtonSelected = (selectedSub) =>
|
||||
/// {
|
||||
/// Debug.Log($"하위 버튼 '{selectedSub.Text}' 선택됨. 주 버튼 아이콘/텍스트 업데이트됨.");
|
||||
/// // 여기서 추가적인 로직 수행 가능 (예: 실제 브러시 크기 변경)
|
||||
/// };
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class ToolbarExpandableButton : ToolbarButtonBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 이 확장 버튼에 속한 하위 버튼들의 목록입니다.
|
||||
/// 이 목록에 있는 버튼들은 주 버튼 클릭 시 View에 의해 표시될 수 있습니다.
|
||||
/// 각 하위 버튼은 ToolbarButtonBase 또는 그 파생 클래스의 인스턴스여야 합니다.
|
||||
/// </summary>
|
||||
public List<ToolbarButtonBase> SubButtons { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 하위 버튼 중 하나가 선택되었을 때 호출되는 액션(콜백)입니다.
|
||||
/// 선택된 하위 버튼 객체가 파라미터로 전달됩니다.
|
||||
/// 주 버튼의 모양이 변경된 후, 추가적인 로직을 수행하고자 할 때 사용합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 이 콜백은 SelectSubButton 메서드 내에서, 주 버튼의 Text/IconSpritePath가 업데이트된 후 호출됩니다.
|
||||
/// </remarks>
|
||||
public Action<ToolbarButtonBase> OnSubButtonSelected { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ToolbarExpandableButton의 새 인스턴스를 초기화합니다.
|
||||
/// SubButtons 리스트를 빈 리스트로 생성합니다.
|
||||
/// </summary>
|
||||
public ToolbarExpandableButton()
|
||||
{
|
||||
SubButtons = new List<ToolbarButtonBase>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 주 확장 버튼이 클릭되었을 때의 로직을 실행합니다.
|
||||
/// 기본적으로 부모 클래스(ToolbarButtonBase)의 ExecuteClick을 호출하여
|
||||
/// 할당된 ClickCommand를 실행합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 하위 메뉴를 실제로 열고 닫는 동작은 View 레이어(예: ToolbarView)에서 이 버튼의 클릭 이벤트를 감지하여
|
||||
/// 별도로 처리하는 것이 일반적입니다. 이 메서드는 주 버튼 자체의 커맨드 실행에 중점을 둡니다.
|
||||
/// </remarks>
|
||||
/// <param name="parameter">ClickCommand에 전달할 파라미터입니다.</param>
|
||||
public override void ExecuteClick(object parameter = null)
|
||||
{
|
||||
if (!IsEnabled) return;
|
||||
|
||||
// 주 버튼 자체에 할당된 ClickCommand 실행 (예: 특정 모드 진입, 상태 변경 등)
|
||||
base.ExecuteClick(parameter);
|
||||
|
||||
// 하위 메뉴를 여는 시각적 동작은 View(예: ToolbarView)에서 이 버튼의 UI 클릭 이벤트를 받아 처리합니다.
|
||||
// 예를 들어, ToolbarView에서는 이 ExecuteClick() 호출 후, ToggleSubMenu(this)와 같은 메서드를 호출할 수 있습니다.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 지정된 하위 버튼을 "선택된" 상태로 처리하고, 주 버튼의 모양(텍스트, 아이콘)을
|
||||
/// 선택된 하위 버튼의 것으로 업데이트합니다.
|
||||
/// OnSubButtonSelected 콜백이 설정되어 있다면 호출합니다.
|
||||
/// </summary>
|
||||
/// <param name="selectedSubButton">선택된 하위 버튼 객체입니다. ToolbarButtonBase 또는 그 파생 타입이어야 합니다.</param>
|
||||
/// <param name="buttonObj">
|
||||
/// [주의] 이 파라미터는 View 레이어의 GameObject를 참조하며, 모델 클래스 설계 원칙에 따르면
|
||||
/// 모델이 View 객체를 직접 참조하는 것은 바람직하지 않을 수 있습니다.
|
||||
/// 현재 코드(ToolbarExpandableButton.cs의 SelectSubButton)에서는 아이콘 즉시 로드를 위해 사용되고 있으나,
|
||||
/// 이상적으로는 아이콘 업데이트도 OnStateChanged 이벤트를 통해 View에서 처리하는 것이 좋습니다.
|
||||
/// 이 파라미터는 향후 리팩토링 대상이 될 수 있습니다. 현재는 기존 코드 구조를 유지합니다.
|
||||
/// </param>
|
||||
public void SelectSubButton(ToolbarButtonBase selectedSubButton)
|
||||
{
|
||||
if (selectedSubButton != null && selectedSubButton.IsEnabled)
|
||||
{
|
||||
// 주 버튼의 텍스트를 선택된 하위 버튼의 텍스트로 변경
|
||||
// Text 속성의 setter는 내부적으로 OnStateChanged를 호출하여 View 업데이트를 트리거합니다.
|
||||
if (this.Text != selectedSubButton.Text)
|
||||
{
|
||||
this.Text = selectedSubButton.Text;
|
||||
}
|
||||
|
||||
// 주 버튼의 아이콘 경로를 선택된 하위 버튼의 아이콘 경로로 변경
|
||||
// IconSpritePath 속성의 setter는 내부적으로 OnStateChanged를 호출합니다.
|
||||
if (this.IconSpritePath != selectedSubButton.IconSpritePath)
|
||||
{
|
||||
this.IconSpritePath = selectedSubButton.IconSpritePath;
|
||||
}
|
||||
|
||||
// 하위 버튼 선택 콜백 호출
|
||||
OnSubButtonSelected?.Invoke(selectedSubButton);
|
||||
|
||||
// 선택된 하위 버튼 자체의 ClickCommand 실행은 여기서 하지 않습니다.
|
||||
// View에서 하위 버튼 UI 클릭 시 해당 하위 버튼의 ExecuteClick()이 직접 호출되는 것이 일반적입니다.
|
||||
// 만약 여기서 실행해야 한다면: selectedSubButton.ExecuteClick();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 이 버튼 모델 및 모든 하위 버튼 모델에 연결된 이벤트 핸들러를 정리합니다.
|
||||
/// </summary>
|
||||
public override void ClearEventHandlers()
|
||||
{
|
||||
base.ClearEventHandlers(); // 부모 클래스의 이벤트 정리 (OnStateChanged)
|
||||
OnSubButtonSelected = null;
|
||||
|
||||
if (SubButtons != null)
|
||||
{
|
||||
foreach (var subButton in SubButtons)
|
||||
{
|
||||
subButton?.ClearEventHandlers(); // 각 하위 버튼의 이벤트 핸들러도 정리
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d7b463d85335feb4db9a045177ee9221
|
||||
235
Assets/Scripts/UVC/UI/ToolBar/Model/ToolbarModel.cs
Normal file
235
Assets/Scripts/UVC/UI/ToolBar/Model/ToolbarModel.cs
Normal file
@@ -0,0 +1,235 @@
|
||||
using UVC.UI.Commands;
|
||||
|
||||
namespace UVC.UI.Toolbar.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// 툴바의 전체적인 데이터 컨테이너 및 관리 클래스입니다.
|
||||
/// 툴바에 표시될 모든 항목(IToolbarItem)들의 목록을 관리하며,
|
||||
/// 각 항목의 생성 및 초기 설정을 위한 헬퍼 메서드들을 제공합니다.
|
||||
/// 이 모델은 UI 표현(View)과 로직(Controller)으로부터 분리되어 툴바의 순수한 데이터 구조를 나타냅니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 역할:
|
||||
/// - 툴바 항목 저장: 툴바에 포함될 버튼, 구분선 등의 IToolbarItem 객체들을 리스트 형태로 보관합니다.
|
||||
/// - 툴바 항목 생성: AddStandardButton, AddToggleButton 등의 팩토리 메서드를 통해 특정 타입의 툴바 항목을 쉽게 생성하고 모델에 추가할 수 있도록 합니다.
|
||||
/// - 라디오 버튼 그룹 관리: 동일한 그룹 이름을 가진 ToolbarRadioButton들을 내부적으로 ToolbarRadioButtonGroup으로 묶어 관리합니다.
|
||||
///
|
||||
/// 사용 흐름:
|
||||
/// 1. Toolbar (또는 유사한 관리 클래스)에서 ToolbarModel의 인스턴스를 생성합니다.
|
||||
/// 2. 다양한 Add[ButtonType] 메서드 또는 AddItem 메서드를 호출하여 툴바 항목 모델들을 생성하고 ToolbarModel에 추가합니다.
|
||||
/// 3. 설정이 완료된 ToolbarModel 객체를 ToolbarView의 Initialize 메서드에 전달하여 UI를 렌더링하도록 합니다.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Toolbar 등에서 ToolbarModel 사용 예시
|
||||
///
|
||||
/// // 1. ToolbarModel 인스턴스 생성
|
||||
/// ToolbarModel myToolbar = new ToolbarModel();
|
||||
///
|
||||
/// // 2. 표준 버튼 추가
|
||||
/// ICommand openCommand = new ActionCommand(() => Debug.Log("파일 열기 실행"));
|
||||
/// myToolbar.AddStandardButton("button_open_file", "icons/open", openCommand, "tooltip_open_file_description");
|
||||
///
|
||||
/// // 3. 구분선 추가
|
||||
/// myToolbar.AddSeparator();
|
||||
///
|
||||
/// // 4. 토글 버튼 추가
|
||||
/// myToolbar.AddToggleButton(
|
||||
/// "button_toggle_grid",
|
||||
/// false, // 초기 상태 (꺼짐)
|
||||
/// "icons/grid_on", // 켜짐 상태 아이콘
|
||||
/// "icons/grid_off", // 꺼짐 상태 아이콘
|
||||
/// (isSelected) => Debug.Log($"그리드 표시: {isSelected}"), // OnToggle 콜백
|
||||
/// null, // ClickCommand (필요시 설정)
|
||||
/// "tooltip_toggle_grid_visibility"
|
||||
/// );
|
||||
///
|
||||
/// // 5. 라디오 버튼 그룹 추가
|
||||
/// string viewModeGroup = "ViewModeSelection";
|
||||
/// myToolbar.AddRadioButton(viewModeGroup, "button_2d_view", true, "icons/2d_view", null,
|
||||
/// (isSelected) => { if(isSelected) Debug.Log("2D 뷰 선택됨"); },
|
||||
/// null, "tooltip_2d_view");
|
||||
/// myToolbar.AddRadioButton(viewModeGroup, "button_3d_view", false, "icons/3d_view", null,
|
||||
/// (isSelected) => { if(isSelected) Debug.Log("3D 뷰 선택됨"); },
|
||||
/// null, "tooltip_3d_view");
|
||||
///
|
||||
/// // 6. 설정된 모델을 ToolbarView에 전달
|
||||
/// // toolbarView.Initialize(myToolbar);
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class ToolbarModel
|
||||
{
|
||||
/// <summary>
|
||||
/// 툴바에 추가된 모든 항목(IToolbarItem)들의 리스트입니다.
|
||||
/// 이 리스트의 순서대로 툴바에 항목들이 표시됩니다.
|
||||
/// 외부에서는 읽기만 가능하도록 private set으로 설정되어 있으며, 항목 추가는 AddItem 또는 Add[ButtonType] 메서드를 통해 이루어집니다.
|
||||
/// </summary>
|
||||
public System.Collections.Generic.List<IToolbarItem> Items { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 라디오 버튼 그룹들을 관리하는 딕셔너리입니다.
|
||||
/// 키는 라디오 버튼의 GroupName이며, 값은 해당 그룹을 관리하는 ToolbarRadioButtonGroup 객체입니다.
|
||||
/// ToolbarRadioButton이 AddItem을 통해 추가될 때 내부적으로 사용됩니다.
|
||||
/// </summary>
|
||||
private System.Collections.Generic.Dictionary<string, ToolbarRadioButtonGroup> _radioGroups;
|
||||
|
||||
/// <summary>
|
||||
/// ToolbarModel의 새 인스턴스를 초기화합니다.
|
||||
/// Items 리스트와 _radioGroups 딕셔너리를 빈 상태로 생성합니다.
|
||||
/// </summary>
|
||||
public ToolbarModel()
|
||||
{
|
||||
Items = new System.Collections.Generic.List<IToolbarItem>();
|
||||
_radioGroups = new System.Collections.Generic.Dictionary<string, ToolbarRadioButtonGroup>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 지정된 툴바 항목(IToolbarItem)을 모델에 추가합니다.
|
||||
/// 추가된 항목은 Items 리스트의 끝에 추가됩니다.
|
||||
/// 만약 추가되는 항목이 ToolbarRadioButton이라면, 해당 버튼을 적절한 ToolbarRadioButtonGroup에 등록하거나 새로 생성하여 관리합니다.
|
||||
/// </summary>
|
||||
/// <param name="item">툴바에 추가할 IToolbarItem 객체입니다 (예: ToolbarStandardButton, ToolbarSeparator 등).</param>
|
||||
public void AddItem(IToolbarItem item)
|
||||
{
|
||||
Items.Add(item);
|
||||
|
||||
if (item is ToolbarRadioButton radioButton)
|
||||
{
|
||||
if (!_radioGroups.TryGetValue(radioButton.GroupName, out var group))
|
||||
{
|
||||
group = new ToolbarRadioButtonGroup();
|
||||
_radioGroups.Add(radioButton.GroupName, group);
|
||||
}
|
||||
group.RegisterButton(radioButton);
|
||||
radioButton.RadioGroup = group; // 버튼 모델에 그룹 참조 설정
|
||||
// 그룹 내 초기 선택 상태 결정 (만약 이 버튼이 initialState=true이고, 그룹에 아직 선택된 버튼이 없다면)
|
||||
// 또는 모든 버튼이 추가된 후 그룹별로 InitializeSelection()을 호출할 수도 있습니다.
|
||||
// 현재는 AddRadioButton 메서드에서 initialState에 따른 SetSelected 호출로 처리하고 있습니다.
|
||||
}
|
||||
// UI 갱신 로직은 ToolbarModel 자체에서는 직접 호출하지 않습니다.
|
||||
// View가 Model의 변경 사항을 구독하거나, Controller가 View의 업데이트를 트리거합니다.
|
||||
// 혹은, Model에 아이템이 추가/제거될 때 이벤트를 발생시켜 View가 감지하도록 할 수 있습니다.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 새로운 ToolbarStandardButton을 생성하여 툴바 모델에 추가합니다.
|
||||
/// </summary>
|
||||
/// <param name="text">버튼에 표시될 텍스트 또는 다국어 키입니다.</param>
|
||||
/// <param name="iconSpritePath">버튼 아이콘의 Resources 경로입니다 (선택 사항).</param>
|
||||
/// <param name="command">버튼 클릭 시 실행될 ICommand 객체입니다 (선택 사항).</param>
|
||||
/// <param name="tooltip">버튼 툴팁의 텍스트 또는 다국어 키입니다 (선택 사항).</param>
|
||||
/// <returns>생성되고 추가된 ToolbarStandardButton 객체입니다.</returns>
|
||||
public ToolbarStandardButton AddStandardButton(string text, string iconSpritePath = null, ICommand command = null, string tooltip = null)
|
||||
{
|
||||
var button = new ToolbarStandardButton { Text = text, IconSpritePath = iconSpritePath, ClickCommand = command, Tooltip = tooltip };
|
||||
AddItem(button);
|
||||
return button;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 새로운 ToolbarToggleButton을 생성하여 툴바 모델에 추가합니다.
|
||||
/// </summary>
|
||||
/// <param name="text">버튼 텍스트 또는 다국어 키입니다.</param>
|
||||
/// <param name="initialState">버튼의 초기 선택 상태입니다 (기본값: false, 해제됨).</param>
|
||||
/// <param name="onIconSpritePath">버튼이 선택(On) 상태일 때의 아이콘 경로입니다 (선택 사항).</param>
|
||||
/// <param name="offIconSpritePath">버튼이 해제(Off) 상태일 때의 아이콘 경로입니다 (선택 사항).</param>
|
||||
/// <param name="onToggle">버튼의 선택 상태가 변경될 때 호출될 콜백 함수입니다 (선택 사항).</param>
|
||||
/// <param name="command">버튼 클릭 시 실행될 ICommand 객체입니다 (선택 사항).</param>
|
||||
/// <param name="tooltip">버튼 툴팁의 텍스트 또는 다국어 키입니다 (선택 사항).</param>
|
||||
/// <returns>생성되고 추가된 ToolbarToggleButton 객체입니다.</returns>
|
||||
public ToolbarToggleButton AddToggleButton(string text, bool initialState = false, string onIconSpritePath = null, string offIconSpritePath = null, System.Action<bool> onToggle = null, ICommand command = null, string tooltip = null)
|
||||
{
|
||||
// ToolbarToggleButton의 IconSpritePath는 '선택된 상태'의 아이콘을 의미합니다.
|
||||
var button = new ToolbarToggleButton
|
||||
{
|
||||
Text = text,
|
||||
IsSelected = initialState,
|
||||
IconSpritePath = onIconSpritePath, // 선택 시 아이콘
|
||||
OffIconSpritePath = offIconSpritePath, // 해제 시 아이콘
|
||||
OnToggle = onToggle,
|
||||
ClickCommand = command,
|
||||
Tooltip = tooltip
|
||||
};
|
||||
AddItem(button);
|
||||
return button;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 새로운 ToolbarRadioButton을 생성하여 툴바 모델에 추가합니다.
|
||||
/// 동일한 groupName을 가진 라디오 버튼들은 하나의 그룹으로 동작합니다.
|
||||
/// </summary>
|
||||
/// <param name="groupName">이 라디오 버튼이 속할 그룹의 이름입니다.</param>
|
||||
/// <param name="text">버튼 텍스트 또는 다국어 키입니다.</param>
|
||||
/// <param name="initialState">버튼의 초기 선택 상태입니다. 같은 그룹 내에서 하나의 버튼만 true로 설정해야 합니다.</param>
|
||||
/// <param name="iconSpritePath">버튼이 선택(On) 상태일 때의 아이콘 경로입니다 (선택 사항).</param>
|
||||
/// <param name="offIconSpritePath">버튼이 해제(Off) 상태일 때의 아이콘 경로입니다 (선택 사항).</param>
|
||||
/// <param name="onToggle">버튼의 선택 상태가 변경될 때 호출될 콜백 함수입니다 (선택 사항).</param>
|
||||
/// <param name="command">버튼 클릭(선택) 시 실행될 ICommand 객체입니다 (선택 사항).</param>
|
||||
/// <param name="tooltip">버튼 툴팁의 텍스트 또는 다국어 키입니다 (선택 사항).</param>
|
||||
/// <returns>생성되고 추가된 ToolbarRadioButton 객체입니다.</returns>
|
||||
public ToolbarRadioButton AddRadioButton(string groupName, string text, bool initialState = false, string iconSpritePath = null, string offIconSpritePath = null, System.Action<bool> onToggle = null, ICommand command = null, string tooltip = null)
|
||||
{
|
||||
var button = new ToolbarRadioButton(groupName)
|
||||
{
|
||||
Text = text,
|
||||
IsSelected = initialState,// 초기 IsSelected 상태 설정
|
||||
IconSpritePath = iconSpritePath,// 선택 시 아이콘
|
||||
OffIconSpritePath = offIconSpritePath,// 해제 시 아이콘
|
||||
OnToggle = onToggle,
|
||||
ClickCommand = command,
|
||||
Tooltip = tooltip
|
||||
};
|
||||
|
||||
// AddItem 내에서 그룹 등록 및 버튼에 그룹 참조 설정이 이루어집니다.
|
||||
AddItem(button);
|
||||
|
||||
// initialState가 true인 경우, 해당 그룹 내에서 이 버튼을 명시적으로 선택된 상태로 만듭니다.
|
||||
// 이렇게 하면 여러 라디오 버튼 중 initialState가 true인 마지막 버튼이 최종 선택됩니다.
|
||||
// 또는, 그룹별로 모든 버튼이 추가된 후 한 번만 선택 로직을 실행하는 것도 방법입니다.
|
||||
if (initialState && _radioGroups.TryGetValue(groupName, out var group))
|
||||
{
|
||||
group.SetSelected(button);
|
||||
}
|
||||
return button;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 새로운 ToolbarExpandableButton(확장 가능한 버튼)을 생성하여 툴바 모델에 추가합니다.
|
||||
/// </summary>
|
||||
/// <param name="text">주 버튼에 표시될 텍스트 또는 다국어 키입니다.</param>
|
||||
/// <param name="iconSpritePath">주 버튼의 기본 아이콘 경로입니다 (선택 사항).</param>
|
||||
/// <param name="command">주 버튼 클릭 시 실행될 ICommand 객체입니다 (선택 사항, 하위 메뉴 토글과는 별개일 수 있음).</param>
|
||||
/// <param name="tooltip">주 버튼 툴팁의 텍스트 또는 다국어 키입니다 (선택 사항).</param>
|
||||
/// <returns>생성되고 추가된 ToolbarExpandableButton 객체입니다.</returns>
|
||||
public ToolbarExpandableButton AddExpandableButton(string text, string iconSpritePath = null, ICommand command = null, string tooltip = null)
|
||||
{
|
||||
var button = new ToolbarExpandableButton { Text = text, IconSpritePath = iconSpritePath, ClickCommand = command, Tooltip = tooltip };
|
||||
AddItem(button);
|
||||
return button;
|
||||
}
|
||||
|
||||
// <summary>
|
||||
/// 새로운 ToolbarSeparator(구분선)를 생성하여 툴바 모델에 추가합니다.
|
||||
/// </summary>
|
||||
public void AddSeparator()
|
||||
{
|
||||
AddItem(new ToolbarSeparator());
|
||||
}
|
||||
|
||||
// 만약 모든 라디오 버튼 그룹의 초기 선택 상태를 한 번에 설정하고 싶다면 다음과 같은 메서드를 추가할 수 있습니다.
|
||||
// /// <summary>
|
||||
// /// 모든 라디오 버튼 그룹에 대해 초기 선택 상태를 설정합니다.
|
||||
// /// 각 그룹에서 IsSelected가 true로 설정된 버튼이 없다면 첫 번째 버튼을 선택합니다.
|
||||
// /// </summary>
|
||||
// public void FinalizeRadioGroupSelections()
|
||||
// {
|
||||
// foreach (var group in _radioGroups.Values)
|
||||
// {
|
||||
// group.InitializeSelection();
|
||||
// }
|
||||
// }
|
||||
|
||||
// 실제 UI 렌더링 및 상호작용 로직은 이 클래스 또는 별도의 UI View 클래스에서 처리됩니다.
|
||||
// (예: Unity UI GameObject 생성, 이벤트 연결 등)
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/UVC/UI/ToolBar/Model/ToolbarModel.cs.meta
Normal file
2
Assets/Scripts/UVC/UI/ToolBar/Model/ToolbarModel.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 27e53214281a62642adbdbf761ffd630
|
||||
164
Assets/Scripts/UVC/UI/ToolBar/Model/ToolbarRadioButton.cs
Normal file
164
Assets/Scripts/UVC/UI/ToolBar/Model/ToolbarRadioButton.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
using System;
|
||||
|
||||
namespace UVC.UI.Toolbar.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// 라디오 버튼 그룹 내에서 사용되는 버튼입니다. 동일한 GroupName을 가진 라디오 버튼들 중에서
|
||||
/// 단 하나만 선택될 수 있도록 동작합니다. ToolbarToggleButton을 상속받습니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 각 ToolbarRadioButton은 GroupName을 가지며, 이 GroupName을 기준으로 ToolbarModel에서
|
||||
/// ToolbarRadioButtonGroup에 의해 관리됩니다. 사용자가 라디오 버튼을 클릭하면,
|
||||
/// 이 버튼은 자신이 속한 RadioGroup에 선택 상태 변경을 요청하고, RadioGroup은
|
||||
/// 그룹 내 다른 버튼들의 선택을 해제하고 이 버튼만 선택된 상태로 만듭니다.
|
||||
/// IsSelected 상태 변경 시 OnToggle, OnStateChanged, OnToggleStateChanged 이벤트가 발생합니다.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Toolbar 또는 ToolbarModel 등에서 ToolbarRadioButton 생성 및 사용 예시
|
||||
///
|
||||
/// string cameraViewGroupName = "CameraViewOptions";
|
||||
///
|
||||
/// // 1. 라디오 버튼1: 탑뷰
|
||||
/// ToolbarRadioButton topViewRadio = new ToolbarRadioButton(cameraViewGroupName)
|
||||
/// {
|
||||
/// Text = "view_top",
|
||||
/// IconSpritePath = "icons/toolbar/view_top_on",
|
||||
/// OffIconSpritePath = "icons/toolbar/view_top_off",
|
||||
/// Tooltip = "tooltip_top_view",
|
||||
/// IsSelected = true, // 초기 선택 상태
|
||||
/// OnToggle = (isSelected) =>
|
||||
/// {
|
||||
/// if (isSelected) UnityEngine.Debug.Log("탑뷰 선택됨 (OnToggle)");
|
||||
/// },
|
||||
/// ClickCommand = new ActionCommand(() =>
|
||||
/// {
|
||||
/// UnityEngine.Debug.Log("탑뷰 커맨드 실행");
|
||||
/// // 실제 탑뷰로 변경하는 로직
|
||||
/// })
|
||||
/// };
|
||||
///
|
||||
/// // 2. 라디오 버튼2: 프론트뷰
|
||||
/// ToolbarRadioButton frontViewRadio = new ToolbarRadioButton(cameraViewGroupName)
|
||||
/// {
|
||||
/// Text = "view_front",
|
||||
/// IconSpritePath = "icons/toolbar/view_front_on",
|
||||
/// OffIconSpritePath = "icons/toolbar/view_front_off",
|
||||
/// Tooltip = "tooltip_front_view",
|
||||
/// IsSelected = false, // 초기 선택 안됨
|
||||
/// OnToggle = (isSelected) =>
|
||||
/// {
|
||||
/// if (isSelected) UnityEngine.Debug.Log("프론트뷰 선택됨 (OnToggle)");
|
||||
/// },
|
||||
/// ClickCommand = new ActionCommand(() =>
|
||||
/// {
|
||||
/// UnityEngine.Debug.Log("프론트뷰 커맨드 실행");
|
||||
/// // 실제 프론트뷰로 변경하는 로직
|
||||
/// })
|
||||
/// };
|
||||
///
|
||||
/// // 3. 생성된 라디오 버튼들을 ToolbarModel에 추가
|
||||
/// // toolbarModel.AddItem(topViewRadio);
|
||||
/// // toolbarModel.AddItem(frontViewRadio);
|
||||
/// // 또는 ToolbarModel의 AddRadioButton 헬퍼 메서드 사용
|
||||
/// // toolbarModel.AddRadioButton(cameraViewGroupName, "view_top", true, "icons/toolbar/view_top_on", ...);
|
||||
/// // toolbarModel.AddRadioButton(cameraViewGroupName, "view_front", false, "icons/toolbar/view_front_on", ...);
|
||||
///
|
||||
/// // ToolbarModel은 AddItem 시 GroupName을 보고 내부적으로 ToolbarRadioButtonGroup을 생성/관리하며
|
||||
/// // 각 라디오 버튼을 해당 그룹에 등록합니다.
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class ToolbarRadioButton : ToolbarToggleButton
|
||||
{
|
||||
/// <summary>
|
||||
/// 이 라디오 버튼이 속한 그룹의 이름입니다.
|
||||
/// 동일한 GroupName을 가진 라디오 버튼들은 하나의 그룹으로 묶여 동작합니다.
|
||||
/// </summary>
|
||||
public string GroupName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 이 라디오 버튼을 관리하는 ToolbarRadioButtonGroup의 내부 참조입니다.
|
||||
/// ToolbarModel에 의해 설정되며, 버튼이 그룹에 등록될 때 할당됩니다.
|
||||
/// </summary>
|
||||
internal ToolbarRadioButtonGroup RadioGroup { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 지정된 그룹 이름을 사용하여 ToolbarRadioButton의 새 인스턴스를 초기화합니다.
|
||||
/// </summary>
|
||||
/// <param name="groupName">이 라디오 버튼이 속할 그룹의 이름입니다. null이거나 비어있을 수 없습니다.</param>
|
||||
/// <exception cref="ArgumentNullException">groupName이 null이거나 빈 문자열일 경우 발생합니다.</exception>
|
||||
public ToolbarRadioButton(string groupName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(groupName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(groupName), "라디오 버튼은 반드시 GroupName을 가져야 합니다.");
|
||||
}
|
||||
GroupName = groupName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 라디오 버튼 클릭 로직을 실행합니다.
|
||||
/// 버튼이 활성화되어 있다면, 자신이 속한 RadioGroup에 자신을 선택하도록 요청합니다.
|
||||
/// RadioGroup은 이 요청을 받아 그룹 내 다른 버튼들의 선택을 해제하고 이 버튼만 선택 상태로 만듭니다.
|
||||
/// 그 후, 이 버튼의 ClickCommand가 실행됩니다 (선택된 상태에서만).
|
||||
/// </summary>
|
||||
/// <param name="parameter">
|
||||
/// 이 파라미터는 ClickCommand에 전달될 수 있습니다.
|
||||
/// ToolbarView에서 UI 이벤트 연결 시, 라디오 버튼의 경우 보통 선택 상태(true)를 전달하거나,
|
||||
/// 버튼 모델 자체를 전달하여 Command가 필요한 정보를 추출하도록 할 수 있습니다.
|
||||
/// 기본적으로는 현재 IsSelected 상태가 ClickCommand에 전달되도록 고려할 수 있습니다.
|
||||
/// </param>
|
||||
public override void ExecuteClick(object parameter = null)
|
||||
{
|
||||
if (!IsEnabled) return;
|
||||
|
||||
// bool previousSelectionState = IsSelected; // 이전 선택 상태 (필요하다면)
|
||||
|
||||
if (RadioGroup != null)
|
||||
{
|
||||
// RadioGroup의 SetSelected 메서드를 호출하여 그룹 내 선택 상태를 관리합니다.
|
||||
// SetSelected 내부에서 이 버튼의 IsSelected가 true로 설정되고,
|
||||
// 다른 버튼들은 false로 설정됩니다.
|
||||
// IsSelected setter는 OnToggle, OnStateChanged, OnToggleStateChanged 이벤트를 발생시킵니다.
|
||||
RadioGroup.SetSelected(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// RadioGroup이 할당되지 않은 경우 (예: ToolbarModel에 추가되기 전 또는 독립적으로 사용 시도)
|
||||
// 이 경우 일반 토글 버튼처럼 동작하거나, 경고를 로깅할 수 있습니다.
|
||||
// 현재 구현은 그룹이 없으면 단독으로 선택되는 것을 방지하거나 특별 처리를 하도록 되어 있습니다.
|
||||
UnityEngine.Debug.LogWarning($"ToolbarRadioButton '{Text}' (그룹: {GroupName})에 RadioGroup이 할당되지 않았습니다. 단독으로 상태가 변경될 수 없습니다. ToolbarModel에 먼저 추가되어야 합니다.");
|
||||
// 만약 그룹 없이도 토글 가능하게 하려면 아래 주석 해제 및 로직 수정 필요
|
||||
// IsSelected = !IsSelected;
|
||||
}
|
||||
|
||||
// ClickCommand 실행:
|
||||
// 라디오 버튼의 ClickCommand는 일반적으로 해당 버튼이 "선택되었을 때"의 액션을 정의합니다.
|
||||
// RadioGroup.SetSelected에 의해 IsSelected 상태가 true로 변경된 후에 실행되어야 합니다.
|
||||
if (IsSelected && ClickCommand != null)
|
||||
{
|
||||
// 파라미터 처리: parameter가 null이 아니면 그것을 우선 사용하고,
|
||||
// 아니면 현재 IsSelected 상태(true) 또는 버튼 자체(this)를 전달할 수 있습니다.
|
||||
// ToolbarView의 SetupButtonVisualsAndInteractions에서 radioModel.ExecuteClick(true)로 호출하는 경우,
|
||||
// parameter는 true가 됩니다.
|
||||
object commandParameterToUse = parameter ?? this; // 예: 명시적 파라미터가 없으면 버튼 인스턴스 전달
|
||||
|
||||
ClickCommand.Execute(commandParameterToUse);
|
||||
}
|
||||
// 만약 선택 해제 시(다른 라디오 버튼이 선택되어 이 버튼이 해제될 때)에도 Command를 실행해야 한다면,
|
||||
// 위 if(IsSelected) 조건을 제거하거나, 별도의 Command(예: DeselectCommand)를 고려해야 합니다.
|
||||
// 하지만 일반적인 라디오 버튼의 사용 패턴은 선택 시의 액션에 중점을 둡니다.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 이 버튼 모델에 연결된 모든 이벤트 핸들러를 정리합니다.
|
||||
/// ToolbarRadioButton은 ToolbarToggleButton에서 상속받은 이벤트 외에 추가적인 이벤트가 없으므로,
|
||||
/// 부모 클래스의 ClearEventHandlers를 호출합니다.
|
||||
/// </summary>
|
||||
public override void ClearEventHandlers()
|
||||
{
|
||||
base.ClearEventHandlers(); // 부모 클래스(ToolbarToggleButton)의 이벤트 정리
|
||||
// ToolbarRadioButton에 특화된 이벤트가 있다면 여기서 정리합니다.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 357d7605383a64d4cb43bd295b261c1a
|
||||
151
Assets/Scripts/UVC/UI/ToolBar/Model/ToolbarRadioButtonGroup.cs
Normal file
151
Assets/Scripts/UVC/UI/ToolBar/Model/ToolbarRadioButtonGroup.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace UVC.UI.Toolbar.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// 여러 개의 ToolbarRadioButton 객체들을 하나의 그룹으로 관리합니다.
|
||||
/// 이 그룹 내에서는 오직 하나의 라디오 버튼만이 선택된(IsSelected = true) 상태를 가질 수 있도록 보장합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ToolbarModel은 ToolbarRadioButton이 추가될 때 GroupName을 기준으로 이 클래스의 인스턴스를 생성하거나
|
||||
/// 기존 인스턴스를 찾아 라디오 버튼을 등록(RegisterButton)합니다.
|
||||
/// 사용자가 라디오 버튼을 클릭하면, 해당 버튼은 이 그룹의 SetSelected 메서드를 호출하여
|
||||
/// 자신을 선택 상태로 만들고 그룹 내 다른 버튼들은 선택 해제 상태로 변경합니다.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // 이 클래스는 주로 ToolbarModel 내부에서 사용됩니다. 개발자가 직접 생성하기보다는
|
||||
/// // ToolbarModel에 ToolbarRadioButton을 추가함으로써 간접적으로 사용됩니다.
|
||||
///
|
||||
/// // ToolbarModel 내부에서의 사용 흐름 (간략화된 예시)
|
||||
/// // public class ToolbarModel
|
||||
/// // {
|
||||
/// // private Dictionary<string, ToolbarRadioButtonGroup> _radioGroups = new Dictionary<string, ToolbarRadioButtonGroup>();
|
||||
/// //
|
||||
/// // public void AddItem(IToolbarItem item)
|
||||
/// // {
|
||||
/// // if (item is ToolbarRadioButton radioButton)
|
||||
/// // {
|
||||
/// // if (!_radioGroups.TryGetValue(radioButton.GroupName, out var group))
|
||||
/// // {
|
||||
/// // group = new ToolbarRadioButtonGroup();
|
||||
/// // _radioGroups.Add(radioButton.GroupName, group);
|
||||
/// // }
|
||||
/// // group.RegisterButton(radioButton); // 라디오 버튼을 그룹에 등록
|
||||
/// // radioButton.RadioGroup = group; // 버튼에 그룹 참조 설정
|
||||
/// // }
|
||||
/// // // ...
|
||||
/// // }
|
||||
/// // }
|
||||
///
|
||||
/// // 그룹 내 버튼 선택 로직 (ToolbarRadioButton의 ExecuteClick 내부에서 호출됨)
|
||||
/// // ToolbarRadioButtonGroup cameraGroup = new ToolbarRadioButtonGroup();
|
||||
/// // ToolbarRadioButton radio1 = new ToolbarRadioButton("camera") { Text = "Cam1" };
|
||||
/// // ToolbarRadioButton radio2 = new ToolbarRadioButton("camera") { Text = "Cam2" };
|
||||
/// //
|
||||
/// // cameraGroup.RegisterButton(radio1);
|
||||
/// // cameraGroup.RegisterButton(radio2);
|
||||
/// // radio1.RadioGroup = cameraGroup;
|
||||
/// // radio2.RadioGroup = cameraGroup;
|
||||
/// //
|
||||
/// // // radio1을 선택 (radio1.IsSelected = true, radio2.IsSelected = false가 됨)
|
||||
/// // cameraGroup.SetSelected(radio1);
|
||||
/// // UnityEngine.Debug.Log($"Radio1 Selected: {radio1.IsSelected}, Radio2 Selected: {radio2.IsSelected}");
|
||||
/// //
|
||||
/// // // radio2를 선택 (radio1.IsSelected = false, radio2.IsSelected = true가 됨)
|
||||
/// // cameraGroup.SetSelected(radio2);
|
||||
/// // UnityEngine.Debug.Log($"Radio1 Selected: {radio1.IsSelected}, Radio2 Selected: {radio2.IsSelected}");
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class ToolbarRadioButtonGroup
|
||||
{
|
||||
private List<ToolbarRadioButton> _buttons = new List<ToolbarRadioButton>();
|
||||
|
||||
/// <summary>
|
||||
/// 현재 그룹 내에서 선택된 라디오 버튼입니다.
|
||||
/// 선택된 버튼이 없으면 null을 반환할 수 있습니다 (일반적으로 그룹은 항상 하나가 선택되도록 설계되지만, 초기 상태 등 예외 가능).
|
||||
/// </summary>
|
||||
public ToolbarRadioButton SelectedButton { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 지정된 라디오 버튼을 이 그룹에 등록합니다.
|
||||
/// 등록된 버튼은 그룹의 선택 관리 대상이 됩니다.
|
||||
/// 버튼의 RadioGroup 속성에도 이 그룹의 참조가 설정되어야 합니다 (보통 ToolbarModel에서 처리).
|
||||
/// </summary>
|
||||
/// <param name="button">그룹에 등록할 ToolbarRadioButton입니다.</param>
|
||||
public void RegisterButton(ToolbarRadioButton button)
|
||||
{
|
||||
if (!_buttons.Contains(button))
|
||||
{
|
||||
_buttons.Add(button);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 지정된 라디오 버튼을 그룹 내에서 선택된 상태로 설정합니다.
|
||||
/// 이전에 선택되었던 다른 버튼은 선택 해제 상태(IsSelected = false)로 변경됩니다.
|
||||
/// </summary>
|
||||
/// <param name="buttonToSelect">선택할 ToolbarRadioButton입니다. 이 버튼은 반드시 그룹에 미리 등록되어 있어야 합니다.</param>
|
||||
public void SetSelected(ToolbarRadioButton buttonToSelect)
|
||||
{
|
||||
if (buttonToSelect == null || !_buttons.Contains(buttonToSelect))
|
||||
{
|
||||
// 그룹에 등록되지 않은 버튼을 선택하려고 하면 무시하거나 경고를 로깅할 수 있습니다.
|
||||
UnityEngine.Debug.LogWarning($"SetSelected: 버튼 '{buttonToSelect?.Text}' (그룹: {buttonToSelect?.GroupName})은 현재 라디오 그룹에 등록되어 있지 않습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 이미 선택된 버튼을 다시 클릭한 경우, 상태를 변경하지 않고 유지합니다.
|
||||
// (라디오 버튼은 일반적으로 한 번 선택되면 사용자가 직접 해제할 수 없고, 다른 버튼을 선택해야 해제됨)
|
||||
if (SelectedButton == buttonToSelect && buttonToSelect.IsSelected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SelectedButton = buttonToSelect; // 새 버튼을 선택된 버튼으로 설정
|
||||
foreach (var buttonInGroup in _buttons)
|
||||
{
|
||||
// 현재 순회 중인 버튼이 선택하려는 버튼(buttonToSelect)과 동일한지 비교하여
|
||||
// IsSelected 상태를 설정합니다.
|
||||
// 이렇게 하면 buttonToSelect만 true가 되고 나머지는 false가 됩니다.
|
||||
// IsSelected 속성의 setter는 필요한 이벤트(OnToggle, OnStateChanged 등)를 발생시킵니다.
|
||||
buttonInGroup.IsSelected = (buttonInGroup == buttonToSelect);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 그룹에 등록된 모든 라디오 버튼의 목록을 가져옵니다.
|
||||
/// </summary>
|
||||
/// <returns>그룹 내 모든 ToolbarRadioButton의 읽기 전용 컬렉션입니다.</returns>
|
||||
public IReadOnlyList<ToolbarRadioButton> GetButtons()
|
||||
{
|
||||
return _buttons.AsReadOnly();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 그룹 내 버튼들의 초기 선택 상태를 설정합니다.
|
||||
/// 주로 ToolbarModel에서 라디오 버튼들을 추가한 후 호출될 수 있습니다.
|
||||
/// initialState가 true인 버튼이 있다면 해당 버튼을, 없다면 첫 번째 버튼을 선택합니다.
|
||||
/// </summary>
|
||||
internal void InitializeSelection()
|
||||
{
|
||||
if (!_buttons.Any()) return;
|
||||
|
||||
ToolbarRadioButton buttonToSelectInitially = _buttons.FirstOrDefault(b => b.IsSelected);
|
||||
|
||||
if (buttonToSelectInitially != null)
|
||||
{
|
||||
// initialState가 true로 설정된 버튼이 있다면, 해당 버튼으로 최종 선택 상태를 확정합니다.
|
||||
// (다른 initialState=true 버튼이 실수로 여러 개 있었을 경우를 대비하여 명확히 하나만 선택되도록 함)
|
||||
SetSelected(buttonToSelectInitially);
|
||||
}
|
||||
else
|
||||
{
|
||||
// IsSelected가 true인 버튼이 하나도 없다면, 그룹의 첫 번째 버튼을 기본으로 선택합니다.
|
||||
// (라디오 그룹은 일반적으로 항상 하나가 선택되어 있는 상태를 유지하려 함)
|
||||
SetSelected(_buttons[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6099bb327827504c8c4e36c74c7c507
|
||||
44
Assets/Scripts/UVC/UI/ToolBar/Model/ToolbarSeparator.cs
Normal file
44
Assets/Scripts/UVC/UI/ToolBar/Model/ToolbarSeparator.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
namespace UVC.UI.Toolbar.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// 툴바 내에서 항목들(예: 버튼 그룹)을 시각적으로 구분하는 구분선을 나타냅니다.
|
||||
/// IToolbarItem 인터페이스를 구현하여 툴바 시스템(ToolbarModel)에 추가될 수 있습니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ToolbarSeparator는 일반적으로 특별한 동작이나 속성을 가지지 않으며,
|
||||
/// View 레이어(예: ToolbarView)에서 이 항목을 만나면 시각적인 구분선 UI (예: 선, 공백 등)를 렌더링합니다.
|
||||
/// 프리팹 기반으로 UI가 생성될 경우, 구분선에 해당하는 프리팹이 사용됩니다.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Toolbar 또는 ToolbarModel 등에서 ToolbarSeparator 사용 예시
|
||||
///
|
||||
/// // ToolbarModel 인스턴스가 있다고 가정
|
||||
/// // ToolbarModel toolbarModel = new ToolbarModel();
|
||||
///
|
||||
/// // 1. 표준 버튼 추가
|
||||
/// // toolbarModel.AddStandardButton("버튼1", null, new ActionCommand(() => {}));
|
||||
///
|
||||
/// // 2. 구분선 추가
|
||||
/// ToolbarSeparator separator = new ToolbarSeparator();
|
||||
/// toolbarModel.AddItem(separator);
|
||||
/// // 또는 ToolbarModel에 AddSeparator() 헬퍼 메서드가 있다면 그것을 사용
|
||||
/// // toolbarModel.AddSeparator();
|
||||
///
|
||||
/// // 3. 또 다른 버튼 추가
|
||||
/// // toolbarModel.AddStandardButton("버튼2", null, new ActionCommand(() => {}));
|
||||
///
|
||||
/// // 위와 같이 추가하면, ToolbarView는 "버튼1"과 "버튼2" 사이에 구분선을 렌더링하게 됩니다.
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class ToolbarSeparator : IToolbarItem
|
||||
{
|
||||
// ToolbarSeparator는 일반적으로 데이터를 가지지 않으므로 내용이 비어있습니다.
|
||||
// 만약 구분선의 특정 스타일(예: 두께, 색상, 여백 등)을 모델 수준에서 제어해야 한다면,
|
||||
// 여기에 관련 속성을 추가할 수 있습니다. 하지만 보통 이러한 시각적 요소는 View에서 처리합니다.
|
||||
//
|
||||
// 예시: 구분선 유형을 나타내는 속성 (필요한 경우)
|
||||
// public enum SeparatorType { Line, Spacer }
|
||||
// public SeparatorType Type { get; set; } = SeparatorType.Line;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2df7acabcce51f34c9495286bc8ed81b
|
||||
96
Assets/Scripts/UVC/UI/ToolBar/Model/ToolbarStandardButton.cs
Normal file
96
Assets/Scripts/UVC/UI/ToolBar/Model/ToolbarStandardButton.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
namespace UVC.UI.Toolbar.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// 한 번 클릭으로 특정 동작을 수행하는 표준적인 버튼입니다.
|
||||
/// ToolbarButtonBase를 상속받아 텍스트, 아이콘, 툴팁, 활성화 상태 및 클릭 커맨드 기능을 가집니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 이 클래스는 ToolbarButtonBase의 기능을 그대로 사용하며, 특별히 추가된 로직은 없습니다.
|
||||
/// 복잡한 상태 관리 없이 단순 클릭 동작만을 필요로 하는 경우에 사용됩니다.
|
||||
/// View 레이어(예: ToolbarView)에서는 이 모델에 해당하는 UI 요소(예: UnityEngine.UI.Button)를 생성하고,
|
||||
/// 사용자가 UI 버튼을 클릭하면 이 모델의 ExecuteClick 메서드가 호출되도록 연결합니다.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Toolbar 또는 ToolbarModel 등에서 ToolbarStandardButton 생성 및 사용 예시
|
||||
///
|
||||
/// // 1. 버튼 클릭 시 실행될 커맨드 정의 (예: 간단한 로그 출력)
|
||||
/// ICommand saveCommand = new ActionCommand(() =>
|
||||
/// {
|
||||
/// UnityEngine.Debug.Log("저장 버튼 클릭됨. 저장 로직 실행...");
|
||||
/// // 여기에 실제 저장 로직 구현
|
||||
/// });
|
||||
///
|
||||
/// // 2. ToolbarStandardButton 인스턴스 생성 및 속성 설정
|
||||
/// ToolbarStandardButton saveButton = new ToolbarStandardButton
|
||||
/// {
|
||||
/// Text = "button_save", // 버튼 텍스트 (다국어 키 또는 실제 텍스트)
|
||||
/// IconSpritePath = "icons/toolbar/save_icon", // Resources 폴더 내 아이콘 경로
|
||||
/// Tooltip = "tooltip_save_button_description", // 툴팁 (다국어 키 또는 실제 텍스트)
|
||||
/// ClickCommand = saveCommand, // 위에서 정의한 커맨드 할당
|
||||
/// IsEnabled = true // 버튼 활성화 상태
|
||||
/// };
|
||||
///
|
||||
/// // 3. 생성된 버튼 모델을 ToolbarModel에 추가 (ToolbarModel의 AddItem 또는 AddStandardButton 메서드 사용)
|
||||
/// // toolbarModel.AddItem(saveButton);
|
||||
/// // 또는
|
||||
/// // toolbarModel.AddStandardButton("button_save", "icons/toolbar/save_icon", saveCommand, "tooltip_save_button_description");
|
||||
///
|
||||
/// // 4. 버튼 상태 변경 예시
|
||||
/// // saveButton.IsEnabled = false; // 버튼 비활성화 (UI에 반영됨)
|
||||
/// // saveButton.Text = "button_saved"; // 버튼 텍스트 변경 (UI에 반영됨)
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class ToolbarStandardButton : ToolbarButtonBase
|
||||
{
|
||||
// ToolbarStandardButton은 ToolbarButtonBase의 모든 기능을 상속받습니다.
|
||||
// 특별히 ToolbarStandardButton만을 위한 추가적인 속성이나 메서드가 필요하다면 여기에 정의할 수 있습니다.
|
||||
|
||||
// 예시: 생성자를 통해 주요 속성을 설정하도록 할 수도 있습니다.
|
||||
// /// <summary>
|
||||
// /// 지정된 속성으로 ToolbarStandardButton의 새 인스턴스를 초기화합니다.
|
||||
// /// </summary>
|
||||
// /// <param name="text">버튼 텍스트 또는 다국어 키입니다.</param>
|
||||
// /// <param name="iconSpritePath">아이콘의 Resources 경로입니다.</param>
|
||||
// /// <param name="command">클릭 시 실행될 커맨드입니다.</param>
|
||||
// /// <param name="tooltip">툴팁 텍스트 또는 다국어 키입니다.</param>
|
||||
// public ToolbarStandardButton(string text, string iconSpritePath = null, ICommand command = null, string tooltip = null)
|
||||
// {
|
||||
// this.Text = text;
|
||||
// this.IconSpritePath = iconSpritePath;
|
||||
// this.ClickCommand = command;
|
||||
// this.TooltipKey = tooltip;
|
||||
// }
|
||||
|
||||
// ToolbarButtonBase의 ExecuteClick 메서드를 그대로 사용합니다.
|
||||
// 만약 StandardButton만의 특별한 클릭 전/후 처리가 필요하다면 ExecuteClick 메서드를 override 할 수 있습니다.
|
||||
// /// <summary>
|
||||
// /// 버튼 클릭 로직을 실행합니다.
|
||||
// /// </summary>
|
||||
// /// <param name="parameter">커맨드에 전달할 파라미터입니다.</param>
|
||||
// public override void ExecuteClick(object parameter = null)
|
||||
// {
|
||||
// if (!IsEnabled) return;
|
||||
//
|
||||
// // UnityEngine.Debug.Log($"ToolbarStandardButton '{Text}' ExecuteClick 호출됨.");
|
||||
//
|
||||
// // 부모 클래스의 ExecuteClick (ClickCommand 실행 등)
|
||||
// base.ExecuteClick(parameter);
|
||||
//
|
||||
// // 여기에 ToolbarStandardButton만의 추가적인 로직이 있다면 작성
|
||||
// // 예를 들어, 클릭 후 특정 사운드 재생 등
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// 이 버튼 모델에 연결된 모든 이벤트 핸들러를 정리합니다.
|
||||
/// ToolbarStandardButton은 ToolbarButtonBase에서 상속받은 이벤트 외에 추가적인 이벤트가 없으므로,
|
||||
/// 기본 구현을 호출합니다.
|
||||
/// </summary>
|
||||
public override void ClearEventHandlers()
|
||||
{
|
||||
base.ClearEventHandlers();
|
||||
// ToolbarStandardButton에 특화된 이벤트가 있다면 여기서 정리합니다.
|
||||
// 예: CustomEvent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce9fef59341116a46a731f2afe6a96f9
|
||||
169
Assets/Scripts/UVC/UI/ToolBar/Model/ToolbarToggleButton.cs
Normal file
169
Assets/Scripts/UVC/UI/ToolBar/Model/ToolbarToggleButton.cs
Normal file
@@ -0,0 +1,169 @@
|
||||
using System;
|
||||
|
||||
namespace UVC.UI.Toolbar.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// 클릭할 때마다 선택(On) 또는 해제(Off) 상태가 전환되는 토글 버튼입니다.
|
||||
/// ToolbarButtonBase를 상속받으며, 추가적으로 선택 상태(IsSelected)와
|
||||
/// 해제 상태일 때의 아이콘 경로(OffIconSpritePath), 상태 변경 시 호출될 콜백(OnToggle)을 관리합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// View 레이어(예: ToolbarView)에서는 이 모델에 해당하는 UI 요소(예: UnityEngine.UI.Toggle)를 생성하고,
|
||||
/// 사용자가 UI 토글을 조작하면 이 모델의 ExecuteClick 메서드를 호출하거나 IsSelected 속성을 직접 변경하여
|
||||
/// 모델과 UI의 상태를 동기화합니다. IsSelected 속성이 변경되면 OnStateChanged 및 OnToggleStateChanged 이벤트가 발생하여
|
||||
/// View가 아이콘(선택/해제 상태에 따라 IconSpritePath 또는 OffIconSpritePath 사용) 및 기타 시각적 요소를 업데이트합니다.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Toolbar 또는 ToolbarModel 등에서 ToolbarToggleButton 생성 및 사용 예시
|
||||
///
|
||||
/// // 1. 토글 상태 변경 시 실행될 액션 정의 (OnToggle 콜백용)
|
||||
/// Action<bool> handleMuteToggle = (isMuted) =>
|
||||
/// {
|
||||
/// UnityEngine.Debug.Log(isMuted ? "음소거됨" : "음소거 해제됨");
|
||||
/// // 여기에 실제 음소거/해제 로직 구현
|
||||
/// };
|
||||
///
|
||||
/// // 2. 토글 버튼 클릭 시 실행될 커맨드 정의 (선택 사항, 주로 상태 변경 후 추가 작업)
|
||||
/// // 이 커맨드는 IsSelected 상태가 변경된 *후에* ExecuteClick 내부에서 호출됩니다.
|
||||
/// // 커맨드 파라미터로 현재 IsSelected 상태를 받고 싶다면 ActionCommand<bool> 사용 가능.
|
||||
/// ICommand muteCommand = new ActionCommand<bool>((isSelected) =>
|
||||
/// {
|
||||
/// UnityEngine.Debug.Log($"음소거 버튼 커맨드 실행됨. 현재 선택 상태: {isSelected}");
|
||||
/// });
|
||||
///
|
||||
/// // 3. ToolbarToggleButton 인스턴스 생성 및 속성 설정
|
||||
/// ToolbarToggleButton muteToggleButton = new ToolbarToggleButton
|
||||
/// {
|
||||
/// Text = "button_mute", // 버튼 텍스트 (다국어 키)
|
||||
/// IconSpritePath = "icons/toolbar/sound_on_icon", // 선택(On) 상태 아이콘
|
||||
/// OffIconSpritePath = "icons/toolbar/sound_off_icon", // 해제(Off) 상태 아이콘
|
||||
/// Tooltip = "tooltip_mute_button", // 툴팁 (다국어 키)
|
||||
/// IsSelected = false, // 초기 상태는 해제(Off)
|
||||
/// OnToggle = handleMuteToggle, // 상태 변경 시 콜백 할당
|
||||
/// ClickCommand = muteCommand, // 클릭 커맨드 할당
|
||||
/// IsEnabled = true // 버튼 활성화 상태
|
||||
/// };
|
||||
///
|
||||
/// // 4. 생성된 버튼 모델을 ToolbarModel에 추가
|
||||
/// // toolbarModel.AddItem(muteToggleButton);
|
||||
/// // 또는
|
||||
/// // toolbarModel.AddToggleButton("button_mute", false, "icons/toolbar/sound_on_icon", "icons/toolbar/sound_off_icon", handleMuteToggle, muteCommand, "tooltip_mute_button");
|
||||
///
|
||||
/// // 5. 버튼 상태 프로그래밍 방식으로 변경 예시
|
||||
/// // muteToggleButton.IsSelected = true; // 버튼을 선택 상태로 변경 (OnToggle 콜백 및 이벤트 발생)
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class ToolbarToggleButton : ToolbarButtonBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 버튼의 선택 상태(IsSelected)가 변경될 때 발생하는 이벤트입니다.
|
||||
/// 변경된 IsSelected 값을 파라미터로 전달합니다.
|
||||
/// View에서 이 이벤트를 구독하여 UI의 시각적 상태(예: Toggle 컴포넌트의 isOn 상태)를 업데이트할 수 있습니다.
|
||||
/// OnStateChanged 이벤트와 별개로, IsSelected 상태 변경에 좀 더 특화된 알림입니다.
|
||||
/// </summary>
|
||||
public event Action<bool> OnToggleStateChanged; // IsSelected 변경 시 IsSelected 값을 전달하는 이벤트
|
||||
|
||||
protected string _offIconSpritePath;
|
||||
/// <summary>
|
||||
/// 버튼이 선택되지 않은(해제된, Off) 상태일 때 표시될 아이콘의 Resources 폴더 내 경로입니다.
|
||||
/// 이 값이 변경되면 OnStateChanged 이벤트가 발생하여 View가 아이콘을 업데이트할 수 있습니다.
|
||||
/// 선택(On) 상태일 때는 ToolbarButtonBase의 IconSpritePath가 사용됩니다.
|
||||
/// </summary>
|
||||
public string OffIconSpritePath
|
||||
{
|
||||
get => _offIconSpritePath;
|
||||
set
|
||||
{
|
||||
if (_offIconSpritePath != value)
|
||||
{
|
||||
_offIconSpritePath = value;
|
||||
NotifyStateChanged();// 아이콘 경로 변경 시 전체 상태 변경으로 간주
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isSelected;
|
||||
/// <summary>
|
||||
/// 버튼의 현재 선택 상태를 나타냅니다. true이면 선택(On)된 상태, false이면 해제(Off)된 상태입니다.
|
||||
/// 이 속성 값이 변경되면 OnStateChanged 이벤트와 OnToggleStateChanged 이벤트가 발생하며,
|
||||
/// OnToggle 콜백이 호출됩니다.
|
||||
/// </summary>
|
||||
public bool IsSelected
|
||||
{
|
||||
get => _isSelected;
|
||||
set
|
||||
{
|
||||
if (_isSelected != value)
|
||||
{
|
||||
_isSelected = value;
|
||||
OnToggle?.Invoke(_isSelected); // OnToggle 콜백 호출
|
||||
OnToggleStateChanged?.Invoke(_isSelected); // IsSelected 값과 함께 이벤트 발생
|
||||
NotifyStateChanged(); // 일반 상태 변경 이벤트도 발생
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 버튼의 선택 상태(IsSelected)가 변경될 때 호출될 사용자 정의 콜백 액션입니다.
|
||||
/// IsSelected 속성의 setter 내부에서 호출됩니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ClickCommand와는 별개로, 순수하게 토글 상태 변경에 대한 응답 로직을 정의할 때 유용합니다.
|
||||
/// 예를 들어, UI 상태 변경에 따른 즉각적인 모델 데이터 변경 등에 사용할 수 있습니다.
|
||||
/// </remarks>
|
||||
public Action<bool> OnToggle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 버튼 클릭 로직을 실행합니다.
|
||||
/// 토글 버튼의 경우, 클릭 시 IsSelected 상태를 반전시키고,
|
||||
/// ClickCommand가 있다면 현재 IsSelected 상태를 파라미터로 전달하여 실행합니다.
|
||||
/// </summary>
|
||||
/// <param name="parameter">
|
||||
/// 이 파라미터는 일반적으로 사용되지 않거나 무시될 수 있습니다.
|
||||
/// ClickCommand에는 현재 IsSelected 상태가 전달됩니다.
|
||||
/// 만약 외부에서 특정 값을 전달해야 한다면, ClickCommand가 ActionCommand<object> 등으로 정의되어야 합니다.
|
||||
/// </param>
|
||||
public override void ExecuteClick(object parameter = null)
|
||||
{
|
||||
if (!IsEnabled) return;
|
||||
|
||||
if (parameter is bool newSelectedStateFromUI)
|
||||
{
|
||||
// UI로부터 직접 상태가 전달된 경우 (View의 onValueChanged 리스너)
|
||||
// IsSelected 프로퍼티 setter가 OnToggle 및 NotifyStateChanged를 호출
|
||||
IsSelected = newSelectedStateFromUI;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 일반적인 ExecuteClick (파라미터 없거나 bool이 아님) - 기존 토글 로직
|
||||
IsSelected = !IsSelected;
|
||||
}
|
||||
|
||||
// ClickCommand 실행. IsSelected 상태가 변경된 *후에* 실행됩니다.
|
||||
// ClickCommand에 현재 선택 상태(IsSelected)를 파라미터로 전달합니다.
|
||||
// 만약 ClickCommand가 파라미터를 받지 않는 ActionCommand라면, 파라미터 없이 실행됩니다.
|
||||
// ClickCommand가 ActionCommand<bool>이라면 IsSelected 값이 전달됩니다.
|
||||
// ClickCommand가 ActionCommand<object>라면 IsSelected 값이 object로 박싱되어 전달됩니다.
|
||||
if (ClickCommand != null)
|
||||
{
|
||||
// ClickCommand의 구체적인 타입(제네릭 여부 등)에 따라 파라미터 전달 방식이 달라질 수 있습니다.
|
||||
// 가장 일반적인 경우는 ClickCommand가 현재 상태를 알 수 있도록 하는 것입니다.
|
||||
// ToolbarView에서 연결 시 new ActionCommand<bool>((isSelectedParam) => { /* 로직 */ }) 형태로 커맨드를 생성했다면,
|
||||
// isSelectedParam으로 현재 IsSelected 값이 전달됩니다.
|
||||
ClickCommand.Execute(IsSelected);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 이 버튼 모델에 연결된 모든 이벤트 핸들러를 정리합니다.
|
||||
/// ToolbarButtonBase의 이벤트 외에 ToolbarToggleButton에 특화된 이벤트(OnToggleStateChanged, OnToggle)를 추가로 정리합니다.
|
||||
/// </summary>
|
||||
public override void ClearEventHandlers()
|
||||
{
|
||||
base.ClearEventHandlers(); // 부모 클래스의 이벤트 정리 (OnStateChanged)
|
||||
OnToggleStateChanged = null;
|
||||
OnToggle = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9530db2a5b423bf4eb8c5d8e4f9dd5ed
|
||||
Reference in New Issue
Block a user