using UnityEngine;
using UnityEngine.UI;
using UVC.Extension;
using UVC.UI.Toolbar.Model;
using UVC.UI.Tooltip;
namespace UVC.UI.Toolbar.View
{
///
/// 확장 가능한 툴바 버튼(ToolbarExpandableButton)의 하위 메뉴 UI를 생성, 관리 및 상호작용을 처리하는 헬퍼 클래스입니다.
/// ToolbarView로부터 하위 메뉴 관련 로직을 위임받아 처리하여 ToolbarView의 복잡도를 낮춥니다.
///
///
/// 주요 역할:
/// - 하위 메뉴 토글: 특정 확장 버튼에 대한 하위 메뉴를 열거나 닫습니다.
/// - 하위 메뉴 UI 생성: `subMenuPanelPrefab`을 사용하여 하위 메뉴의 패널과 그 안의 버튼들을 동적으로 생성합니다.
/// - 위치 계산: 하위 메뉴가 나타날 위치를 주 확장 버튼의 위치를 기준으로 계산하여 설정합니다.
/// - 이벤트 처리: 생성된 하위 메뉴의 버튼들에 대한 클릭 이벤트를 설정하고, 클릭 시 메뉴를 닫는 등의 동작을 처리합니다.
/// - 외부 클릭 감지: 하위 메뉴가 열려 있을 때 메뉴 영역 바깥을 클릭하면 메뉴가 닫히도록 처리합니다.
/// - 리소스 정리: 하위 메뉴가 닫힐 때 생성되었던 모든 UI 요소와 이벤트 리스너를 깨끗하게 제거합니다.
///
///
///
/// // 이 클래스는 ToolbarView 내부에서 다음과 같이 사용됩니다.
///
/// // 1. ToolbarView의 Awake 메서드에서 SubMenuHandler 인스턴스 생성
/// // public class ToolbarView : MonoBehaviour
/// // {
/// // private SubMenuHandler _subMenuHandler;
/// //
/// // void Awake()
/// // {
/// // _subMenuHandler = new SubMenuHandler(this);
/// // }
/// //
/// // // ...
/// // }
///
/// // 2. 확장 버튼(ExpandableButton)의 UI 프로세서에서 `ToggleSubMenu` 호출
/// // public class ToolbarExpandableButtonViewProcessor : IButtonViewProcessor
/// // {
/// // public void SetupButtonInteractions(ToolbarButtonBase buttonModel, GameObject buttonUIObject, ToolbarView viewContext)
/// // {
/// // ToolbarExpandableButton expandableModel = buttonModel as ToolbarExpandableButton;
/// // Button uiButton = buttonUIObject.GetComponent
///
public class SubMenuHandler
{
private readonly ToolbarView _view;
private GameObject _currentSubMenu;
private ToolbarExpandableButton _ownerOfCurrentSubMenu;
///
/// SubMenuHandler의 새 인스턴스를 초기화합니다.
///
/// 이 핸들러를 소유하고 관리하는 ToolbarView의 인스턴스입니다. 프리팹, 캔버스 등의 참조에 사용됩니다.
public SubMenuHandler(ToolbarView view)
{
_view = view;
}
///
/// 확장 버튼에 대한 하위 메뉴 UI를 토글합니다.
/// 이미 해당 버튼의 하위 메뉴가 열려있으면 닫고, 닫혀있으면 엽니다.
/// 다른 버튼의 하위 메뉴가 열려있었다면, 그 메뉴는 닫고 새로운 메뉴를 엽니다.
///
/// 하위 버튼 목록을 가진 확장 버튼의 데이터 모델입니다.
/// 하위 메뉴의 위치를 결정하는 데 사용될 주 확장 버튼의 UI GameObject입니다.
public void ToggleSubMenu(ToolbarExpandableButton expandableButtonModel, GameObject expandableButtonObj)
{
bool closeOnly = false;
// 이미 하위 메뉴가 열려있는 경우
if (_currentSubMenu != null)
{
// 현재 열린 메뉴가 지금 클릭한 버튼에 의해 열린 것인지 확인
if (_ownerOfCurrentSubMenu == expandableButtonModel)
{
// 같은 버튼을 다시 클릭했으므로, 메뉴를 닫기만 하고 다시 열지 않습니다.
closeOnly = true;
}
// 기존에 열려있던 하위 메뉴를 정리합니다.
DestroyCurrentSubMenuAndClearListeners();
}
// 닫기만 해야 하는 경우, 여기서 함수를 종료합니다.
if (closeOnly)
{
return;
}
// --- 새 하위 메뉴를 여는 로직 ---
// 필요한 프리팹이 할당되었는지 확인합니다.
if (_view.subMenuPanelPrefab == null)
{
Debug.LogError("ToolbarView: subMenuPanelPrefab이 할당되지 않아 하위 메뉴를 열 수 없습니다.", _view);
return;
}
// 하위 버튼이 하나라도 있는지 확인합니다.
if (expandableButtonModel.SubButtons == null || expandableButtonModel.SubButtons.Count == 0)
{
return; // 하위 버튼이 없으면 메뉴를 열지 않습니다.
}
// 하위 메뉴 패널 UI를 생성합니다. 생성 위치는 ToolbarView의 자식으로 합니다.
_currentSubMenu = Object.Instantiate(_view.subMenuPanelPrefab, _view.transform);
_currentSubMenu.name = $"SubMenu_{expandableButtonModel.Text}";
_ownerOfCurrentSubMenu = expandableButtonModel; // 이 메뉴를 연 버튼 모델을 기록합니다.
RectTransform panelRect = _currentSubMenu.GetComponent();
if (panelRect == null)
{
Debug.LogError("ToolbarView: subMenuPanelPrefab에 RectTransform이 없습니다.", _currentSubMenu);
Object.Destroy(_currentSubMenu);
_currentSubMenu = null;
return;
}
// 하위 메뉴 패널의 위치와 피벗을 설정합니다.
RectTransform expandableButtonRect = expandableButtonObj.GetComponent();
panelRect.anchorMin = new Vector2(0, 1);
panelRect.anchorMax = new Vector2(0, 1);
panelRect.pivot = new Vector2(0, 1); // 좌상단 기준
_currentSubMenu.SetActive(true);
// UI 계층에서 가장 마지막 자식으로 만들어 다른 UI 요소들 위에 표시되도록 합니다.
if (panelRect.parent != null) panelRect.SetAsLastSibling();
// 하위 메뉴 패널에 LayoutGroup이 있는지 확인하고, 없다면 기본값으로 추가합니다.
LayoutGroup subMenuLayoutGroup = _currentSubMenu.GetComponent();
if (subMenuLayoutGroup == null)
{
subMenuLayoutGroup = _currentSubMenu.AddComponent();
}
// 툴바의 레이아웃 방향(수직/수평)에 따라 하위 메뉴의 위치를 조정합니다.
Vector2 offset = new Vector2(expandableButtonRect.anchoredPosition.x, expandableButtonRect.anchoredPosition.y);
if (_view.layoutGroup is VerticalLayoutGroup)
{
// 툴바가 수직이면, 버튼 오른쪽에 메뉴 표시
offset.x += expandableButtonRect.rect.width;
}
else
{
// 툴바가 수평이면, 버튼 아래쪽에 메뉴 표시
if (subMenuLayoutGroup != null) offset.x -= subMenuLayoutGroup.padding.left;
offset.y -= expandableButtonRect.rect.height;
}
panelRect.anchoredPosition = offset;
// 확장 버튼 모델에 포함된 모든 하위 버튼들을 UI로 생성합니다.
foreach (var subItemBase in expandableButtonModel.SubButtons)
{
if (subItemBase is ToolbarButtonBase subItemModel)
{
// 하위 버튼 타입에 맞는 UI 처리기를 가져옵니다.
IButtonViewProcessor subProcessor = _view.GetButtonViewProcessor(subItemModel.GetType());
if (subProcessor == null)
{
Debug.LogWarning($"하위 버튼 '{subItemModel.Text}'에 대한 ViewProcessor를 찾을 수 없습니다.", _view);
continue;
}
// 하위 버튼 UI를 생성합니다.
GameObject subButtonObj = subProcessor.CreateButtonUI(subItemModel, panelRect, _view);
if (subButtonObj == null)
{
Debug.LogError($"하위 버튼 '{subItemModel.Text}'의 UI를 생성하지 못했습니다.", _view);
continue;
}
subButtonObj.name = $"SubItem_{subItemModel.Text}";
// 하위 버튼의 상호작용을 설정합니다. (클릭 이벤트 등)
subProcessor.SetupButtonInteractions(subItemModel, subButtonObj, _view);
// 하위 버튼의 공통 시각적 요소(텍스트, 아이콘 등)를 업데이트합니다.
_view.InternalUpdateCommonButtonVisuals(subItemModel, subButtonObj);
// 하위 버튼의 클릭 이벤트에 추가적인 로직을 연결합니다.
// (하위 메뉴를 닫고, 주 확장 버튼의 상태를 업데이트하는 등)
var buttonComponent = subButtonObj.GetComponent