using System; using System.Collections.Generic; // using UnityEngine.UI; // Image 사용을 위해 필요할 수 있으나, 모델 클래스에서는 직접적인 UI 참조를 최소화하는 것이 좋음 namespace UVC.UI.Toolbar.Model { /// /// 클릭 시 하위 버튼 목록(서브 메뉴)을 표시하거나 숨길 수 있는 확장형 버튼입니다. /// ToolbarButtonBase를 상속받아 기본적인 버튼 속성(텍스트, 아이콘, 커맨드 등)을 가집니다. /// 주 버튼을 클릭하면 연결된 서브 메뉴가 표시되며, 서브 메뉴의 버튼을 선택하면 /// 주 버튼의 텍스트나 아이콘이 선택된 하위 버튼의 것으로 변경될 수 있습니다. /// /// /// 이 모델 클래스는 데이터와 상태를 관리합니다. 실제 서브 메뉴의 표시/숨김 및 UI 렌더링은 /// ToolbarView와 같은 View 클래스에서 처리됩니다. /// /// /// /// // 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}' 선택됨. 주 버튼 아이콘/텍스트 업데이트됨."); /// // 여기서 추가적인 로직 수행 가능 (예: 실제 브러시 크기 변경) /// }; /// /// public class ToolbarExpandableButton : ToolbarButtonBase { /// /// 이 확장 버튼에 속한 하위 버튼들의 목록입니다. /// 이 목록에 있는 버튼들은 주 버튼 클릭 시 View에 의해 표시될 수 있습니다. /// 각 하위 버튼은 ToolbarButtonBase 또는 그 파생 클래스의 인스턴스여야 합니다. /// public List SubButtons { get; private set; } /// /// 하위 버튼 중 하나가 선택되었을 때 호출되는 액션(콜백)입니다. /// 선택된 하위 버튼 객체가 파라미터로 전달됩니다. /// 주 버튼의 모양이 변경된 후, 추가적인 로직을 수행하고자 할 때 사용합니다. /// /// /// 이 콜백은 SelectSubButton 메서드 내에서, 주 버튼의 Text/IconSpritePath가 업데이트된 후 호출됩니다. /// public Action OnSubButtonSelected { get; set; } /// /// ToolbarExpandableButton의 새 인스턴스를 초기화합니다. /// SubButtons 리스트를 빈 리스트로 생성합니다. /// public ToolbarExpandableButton() { SubButtons = new List(); } /// /// 주 확장 버튼이 클릭되었을 때의 로직을 실행합니다. /// 기본적으로 부모 클래스(ToolbarButtonBase)의 ExecuteClick을 호출하여 /// 할당된 ClickCommand를 실행합니다. /// /// /// 하위 메뉴를 실제로 열고 닫는 동작은 View 레이어(예: ToolbarView)에서 이 버튼의 클릭 이벤트를 감지하여 /// 별도로 처리하는 것이 일반적입니다. 이 메서드는 주 버튼 자체의 커맨드 실행에 중점을 둡니다. /// /// ClickCommand에 전달할 파라미터입니다. public override void ExecuteClick(object parameter = null) { if (!IsEnabled) return; // 주 버튼 자체에 할당된 ClickCommand 실행 (예: 특정 모드 진입, 상태 변경 등) base.ExecuteClick(parameter); // 하위 메뉴를 여는 시각적 동작은 View(예: ToolbarView)에서 이 버튼의 UI 클릭 이벤트를 받아 처리합니다. // 예를 들어, ToolbarView에서는 이 ExecuteClick() 호출 후, ToggleSubMenu(this)와 같은 메서드를 호출할 수 있습니다. } /// /// 지정된 하위 버튼을 "선택된" 상태로 처리하고, 주 버튼의 모양(텍스트, 아이콘)을 /// 선택된 하위 버튼의 것으로 업데이트합니다. /// OnSubButtonSelected 콜백이 설정되어 있다면 호출합니다. /// /// 선택된 하위 버튼 객체입니다. ToolbarButtonBase 또는 그 파생 타입이어야 합니다. /// /// [주의] 이 파라미터는 View 레이어의 GameObject를 참조하며, 모델 클래스 설계 원칙에 따르면 /// 모델이 View 객체를 직접 참조하는 것은 바람직하지 않을 수 있습니다. /// 현재 코드(ToolbarExpandableButton.cs의 SelectSubButton)에서는 아이콘 즉시 로드를 위해 사용되고 있으나, /// 이상적으로는 아이콘 업데이트도 OnStateChanged 이벤트를 통해 View에서 처리하는 것이 좋습니다. /// 이 파라미터는 향후 리팩토링 대상이 될 수 있습니다. 현재는 기존 코드 구조를 유지합니다. /// 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(); } } /// /// 이 버튼 모델 및 모든 하위 버튼 모델에 연결된 이벤트 핸들러를 정리합니다. /// public override void ClearEventHandlers() { base.ClearEventHandlers(); // 부모 클래스의 이벤트 정리 (OnStateChanged) OnSubButtonSelected = null; if (SubButtons != null) { foreach (var subButton in SubButtons) { subButton?.ClearEventHandlers(); // 각 하위 버튼의 이벤트 핸들러도 정리 } } } } }