toolbar 개발 완료. 주석 다는 중
This commit is contained in:
@@ -1,7 +1,40 @@
|
||||
namespace UVC.UI.Toolbar
|
||||
{
|
||||
/// <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 { }
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,74 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UVC.UI.Commands;
|
||||
|
||||
namespace UVC.UI.Toolbar
|
||||
{
|
||||
/// <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 속성 사용
|
||||
/// TooltipKey = tooltip; // 부모 클래스의 TooltipKey 속성 사용
|
||||
/// 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
|
||||
{
|
||||
public event Action OnStateChanged; // 상태 변경 알림 이벤트
|
||||
/// <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;
|
||||
@@ -27,6 +83,16 @@ namespace UVC.UI.Toolbar
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -41,6 +107,16 @@ namespace UVC.UI.Toolbar
|
||||
}
|
||||
|
||||
protected bool _isEnabled = true;
|
||||
/// <summary>
|
||||
/// 버튼의 활성화 상태를 나타냅니다. true이면 사용자와 상호작용할 수 있고, false이면 비활성화되어 상호작용할 수 없습니다.
|
||||
/// 값이 변경되면 OnStateChanged 이벤트가 발생합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// button.IsEnabled = false; // 버튼 비활성화
|
||||
/// if (button.IsEnabled) { /* 버튼 사용 가능 로직 */ }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => _isEnabled;
|
||||
@@ -54,7 +130,18 @@ namespace UVC.UI.Toolbar
|
||||
}
|
||||
}
|
||||
|
||||
protected string _tooltipKey; // 툴팁 다국어 키
|
||||
protected string _tooltipKey;
|
||||
/// <summary>
|
||||
/// 버튼에 마우스를 올렸을 때 표시될 툴팁의 텍스트 또는 다국어 키입니다.
|
||||
/// TooltipKey 변경 시에는 기본적으로 OnStateChanged 이벤트가 발생하지 않지만,
|
||||
/// 필요에 따라 View에서 이 값을 직접 참조하여 툴팁을 업데이트할 수 있습니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// button.TooltipKey = "tooltip_save_button"; // 다국어 키 사용
|
||||
/// button.TooltipKey = "클릭하여 문서를 저장합니다."; // 직접 텍스트 사용
|
||||
/// </code>
|
||||
/// </example>
|
||||
public string TooltipKey
|
||||
{
|
||||
get => _tooltipKey;
|
||||
@@ -66,17 +153,42 @@ namespace UVC.UI.Toolbar
|
||||
// TooltipKey 변경 시 OnStateChanged를 호출할 필요는 일반적으로 없으나,
|
||||
// 만약 UI가 TooltipKey 자체를 표시하는 등의 로직이 있다면 필요할 수 있습니다.
|
||||
// 여기서는 툴팁 내용이 동적으로 변경되는 경우가 적다고 가정하고 생략합니다.
|
||||
// 필요하다면 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 이벤트에 의해 호출됩니다.
|
||||
/// 이 메서드는 일반적으로 UI 시스템(예: Unity UI의 Button.onClick 이벤트)에 의해 호출되도록 설계됩니다.
|
||||
/// 버튼이 활성화(IsEnabled == true)되어 있고 ClickCommand가 할당되어 있다면, 해당 커맨드를 실행합니다.
|
||||
/// 파생 클래스에서 이 메서드를 재정의하여 특정 버튼 타입에 맞는 추가적인 클릭 동작을 구현할 수 있습니다.
|
||||
/// </summary>
|
||||
/// <param name="parameter">커맨드에 전달할 파라미터입니다.</param>
|
||||
/// <param name="parameter">ClickCommand에 전달할 선택적 파라미터입니다.</param>
|
||||
public virtual void ExecuteClick(object parameter = null)
|
||||
{
|
||||
if (IsEnabled && ClickCommand != null)
|
||||
@@ -85,10 +197,24 @@ namespace UVC.UI.Toolbar
|
||||
}
|
||||
}
|
||||
|
||||
// OnStateChanged 이벤트를 외부에서 강제로 발생시켜야 할 때 사용 (예: 복합적인 상태 변경 후)
|
||||
/// <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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +1,145 @@
|
||||
|
||||
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine; // GameObject 사용을 위해 필요
|
||||
// using UnityEngine.UI; // Image 사용을 위해 필요할 수 있으나, 모델 클래스에서는 직접적인 UI 참조를 최소화하는 것이 좋음
|
||||
|
||||
namespace UVC.UI.Toolbar
|
||||
{
|
||||
/// <summary>
|
||||
/// 클릭 시 하위 버튼 그룹을 확장하여 보여주는 버튼입니다.
|
||||
/// 하위 버튼 선택 시, 주 버튼의 내용이 업데이트될 수 있습니다.
|
||||
/// 클릭 시 하위 버튼 목록(서브 메뉴)을 표시하거나 숨길 수 있는 확장형 버튼입니다.
|
||||
/// ToolbarButtonBase를 상속받아 기본적인 버튼 속성(텍스트, 아이콘, 커맨드 등)을 가집니다.
|
||||
/// 주 버튼을 클릭하면 연결된 서브 메뉴가 표시되며, 서브 메뉴의 버튼을 선택하면
|
||||
/// 주 버튼의 텍스트나 아이콘이 선택된 하위 버튼의 것으로 변경될 수 있습니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 이 모델 클래스는 데이터와 상태를 관리합니다. 실제 서브 메뉴의 표시/숨김 및 UI 렌더링은
|
||||
/// ToolbarView와 같은 View 클래스에서 처리됩니다.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // ToolbarController 등에서 확장 버튼 생성 및 설정 예시
|
||||
/// 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",
|
||||
/// TooltipKey = "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",
|
||||
/// TooltipKey = "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
|
||||
{
|
||||
public enum ExpansionDirection { Horizontal, Vertical }
|
||||
|
||||
/// <summary>
|
||||
/// 이 확장 버튼에 속한 하위 버튼들의 목록입니다.
|
||||
/// 이 목록에 있는 버튼들은 주 버튼 클릭 시 View에 의해 표시될 수 있습니다.
|
||||
/// 각 하위 버튼은 ToolbarButtonBase 또는 그 파생 클래스의 인스턴스여야 합니다.
|
||||
/// </summary>
|
||||
public List<ToolbarButtonBase> SubButtons { get; private set; }
|
||||
public ExpansionDirection Direction { get; set; } = ExpansionDirection.Vertical;
|
||||
|
||||
/// <summary>
|
||||
/// 하위 버튼 중 하나가 선택되었을 때 호출되는 액션(콜백)입니다.
|
||||
/// 선택된 하위 버튼 객체가 파라미터로 전달됩니다.
|
||||
/// 주 버튼의 모양이 변경된 후, 추가적인 로직을 수행하고자 할 때 사용합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 이 콜백은 SelectSubButton 메서드 내에서, 주 버튼의 Text/IconSpritePath가 업데이트된 후 호출됩니다.
|
||||
/// </remarks>
|
||||
public Action<ToolbarButtonBase> OnSubButtonSelected { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ToolbarExpandableButton의 새 인스턴스를 초기화합니다.
|
||||
/// SubButtons 리스트를 빈 리스트로 생성합니다.
|
||||
/// </summary>
|
||||
public ToolbarExpandableButton()
|
||||
{
|
||||
SubButtons = new List<ToolbarButtonBase>();
|
||||
}
|
||||
|
||||
// 주 버튼 클릭 시 하위 메뉴를 토글하는 동작은 View에서 처리될 수 있고,
|
||||
// ClickCommand는 주 버튼 자체의 액션(있다면)을 정의합니다.
|
||||
// 또는 ClickCommand가 하위 메뉴 토글 로직을 포함할 수도 있습니다.
|
||||
// 여기서는 ClickCommand는 주 버튼의 고유 액션, 하위 메뉴 토글은 View의 역할로 가정합니다.
|
||||
/// <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는 주 버튼 자체의 액션 (예: 상태 변경, 특정 기능 수행)
|
||||
ClickCommand?.Execute(parameter);
|
||||
// 주 버튼 자체에 할당된 ClickCommand 실행 (예: 특정 모드 진입, 상태 변경 등)
|
||||
base.ExecuteClick(parameter);
|
||||
|
||||
// 하위 메뉴를 여는 동작은 보통 View에서 이 버튼 클릭 시 별도로 처리합니다.
|
||||
// OnClick (이제 ClickCommand)이 그 역할을 할 수도 있지만,
|
||||
// View에서 직접 ToggleSubMenu를 호출하는 것이 더 명확할 수 있습니다.
|
||||
// ToolbarView의 SetupButtonVisualsAndInteractions에서 expandableModel.ExecuteClick() 후
|
||||
// ToggleSubMenu()를 호출하는 현재 구조를 유지할 수 있습니다.
|
||||
// 하위 메뉴를 여는 시각적 동작은 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)
|
||||
{
|
||||
bool changed = false;
|
||||
// 주 버튼의 텍스트를 선택된 하위 버튼의 텍스트로 변경
|
||||
// Text 속성의 setter는 내부적으로 OnStateChanged를 호출하여 View 업데이트를 트리거합니다.
|
||||
if (this.Text != selectedSubButton.Text)
|
||||
{
|
||||
this.Text = selectedSubButton.Text; // Setter가 OnStateChanged 호출 (단, Text가 실제로 변경되어야 함)
|
||||
changed = true;
|
||||
this.Text = selectedSubButton.Text;
|
||||
}
|
||||
|
||||
// 주 버튼의 아이콘 경로를 선택된 하위 버튼의 아이콘 경로로 변경
|
||||
// IconSpritePath 속성의 setter는 내부적으로 OnStateChanged를 호출합니다.
|
||||
if (this.IconSpritePath != selectedSubButton.IconSpritePath)
|
||||
{
|
||||
this.IconSpritePath = selectedSubButton.IconSpritePath; // Setter가 OnStateChanged 호출
|
||||
changed = true;
|
||||
this.IconSpritePath = selectedSubButton.IconSpritePath;
|
||||
}
|
||||
|
||||
// 하위 버튼 선택 콜백 호출
|
||||
OnSubButtonSelected?.Invoke(selectedSubButton);
|
||||
// selectedSubButton.ExecuteClick(); // 하위 버튼의 클릭 로직 실행은 선택 사항
|
||||
|
||||
if (changed) // Text나 Icon이 실제로 변경된 경우에만 명시적으로 호출하거나, 각 setter에 맡김
|
||||
{
|
||||
// NotifyStateChanged(); // Text, Icon setter가 이미 호출하므로 중복될 수 있음.
|
||||
// 만약 Text, Icon 외 다른 상태도 변경된다면 필요.
|
||||
}
|
||||
// 선택된 하위 버튼 자체의 ClickCommand 실행은 여기서 하지 않습니다.
|
||||
// View에서 하위 버튼 UI 클릭 시 해당 하위 버튼의 ExecuteClick()이 직접 호출되는 것이 일반적입니다.
|
||||
// 만약 여기서 실행해야 한다면: selectedSubButton.ExecuteClick();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +1,153 @@
|
||||
namespace UVC.UI.Toolbar
|
||||
using System;
|
||||
|
||||
namespace UVC.UI.Toolbar
|
||||
{
|
||||
/// <summary>
|
||||
/// 라디오 버튼 그룹 내에서 사용되는 버튼입니다.
|
||||
/// 그룹 내 하나의 버튼만 선택될 수 있습니다.
|
||||
/// 라디오 버튼 그룹 내에서 사용되는 버튼입니다. 동일한 GroupName을 가진 라디오 버튼들 중에서
|
||||
/// 단 하나만 선택될 수 있도록 동작합니다. ToolbarToggleButton을 상속받습니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 각 ToolbarRadioButton은 GroupName을 가지며, 이 GroupName을 기준으로 ToolbarModel에서
|
||||
/// ToolbarRadioButtonGroup에 의해 관리됩니다. 사용자가 라디오 버튼을 클릭하면,
|
||||
/// 이 버튼은 자신이 속한 RadioGroup에 선택 상태 변경을 요청하고, RadioGroup은
|
||||
/// 그룹 내 다른 버튼들의 선택을 해제하고 이 버튼만 선택된 상태로 만듭니다.
|
||||
/// IsSelected 상태 변경 시 OnToggle, OnStateChanged, OnToggleStateChanged 이벤트가 발생합니다.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // ToolbarController 또는 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",
|
||||
/// TooltipKey = "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",
|
||||
/// TooltipKey = "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 wasSelected = IsSelected;
|
||||
// bool previousSelectionState = IsSelected; // 이전 선택 상태 (필요하다면)
|
||||
|
||||
if (RadioGroup != null)
|
||||
{
|
||||
// SetSelected는 IsSelected 상태를 변경하고,
|
||||
// IsSelected setter는 OnToggleStateChanged 및 NotifyStateChanged를 호출합니다.
|
||||
// OnToggle 콜백도 IsSelected setter 내부 또는 SetSelected 메서드 내에서 호출될 수 있습니다.
|
||||
// RadioGroup의 SetSelected 메서드를 호출하여 그룹 내 선택 상태를 관리합니다.
|
||||
// SetSelected 내부에서 이 버튼의 IsSelected가 true로 설정되고,
|
||||
// 다른 버튼들은 false로 설정됩니다.
|
||||
// IsSelected setter는 OnToggle, OnStateChanged, OnToggleStateChanged 이벤트를 발생시킵니다.
|
||||
RadioGroup.SetSelected(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 그룹이 없는 라디오 버튼은 의미가 모호하므로, 단독으로 선택되는 것을 방지하거나 특별 처리.
|
||||
// 여기서는 그룹이 없으면 아무것도 하지 않거나, 경고를 로깅할 수 있습니다.
|
||||
UnityEngine.Debug.LogWarning($"ToolbarRadioButton '{Text}' (그룹: {GroupName})에 RadioGroup이 할당되지 않았습니다.");
|
||||
// 또는 강제로 선택 상태로 만들고 콜백 호출 (기존 주석 참고)
|
||||
// if (!IsSelected) // 현재 선택되지 않았다면 선택
|
||||
// {
|
||||
// IsSelected = true;
|
||||
// OnToggle?.Invoke(IsSelected);
|
||||
// }
|
||||
// RadioGroup이 할당되지 않은 경우 (예: ToolbarModel에 추가되기 전 또는 독립적으로 사용 시도)
|
||||
// 이 경우 일반 토글 버튼처럼 동작하거나, 경고를 로깅할 수 있습니다.
|
||||
// 현재 구현은 그룹이 없으면 단독으로 선택되는 것을 방지하거나 특별 처리를 하도록 되어 있습니다.
|
||||
UnityEngine.Debug.LogWarning($"ToolbarRadioButton '{Text}' (그룹: {GroupName})에 RadioGroup이 할당되지 않았습니다. 단독으로 상태가 변경될 수 없습니다. ToolbarModel에 먼저 추가되어야 합니다.");
|
||||
// 만약 그룹 없이도 토글 가능하게 하려면 아래 주석 해제 및 로직 수정 필요
|
||||
// IsSelected = !IsSelected;
|
||||
}
|
||||
|
||||
// ClickCommand 실행
|
||||
// RadioGroup.SetSelected에 의해 IsSelected 상태가 변경된 후 실행됩니다.
|
||||
// parameter가 null이 아니면 그것을 우선 사용하고, 아니면 현재 IsSelected 상태를 전달할 수 있습니다.
|
||||
// 또는 버튼 자체를 파라미터로 전달하여 Command가 필요한 정보를 추출하도록 할 수도 있습니다.
|
||||
object commandParameterToUse = parameter ?? this; // 예: 파라미터가 없으면 버튼 인스턴스 전달
|
||||
|
||||
// 라디오 버튼의 경우, ClickCommand는 주로 해당 버튼이 "선택되었을 때"의 액션을 정의합니다.
|
||||
// 따라서 IsSelected가 true일 때만 Command를 실행하는 것이 일반적일 수 있습니다.
|
||||
if (IsSelected) // 현재 버튼이 선택된 상태일 때만 Command 실행
|
||||
// ClickCommand 실행:
|
||||
// 라디오 버튼의 ClickCommand는 일반적으로 해당 버튼이 "선택되었을 때"의 액션을 정의합니다.
|
||||
// RadioGroup.SetSelected에 의해 IsSelected 상태가 true로 변경된 후에 실행되어야 합니다.
|
||||
if (IsSelected && ClickCommand != null)
|
||||
{
|
||||
ClickCommand?.Execute(commandParameterToUse);
|
||||
// 파라미터 처리: parameter가 null이 아니면 그것을 우선 사용하고,
|
||||
// 아니면 현재 IsSelected 상태(true) 또는 버튼 자체(this)를 전달할 수 있습니다.
|
||||
// ToolbarView의 SetupButtonVisualsAndInteractions에서 radioModel.ExecuteClick(true)로 호출하는 경우,
|
||||
// parameter는 true가 됩니다.
|
||||
object commandParameterToUse = parameter ?? this; // 예: 명시적 파라미터가 없으면 버튼 인스턴스 전달
|
||||
|
||||
ClickCommand.Execute(commandParameterToUse);
|
||||
}
|
||||
// 만약 선택 해제 시에도 Command를 실행해야 한다면 위 if 조건을 제거합니다.
|
||||
// 혹은, 선택/해제 상태 모두에 대해 Command를 실행하되, Command 내부에서 IsSelected 값을 확인하도록 합니다.
|
||||
// 만약 선택 해제 시(다른 라디오 버튼이 선택되어 이 버튼이 해제될 때)에도 Command를 실행해야 한다면,
|
||||
// 위 if(IsSelected) 조건을 제거하거나, 별도의 Command(예: DeselectCommand)를 고려해야 합니다.
|
||||
// 하지만 일반적인 라디오 버튼의 사용 패턴은 선택 시의 액션에 중점을 둡니다.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +1,150 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace UVC.UI.Toolbar
|
||||
{
|
||||
/// <summary>
|
||||
/// 라디오 버튼들을 그룹으로 관리하여 하나만 선택되도록 합니다.
|
||||
/// ToolbarRadioButtonGroup, ToolbarExpandableButton 클래스는 이전 제안과 거의 동일하게 유지하되,
|
||||
/// 상태 변경 시 NotifyStateChanged() 호출을 고려할 수 있습니다.
|
||||
/// 예를 들어 ToolbarExpandableButton에서 SelectSubButton 후 주 버튼의 Text, Icon이 변경되면 NotifyStateChanged() 호출
|
||||
/// 여러 개의 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);
|
||||
button.RadioGroup = this;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 지정된 라디오 버튼을 그룹 내에서 선택된 상태로 설정합니다.
|
||||
/// 이전에 선택되었던 다른 버튼은 선택 해제 상태(IsSelected = false)로 변경됩니다.
|
||||
/// </summary>
|
||||
/// <param name="buttonToSelect">선택할 ToolbarRadioButton입니다. 이 버튼은 반드시 그룹에 미리 등록되어 있어야 합니다.</param>
|
||||
public void SetSelected(ToolbarRadioButton buttonToSelect)
|
||||
{
|
||||
if (!_buttons.Contains(buttonToSelect) || !buttonToSelect.IsEnabled) return;
|
||||
|
||||
SelectedButton = buttonToSelect;
|
||||
foreach (var btn in _buttons)
|
||||
if (buttonToSelect == null || !_buttons.Contains(buttonToSelect))
|
||||
{
|
||||
bool isNowSelected = (btn == SelectedButton);
|
||||
if (btn.IsSelected != isNowSelected) // 실제 상태 변경이 있을 때만
|
||||
{
|
||||
btn.IsSelected = isNowSelected; // 이 setter가 OnStateChanged를 호출
|
||||
// btn.OnClick?.Invoke(); // OnClick은 버튼 자체의 ExecuteClick에서 관리하는 것이 더 적절할 수 있음
|
||||
// 또는 선택 변경 시 항상 호출하고 싶다면 여기에 둠
|
||||
// btn.OnToggle?.Invoke(isNowSelected); // OnToggle은 IsSelected setter에서 OnToggleStateChanged로 대체 가능
|
||||
}
|
||||
// 그룹에 등록되지 않은 버튼을 선택하려고 하면 무시하거나 경고를 로깅할 수 있습니다.
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,44 @@
|
||||
namespace UVC.UI.Toolbar
|
||||
{
|
||||
/// <summary>
|
||||
/// 툴바 구분선을 나타냅니다.
|
||||
/// 툴바 내에서 항목들(예: 버튼 그룹)을 시각적으로 구분하는 구분선을 나타냅니다.
|
||||
/// IToolbarItem 인터페이스를 구현하여 툴바 시스템(ToolbarModel)에 추가될 수 있습니다.
|
||||
/// </summary>
|
||||
public class ToolbarSeparator : IToolbarItem { }
|
||||
}
|
||||
/// <remarks>
|
||||
/// ToolbarSeparator는 일반적으로 특별한 동작이나 속성을 가지지 않으며,
|
||||
/// View 레이어(예: ToolbarView)에서 이 항목을 만나면 시각적인 구분선 UI (예: 선, 공백 등)를 렌더링합니다.
|
||||
/// 프리팹 기반으로 UI가 생성될 경우, 구분선에 해당하는 프리팹이 사용됩니다.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // ToolbarController 또는 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;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,84 @@
|
||||
namespace UVC.UI.Toolbar
|
||||
{
|
||||
/// <summary>
|
||||
/// 일반적인 클릭 버튼입니다.
|
||||
/// 한 번 클릭으로 특정 동작을 수행하는 표준적인 버튼입니다.
|
||||
/// ToolbarButtonBase를 상속받아 텍스트, 아이콘, 툴팁, 활성화 상태 및 클릭 커맨드 기능을 가집니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 이 클래스는 ToolbarButtonBase의 기능을 그대로 사용하며, 특별히 추가된 로직은 없습니다.
|
||||
/// 복잡한 상태 관리 없이 단순 클릭 동작만을 필요로 하는 경우에 사용됩니다.
|
||||
/// View 레이어(예: ToolbarView)에서는 이 모델에 해당하는 UI 요소(예: UnityEngine.UI.Button)를 생성하고,
|
||||
/// 사용자가 UI 버튼을 클릭하면 이 모델의 ExecuteClick 메서드가 호출되도록 연결합니다.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // ToolbarController 또는 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 폴더 내 아이콘 경로
|
||||
/// TooltipKey = "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
|
||||
{
|
||||
// 생성자 또는 초기화 메서드가 있다면 ICommand를 받도록 수정
|
||||
// 예: public ToolbarStandardButton(ICommand command) { this.ClickCommand = command; }
|
||||
// ToolbarStandardButton은 ToolbarButtonBase의 모든 기능을 상속받습니다.
|
||||
// 특별히 ToolbarStandardButton만을 위한 추가적인 속성이나 메서드가 필요하다면 여기에 정의할 수 있습니다.
|
||||
|
||||
// ToolbarButtonBase의 ExecuteClick을 그대로 사용하거나,
|
||||
// 필요시 override 할 수 있습니다.
|
||||
// public override void ExecuteClick()
|
||||
// 예시: 생성자를 통해 주요 속성을 설정하도록 할 수도 있습니다.
|
||||
// /// <summary>
|
||||
// /// 지정된 속성으로 ToolbarStandardButton의 새 인스턴스를 초기화합니다.
|
||||
// /// </summary>
|
||||
// /// <param name="text">버튼 텍스트 또는 다국어 키입니다.</param>
|
||||
// /// <param name="iconSpritePath">아이콘의 Resources 경로입니다.</param>
|
||||
// /// <param name="command">클릭 시 실행될 커맨드입니다.</param>
|
||||
// /// <param name="tooltipKey">툴팁 텍스트 또는 다국어 키입니다.</param>
|
||||
// public ToolbarStandardButton(string text, string iconSpritePath = null, ICommand command = null, string tooltipKey = null)
|
||||
// {
|
||||
// base.ExecuteClick(); // 기본 Command 실행
|
||||
// // 추가적인 StandardButton만의 로직
|
||||
// this.Text = text;
|
||||
// this.IconSpritePath = iconSpritePath;
|
||||
// this.ClickCommand = command;
|
||||
// this.TooltipKey = tooltipKey;
|
||||
// }
|
||||
|
||||
// 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만의 추가적인 로직이 있다면 작성
|
||||
// // 예를 들어, 클릭 후 특정 사운드 재생 등
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,14 +4,73 @@ using UnityEngine;
|
||||
namespace UVC.UI.Toolbar
|
||||
{
|
||||
/// <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>
|
||||
/// // ToolbarController 또는 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) 상태 아이콘
|
||||
/// TooltipKey = "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;
|
||||
@@ -20,12 +79,17 @@ namespace UVC.UI.Toolbar
|
||||
if (_offIconSpritePath != value)
|
||||
{
|
||||
_offIconSpritePath = value;
|
||||
NotifyStateChanged();
|
||||
NotifyStateChanged();// 아이콘 경로 변경 시 전체 상태 변경으로 간주
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isSelected;
|
||||
/// <summary>
|
||||
/// 버튼의 현재 선택 상태를 나타냅니다. true이면 선택(On)된 상태, false이면 해제(Off)된 상태입니다.
|
||||
/// 이 속성 값이 변경되면 OnStateChanged 이벤트와 OnToggleStateChanged 이벤트가 발생하며,
|
||||
/// OnToggle 콜백이 호출됩니다.
|
||||
/// </summary>
|
||||
public bool IsSelected
|
||||
{
|
||||
get => _isSelected;
|
||||
@@ -34,13 +98,33 @@ namespace UVC.UI.Toolbar
|
||||
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;
|
||||
@@ -57,12 +141,19 @@ namespace UVC.UI.Toolbar
|
||||
IsSelected = !IsSelected;
|
||||
}
|
||||
|
||||
OnToggle?.Invoke(IsSelected); // IsSelected는 이미 위에서 최종 상태로 설정됨
|
||||
|
||||
// ClickCommand 실행. 필요하다면 IsSelected나 다른 값을 파라미터로 전달.
|
||||
// 여기서는 parameter 인자로 받은 값을 우선 사용하고, 없으면 IsSelected를 사용.
|
||||
object commandParameter = parameter ?? IsSelected;
|
||||
ClickCommand?.Execute(commandParameter);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
219
Assets/Scripts/UVC/UI/ToolBar/ToolboxController.cs
Normal file
219
Assets/Scripts/UVC/UI/ToolBar/ToolboxController.cs
Normal file
@@ -0,0 +1,219 @@
|
||||
using UnityEngine;
|
||||
using UVC.Locale;
|
||||
using UVC.UI.Commands;
|
||||
using UVC.UI.Toolbar;
|
||||
|
||||
namespace UVC.UI.ToolBar
|
||||
{
|
||||
/// <summary>
|
||||
/// 툴바의 생성, 설정 및 관리를 담당하는 MonoBehaviour 컨트롤러 클래스입니다.
|
||||
/// ToolbarModel(데이터)과 ToolbarView(UI 표현) 사이의 중재자 역할을 합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 주요 역할:
|
||||
/// - 초기화(Awake, Start): 필요한 컴포넌트(특히 ToolbarView)를 찾거나 확인합니다.
|
||||
/// - 모델 생성 및 설정(Start): ToolbarModel 인스턴스를 만들고,
|
||||
/// AddStandardButton, AddRadioButton 등의 메서드를 사용해 툴바에 표시될 항목들을 정의하고 추가합니다.
|
||||
/// - 뷰 초기화(Start): 설정된 ToolbarModel을 ToolbarView에 전달하여 UI를 렌더링하도록 합니다.
|
||||
///
|
||||
/// 이 클래스의 인스턴스는 Unity 씬 내의 GameObject에 컴포넌트로 추가되어야 합니다.
|
||||
/// ToolbarView 컴포넌트도 동일한 GameObject 또는 그 자식에 존재해야 합니다.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Unity 에디터에서 GameObject를 생성하고 이 ToolboxController 스크립트를 추가합니다.
|
||||
/// // 또한, ToolbarView 스크립트도 해당 GameObject 또는 자식 GameObject에 추가하고,
|
||||
/// // ToolbarView의 프리팹 필드들(standardButtonPrefab 등)을 Inspector에서 할당해야 합니다.
|
||||
///
|
||||
/// // 이 스크립트의 Start 메서드 내에서 툴바 항목들이 정의됩니다.
|
||||
/// // 예:
|
||||
/// // mainToolbar.AddStandardButton("파일 열기", "icons/open", new ActionCommand(OpenFile), "파일을 엽니다.");
|
||||
/// // private void OpenFile() { Debug.Log("파일 열기 기능 실행"); }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class ToolboxController : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// 툴바의 데이터 모델입니다. 툴바 항목들의 정보와 구조를 담고 있습니다.
|
||||
/// protected로 선언되어 파생 클래스에서 접근 가능합니다.
|
||||
/// </summary>
|
||||
protected ToolbarModel mainToolbar;
|
||||
|
||||
/// <summary>
|
||||
/// 툴바의 UI 표현을 담당하는 뷰 컴포넌트입니다.
|
||||
/// protected로 선언되어 파생 클래스에서 접근 가능합니다.
|
||||
/// </summary>
|
||||
protected ToolbarView mainToolbarView;
|
||||
|
||||
/// <summary>
|
||||
/// MonoBehaviour의 Awake 메서드입니다.
|
||||
/// 주로 현재 GameObject 또는 자식 GameObject에서 ToolbarView 컴포넌트를 찾아 mainToolbarView 필드에 할당합니다.
|
||||
/// </summary>
|
||||
protected virtual void Awake()
|
||||
{
|
||||
// 1. 이 GameObject에 연결된 ToolbarView 컴포넌트를 찾습니다.
|
||||
mainToolbarView = GetComponent<ToolbarView>();
|
||||
|
||||
// 2. 만약 현재 GameObject에 없다면, 자식 GameObject들 중에서 ToolbarView 컴포넌트를 찾습니다.
|
||||
if (mainToolbarView == null)
|
||||
{
|
||||
mainToolbarView = GetComponentInChildren<ToolbarView>();
|
||||
}
|
||||
|
||||
if (mainToolbarView == null)
|
||||
{
|
||||
Debug.LogError("ToolboxController: ToolbarView 컴포넌트를 찾을 수 없습니다. GameObject에 ToolbarView를 추가하고 연결해주세요.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MonoBehaviour의 Start 메서드입니다. 첫 번째 프레임 업데이트 전에 호출됩니다.
|
||||
/// ToolbarModel을 생성하고, 다양한 툴바 항목들을 모델에 추가한 후,
|
||||
/// 설정된 모델을 사용하여 ToolbarView를 초기화(UI 렌더링)합니다.
|
||||
/// </summary>
|
||||
protected virtual void Start()
|
||||
{
|
||||
// ToolbarModel 인스턴스 생성
|
||||
mainToolbar = new ToolbarModel();
|
||||
|
||||
// ToolbarView가 제대로 할당되었는지 확인
|
||||
if (mainToolbarView == null)
|
||||
{
|
||||
// Awake에서 이미 로그를 남겼을 수 있지만, 한 번 더 확인하여 Start 로직 중단
|
||||
Debug.LogError("ToolboxController: ToolbarView가 할당되지 않아 툴바를 초기화할 수 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// --- 툴바 모델 구성 시작 ---
|
||||
// 여기에 다양한 툴바 항목(버튼, 구분선 등)을 mainToolbar 모델에 추가합니다.
|
||||
|
||||
// 예시 1: 카메라 조절 라디오 버튼 그룹
|
||||
// "CameraControlGroup"이라는 이름으로 라디오 버튼 그룹을 만듭니다.
|
||||
// AddRadioButton의 세 번째 파라미터(initialState)는 해당 버튼이 초기에 선택될지 여부입니다.
|
||||
// 각 버튼은 아이콘 경로(선택 시/해제 시), OnToggle 콜백, ClickCommand, 툴팁 키를 가질 수 있습니다.
|
||||
mainToolbar.AddRadioButton("CameraControlGroup", "Top View", true,
|
||||
"Prefabs/UI/Toolbar/images/ic_camera_top_on",
|
||||
"Prefabs/UI/Toolbar/images/ic_camera_top_off_white",
|
||||
(isSelected) => { if (isSelected) Debug.Log("탑뷰 카메라 선택됨"); },
|
||||
new ActionCommand(() => Debug.Log("탑뷰 카메라 Command 실행")),
|
||||
"Top View 시점으로 변경합니다.");
|
||||
|
||||
mainToolbar.AddRadioButton("CameraControlGroup", "Quarter View", false,
|
||||
"Prefabs/UI/Toolbar/images/ic_camera_quarter_on",
|
||||
"Prefabs/UI/Toolbar/images/ic_camera_quarter_off_white",
|
||||
(isSelected) => { if (isSelected) Debug.Log("쿼터뷰 카메라 선택됨"); },
|
||||
new ActionCommand(() => Debug.Log("쿼터뷰 카메라 Command 실행")),
|
||||
"Quarter View 시점으로 변경합니다.");
|
||||
|
||||
mainToolbar.AddRadioButton("CameraControlGroup", "Front View", false,
|
||||
"Prefabs/UI/Toolbar/images/ic_camera_top_on",
|
||||
"Prefabs/UI/Toolbar/images/ic_camera_top_off_white",
|
||||
(isSelected) => { if (isSelected) Debug.Log("프런트뷰 카메라 선택됨"); },
|
||||
new ActionCommand(() => Debug.Log("프런트뷰 카메라 Command 실행")),
|
||||
"Front View 시점으로 변경합니다.");
|
||||
|
||||
// 예시 2: 구분선 추가
|
||||
mainToolbar.AddSeparator();
|
||||
|
||||
// 예시 3: 표준 버튼 (객체 선택)
|
||||
// AddStandardButton은 텍스트, 아이콘 경로, 클릭 커맨드, 툴팁 키를 파라미터로 받습니다.
|
||||
mainToolbar.AddStandardButton("선택", // 버튼 텍스트 (또는 다국어 키)
|
||||
"Prefabs/UI/Toolbar/images/ic_select_white", // 아이콘 경로
|
||||
new ActionCommand(() => Debug.Log("객체 선택 버튼 클릭됨")), // 클릭 시 실행될 커맨드
|
||||
"객체를 선택합니다."); // 툴팁
|
||||
|
||||
// 객체 이동
|
||||
mainToolbar.AddStandardButton("이동",
|
||||
"Prefabs/UI/Toolbar/images/ic_move_white",
|
||||
new ActionCommand(() => Debug.Log("객체 이동 버튼 클릭됨")),
|
||||
"객체를 이동 시킵니다.");
|
||||
|
||||
// 객체 회전
|
||||
mainToolbar.AddStandardButton("회전",
|
||||
"Prefabs/UI/Toolbar/images/ic_rotation_white",
|
||||
new ActionCommand(() => Debug.Log("객체 회전 버튼 클릭됨")),
|
||||
"객체의 각도를 조절합니다.");
|
||||
|
||||
// 객체 크기조절
|
||||
mainToolbar.AddStandardButton("크기조절",
|
||||
"Prefabs/UI/Toolbar/images/ic_scale_white",
|
||||
new ActionCommand(() => Debug.Log("객체 크기조절 버튼 클릭됨")),
|
||||
"객체 크기를 조절합니다.");
|
||||
|
||||
// 객체 복제
|
||||
mainToolbar.AddStandardButton("복제",
|
||||
"Prefabs/UI/Toolbar/images/ic_copy_white",
|
||||
new ActionCommand(() => Debug.Log("객체 복제 버튼 클릭됨")),
|
||||
"객체를 복제 합니다.");
|
||||
|
||||
// 객체 삭제
|
||||
mainToolbar.AddStandardButton("삭제",
|
||||
"Prefabs/UI/Toolbar/images/ic_delete_white",
|
||||
new ActionCommand(() => Debug.Log("객체 삭제 버튼 클릭됨")),
|
||||
"객체를 삭제 합니다.");
|
||||
|
||||
mainToolbar.AddSeparator();
|
||||
|
||||
// 예시 4: 화면 캡처 버튼 (텍스트가 다국어 키일 수 있음)
|
||||
mainToolbar.AddStandardButton("button_capture_screen", // 다국어 키로 사용될 수 있는 텍스트
|
||||
"Prefabs/UI/Toolbar/images/ic_chapture_white",
|
||||
new ActionCommand(() => Debug.Log("화면 캡처 버튼 클릭됨")),
|
||||
"tooltip_capture_screen"); // 툴팁도 다국어 키 사용 가능
|
||||
|
||||
// 예시 5: 화면 녹화 시작/중지 토글 버튼
|
||||
// AddToggleButton은 초기 상태, 선택/해제 아이콘, OnToggle 콜백 등을 설정합니다.
|
||||
// ClickCommand는 ActionCommand<bool>을 사용하여 현재 토글 상태를 파라미터로 받을 수 있습니다.
|
||||
mainToolbar.AddToggleButton("button_record_screen", false, // 초기 상태: 꺼짐(false)
|
||||
"Prefabs/UI/Toolbar/images/ic_record_on_white", // 켜짐(selected) 상태 아이콘
|
||||
"Prefabs/UI/Toolbar/images/ic_record_off_white", // 꺼짐(deselected) 상태 아이콘
|
||||
(isSelected) => Debug.Log($"화면 녹화 상태: {(isSelected ? "녹화 중" : "중지")} (OnToggle 콜백)"),
|
||||
new ActionCommand<bool>((isRecording) => Debug.Log($"화면 녹화 Command 실행: {(isRecording ? "녹화 시작" : "녹화 중지")}")),
|
||||
"tooltip_record_screen");
|
||||
|
||||
|
||||
// 예시 6: 확장 버튼 (브러시 크기 선택)
|
||||
// AddExpandableButton으로 주 버튼을 만들고, 반환된 객체의 SubButtons 리스트에 하위 버튼들을 추가합니다.
|
||||
var expandableBtnModel = mainToolbar.AddExpandableButton("button_brush_size", // 주 버튼 텍스트/키
|
||||
"Prefabs/UI/Toolbar/images/ic_brush_default_white", // 주 버튼 기본 아이콘
|
||||
new ActionCommand(() => Debug.Log("브러시 크기 주 버튼 클릭됨 (Command)")), // 주 버튼 자체의 커맨드
|
||||
"붓 사이즈 선택 합니다."); // 주 버튼 툴팁
|
||||
|
||||
// 하위 버튼1: 작은 브러시 (ToolbarStandardButton 사용)
|
||||
var smallBrushCmd = new ActionCommand(() => Debug.Log($"작은 브러시 선택됨"));
|
||||
var smallBrush = new ToolbarStandardButton
|
||||
{
|
||||
Text = "brush_size_small", // 하위 버튼 텍스트/키
|
||||
IconSpritePath = "Prefabs/UI/Toolbar/images/ic_brush_small_white", // 하위 버튼 아이콘
|
||||
TooltipKey = "tooltip_brush_small", // 하위 버튼 툴팁
|
||||
ClickCommand = smallBrushCmd
|
||||
};
|
||||
expandableBtnModel.SubButtons.Add(smallBrush); // 확장 버튼 모델에 하위 버튼 추가
|
||||
|
||||
// 하위 버튼2: 중간 브러시
|
||||
var mediumBrush = new ToolbarStandardButton
|
||||
{
|
||||
Text = "brush_size_medium",
|
||||
IconSpritePath = "Prefabs/UI/Toolbar/images/ic_brush_medium_white",
|
||||
TooltipKey = "tooltip_brush_medium",
|
||||
ClickCommand = new ActionCommand(() => Debug.Log("중간 브러시 선택됨 (Sub-Command 실행)"))
|
||||
};
|
||||
expandableBtnModel.SubButtons.Add(mediumBrush);
|
||||
|
||||
// 확장 버튼의 하위 버튼이 선택되었을 때 호출될 콜백 설정
|
||||
expandableBtnModel.OnSubButtonSelected = (selectedSubButtonModel) =>
|
||||
{
|
||||
// LocalizationManager를 사용하여 텍스트를 현재 언어에 맞게 가져올 수 있습니다.
|
||||
string localizedSubButtonText = LocalizationManager.Instance != null ? LocalizationManager.Instance.GetString(selectedSubButtonModel.Text) : selectedSubButtonModel.Text;
|
||||
Debug.Log($"브러시 크기 '{localizedSubButtonText}' 선택됨 (OnSubButtonSelected 콜백). 주 버튼 업데이트 로직 실행 가능.");
|
||||
};
|
||||
// --- 툴바 모델 구성 끝 ---
|
||||
|
||||
// 설정이 완료된 ToolbarModel을 ToolbarView에 전달하여 UI 렌더링 시작
|
||||
mainToolbarView.Initialize(mainToolbar);
|
||||
|
||||
// 예시: 모델 상태를 코드로 변경하고 UI가 업데이트되는지 테스트 (주석 처리된 기존 코드)
|
||||
// StartCoroutine(TestModelChange(saveBtnModel, muteToggleModel));
|
||||
// 이 테스트는 특정 버튼 모델의 상태를 시간차를 두고 변경하여 UI가 반응하는지 확인하는 용도입니다.
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/UVC/UI/ToolBar/ToolboxController.cs.meta
Normal file
2
Assets/Scripts/UVC/UI/ToolBar/ToolboxController.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b8045a62c23fdbb479bb5160bba0d9be
|
||||
Reference in New Issue
Block a user