Files
EnglewoodLAB/Assets/Scripts/UVC/UI/ToolBar/View/ToolbarToggleButtonViewProcessor.cs

161 lines
12 KiB
C#

using UnityEngine;
using UnityEngine.UI;
using UVC.UI.Toolbar.Model;
namespace UVC.UI.Toolbar.View
{
/// <summary>
/// <see cref="ToolbarToggleButton"/> 모델의 UI 표현을 생성하고 관리하는 클래스입니다.
/// 이 클래스는 <see cref="IButtonViewProcessor"/> 인터페이스를 구현하여,
/// 토글 버튼의 UI GameObject 생성, 이벤트 바인딩, 시각적 업데이트 로직을 담당합니다.
/// </summary>
/// <remarks>
/// 주요 역할:
/// - <see cref="ToolbarView.toggleButtonPrefab"/>을 사용하여 토글 버튼 UI GameObject를 인스턴스화합니다.
/// - 생성된 UI의 <see cref="Toggle"/> 컴포넌트와 <see cref="ToolbarToggleButton"/> 모델 간의 상호작용을 설정합니다.
/// (예: UI 토글 시 모델의 <see cref="ToolbarToggleButton.ExecuteClick"/> 호출)
/// - 모델의 상태 변경(<see cref="ToolbarButtonBase.OnStateChanged"/>, <see cref="ToolbarToggleButton.OnToggleStateChanged"/>)에 따라
/// UI의 시각적 요소(텍스트, 아이콘, 토글 상태 등)를 업데이트합니다.
/// </remarks>
/// <example>
/// <code>
/// // ToolbarView 내에서 이 프로세서가 사용되는 방식 (간략화된 예시):
/// // ToolbarView view = GetComponent<ToolbarView>();
/// // ToolbarToggleButton toggleModel = new ToolbarToggleButton { Text = "ToggleMe", IsSelected = true };
/// //
/// // IButtonViewProcessor processor = new ToolbarToggleButtonViewProcessor();
/// //
/// // // 1. UI 생성
/// // GameObject buttonUI = processor.CreateButtonUI(toggleModel, view.toolbarContainer, view);
/// //
/// // // 2. 상호작용 및 초기 상태 설정
/// // processor.SetupButtonInteractions(toggleModel, buttonUI, view);
/// //
/// // // 3. 모델 상태 변경 시 UI 업데이트 (ToolbarView의 RenderToolbar 내에서 이벤트 구독을 통해 자동 처리됨)
/// // // toggleModel.IsSelected = false; // 이 변경이 UI에 반영됨
/// // // toggleModel.Text = "New Text"; // 이 변경도 UI에 반영됨
/// </code>
/// </example>
public class ToolbarToggleButtonViewProcessor : IButtonViewProcessor
{
/// <summary>
/// <see cref="ToolbarToggleButton"/> 모델에 대한 UI GameObject를 생성합니다.
/// <see cref="ToolbarView.toggleButtonPrefab"/>을 사용하여 인스턴스화합니다.
/// </summary>
/// <param name="buttonModel">UI를 생성할 대상인 <see cref="ToolbarButtonBase"/> 타입의 모델 객체입니다. 내부적으로 <see cref="ToolbarToggleButton"/>으로 캐스팅하여 사용합니다.</param>
/// <param name="parentContainer">생성된 UI GameObject가 자식으로 추가될 부모 <see cref="Transform"/>입니다.</param>
/// <param name="viewContext">현재 <see cref="ToolbarView"/>의 컨텍스트입니다. 프리팹 참조 등에 사용됩니다.</param>
/// <returns>생성된 UI GameObject입니다. <see cref="ToolbarView.toggleButtonPrefab"/>이 할당되지 않은 경우 null을 반환할 수 있습니다.</returns>
public GameObject CreateButtonUI(ToolbarButtonBase buttonModel, Transform parentContainer, ToolbarView viewContext)
{
if (viewContext.toggleButtonPrefab == null)
{
Debug.LogError("ToggleButtonViewProcessor: toggleButtonPrefab이 ToolbarView에 할당되지 않았습니다.", viewContext);
return null;
}
// toggleButtonPrefab을 사용하여 새 GameObject를 만들고 parentContainer의 자식으로 배치합니다.
return Object.Instantiate(viewContext.toggleButtonPrefab, parentContainer);
}
/// <summary>
/// 생성된 토글 버튼 UI GameObject에 초기 시각적 요소를 설정하고, 사용자 상호작용 및 모델 상태 변경에 따른 UI 업데이트 로직을 바인딩합니다.
/// </summary>
/// <param name="buttonModel">설정할 버튼의 데이터 모델 (<see cref="ToolbarToggleButton"/> 타입이어야 함).</param>
/// <param name="buttonUIObject">모델에 해당하는, 화면에 표시될 UI GameObject.</param>
/// <param name="viewContext">현재 <see cref="ToolbarView"/>의 컨텍스트.</param>
/// <remarks>
/// 이 메서드는 다음을 수행합니다:
/// 1. <paramref name="buttonModel"/>을 <see cref="ToolbarToggleButton"/>으로 캐스팅합니다.
/// 2. <paramref name="buttonUIObject"/>에서 <see cref="Toggle"/> 컴포넌트를 찾습니다.
/// 3. <see cref="Toggle"/> 컴포넌트의 초기 `isOn` 상태를 모델의 <see cref="ToolbarToggleButton.IsSelected"/> 값으로 설정합니다. (UI 이벤트 발생 없이)
/// 4. <see cref="Toggle"/> 컴포넌트의 `OnValueChanged` 이벤트에 리스너를 추가하여, UI에서 토글 상태가 변경되면 모델의 <see cref="ToolbarToggleButton.ExecuteClick"/> 메서드를 호출합니다.
/// 이를 통해 모델의 상태가 업데이트되고, 연결된 커맨드가 실행될 수 있습니다.
/// 5. <see cref="UpdateCommonButtonVisuals"/>를 호출하여 텍스트, 아이콘 등 공통 시각 요소를 초기화합니다.
/// 6. <see cref="UpdateToggleStateVisuals"/>를 호출하여 토글 상태에 따른 시각적 표현(예: 아이콘 변경)을 초기화합니다.
/// </remarks>
public void SetupButtonInteractions(ToolbarButtonBase buttonModel, GameObject buttonUIObject, ToolbarView viewContext)
{
ToolbarToggleButton toggleModel = buttonModel as ToolbarToggleButton;
if (toggleModel == null) return;
Toggle toggleComponent = buttonUIObject.GetComponent<Toggle>();
if (toggleComponent != null)
{
// 1. 모델의 현재 선택 상태로 UI의 Toggle 컴포넌트 상태를 초기화합니다.
// SetIsOnWithoutNotify를 사용하여 이 변경으로 인해 OnValueChanged 이벤트가 발생하지 않도록 합니다.
toggleComponent.SetIsOnWithoutNotify(toggleModel.IsSelected);
// 2. UI의 Toggle 컴포넌트의 값이 변경될 때(사용자가 클릭 등) 모델의 ExecuteClick 메서드를 호출합니다.
// ExecuteClick 내부에서 IsSelected 상태가 변경되고, 관련 커맨드가 실행되며, OnToggleStateChanged 이벤트가 발생합니다.
toggleComponent.onValueChanged.AddListener((isSelected) =>
{
// IsSelected 파라미터는 UI에서 변경된 새 상태이지만,
// ToolbarToggleButton의 ExecuteClick() 내부에서 IsSelected가 토글되므로
// IsSelected 파라미터를 직접 사용하지 않고 ExecuteClick()만 호출합니다.
// ExecuteClick()이 모델의 상태를 올바르게 변경하고 이벤트를 발생시킬 책임이 있습니다.
toggleModel.ExecuteClick();
});
}
else
{
Debug.LogError($"ToggleButtonViewProcessor: ToggleButton '{toggleModel.Text}'의 GameObject에 Toggle 컴포넌트가 없습니다.", buttonUIObject);
}
// 공통 시각적 요소(텍스트, 아이콘, 활성화 상태) 업데이트
UpdateCommonButtonVisuals(buttonModel, buttonUIObject, viewContext);
// 토글 상태에 따른 시각적 요소(예: 선택/해제 시 아이콘 변경) 업데이트
// 이 호출은 초기 상태를 반영하고, 모델의 IsSelected 값에 따라 올바른 아이콘(On/Off)을 표시하도록 합니다.
UpdateToggleStateVisuals(toggleModel, buttonUIObject, toggleModel.IsSelected, viewContext);
}
/// <summary>
/// 버튼 모델의 공통적인 상태(텍스트, 아이콘, 활성화 상태 등)가 변경되었을 때,
/// 해당 버튼 UI의 시각적 요소를 업데이트합니다.
/// 이 메서드는 <see cref="ToolbarView.InternalUpdateCommonButtonVisuals"/>를 호출하여 실제 업데이트 로직을 수행합니다.
/// </summary>
/// <param name="buttonModel">상태가 변경된 버튼의 모델입니다.</param>
/// <param name="buttonUIObject">업데이트할 UI GameObject입니다.</param>
/// <param name="viewContext">현재 <see cref="ToolbarView"/>의 컨텍스트입니다.</param>
public void UpdateCommonButtonVisuals(ToolbarButtonBase buttonModel, GameObject buttonUIObject, ToolbarView viewContext)
{
// ToolbarView에 있는 공통 업데이트 로직을 사용합니다.
// 이 메서드는 텍스트, 기본 아이콘(토글 상태에 따라 다를 수 있음), 활성화 상태(interactable) 등을 업데이트합니다.
viewContext.InternalUpdateCommonButtonVisuals(buttonModel, buttonUIObject);
}
/// <summary>
/// <see cref="ToolbarToggleButton"/>의 선택 상태(<see cref="ToolbarToggleButton.IsSelected"/>)가 모델에서 변경되었을 때 호출됩니다.
/// 해당 UI <see cref="Toggle"/> 컴포넌트의 `isOn` 상태를 모델의 <see cref="ToolbarToggleButton.IsSelected"/> 값과 동기화하고,
/// 선택 상태에 따른 아이콘 변경 등을 위해 <see cref="ToolbarView.InternalUpdateCommonButtonVisuals"/>를 호출합니다.
/// </summary>
/// <param name="toggleButtonModel">IsSelected 상태가 변경된 토글 버튼의 모델입니다.</param>
/// <param name="buttonUIObject">업데이트할 UI GameObject입니다.</param>
/// <param name="isSelected">모델의 새로운 <see cref="ToolbarToggleButton.IsSelected"/> 값입니다.</param>
/// <param name="viewContext">현재 <see cref="ToolbarView"/>의 컨텍스트입니다.</param>
/// <remarks>
/// 이 메서드는 주로 모델의 <see cref="ToolbarToggleButton.OnToggleStateChanged"/> 이벤트에 의해 호출됩니다.
/// UI의 <see cref="Toggle"/> 컴포넌트 상태를 모델과 동기화하고,
/// 선택/해제 상태에 따라 다른 아이콘을 사용한다면 <see cref="ToolbarView.InternalUpdateCommonButtonVisuals"/>가 이를 처리합니다.
/// </remarks>
public void UpdateToggleStateVisuals(ToolbarToggleButton toggleButtonModel, GameObject buttonUIObject, bool isSelected, ToolbarView viewContext)
{
Toggle toggleComponent = buttonUIObject.GetComponent<Toggle>();
if (toggleComponent != null)
{
// 모델의 IsSelected 상태와 UI의 Toggle.isOn 상태가 다를 경우에만 UI를 업데이트합니다.
// 이는 불필요한 UI 업데이트 및 이벤트 발생을 방지합니다.
if (toggleComponent.isOn != isSelected)
{
toggleComponent.SetIsOnWithoutNotify(isSelected);
}
}
// 아이콘 업데이트를 위해 InternalUpdateCommonButtonVisuals를 다시 호출합니다.
// ToolbarToggleButton은 IsSelected 상태에 따라 다른 아이콘(IconSpritePath 또는 OffIconSpritePath)을
// 가질 수 있으므로, 이 상태 변경 시 아이콘도 업데이트되어야 합니다.
// InternalUpdateCommonButtonVisuals 내부에서 ToolbarToggleButton 타입인지 확인하고 적절한 아이콘 경로를 사용합니다.
viewContext.InternalUpdateCommonButtonVisuals(toggleButtonModel, buttonUIObject);
}
}
}