using UnityEngine; using UnityEngine.UI; using UVC.UI.Toolbar.Model; namespace UVC.UI.Toolbar.View { /// /// 모델의 UI 표현을 생성하고 관리하는 클래스입니다. /// 이 클래스는 인터페이스를 구현하여, /// 토글 버튼의 UI GameObject 생성, 이벤트 바인딩, 시각적 업데이트 로직을 담당합니다. /// /// /// 주요 역할: /// - 을 사용하여 토글 버튼 UI GameObject를 인스턴스화합니다. /// - 생성된 UI의 컴포넌트와 모델 간의 상호작용을 설정합니다. /// (예: UI 토글 시 모델의 호출) /// - 모델의 상태 변경(, )에 따라 /// UI의 시각적 요소(텍스트, 아이콘, 토글 상태 등)를 업데이트합니다. /// /// /// /// // ToolbarView 내에서 이 프로세서가 사용되는 방식 (간략화된 예시): /// // ToolbarView view = GetComponent(); /// // 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에 반영됨 /// /// public class ToolbarToggleButtonViewProcessor : IButtonViewProcessor { /// /// 모델에 대한 UI GameObject를 생성합니다. /// 을 사용하여 인스턴스화합니다. /// /// UI를 생성할 대상인 타입의 모델 객체입니다. 내부적으로 으로 캐스팅하여 사용합니다. /// 생성된 UI GameObject가 자식으로 추가될 부모 입니다. /// 현재 의 컨텍스트입니다. 프리팹 참조 등에 사용됩니다. /// 생성된 UI GameObject입니다. 이 할당되지 않은 경우 null을 반환할 수 있습니다. 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); } /// /// 생성된 토글 버튼 UI GameObject에 초기 시각적 요소를 설정하고, 사용자 상호작용 및 모델 상태 변경에 따른 UI 업데이트 로직을 바인딩합니다. /// /// 설정할 버튼의 데이터 모델 ( 타입이어야 함). /// 모델에 해당하는, 화면에 표시될 UI GameObject. /// 현재 의 컨텍스트. /// /// 이 메서드는 다음을 수행합니다: /// 1. 으로 캐스팅합니다. /// 2. 에서 컴포넌트를 찾습니다. /// 3. 컴포넌트의 초기 `isOn` 상태를 모델의 값으로 설정합니다. (UI 이벤트 발생 없이) /// 4. 컴포넌트의 `OnValueChanged` 이벤트에 리스너를 추가하여, UI에서 토글 상태가 변경되면 모델의 메서드를 호출합니다. /// 이를 통해 모델의 상태가 업데이트되고, 연결된 커맨드가 실행될 수 있습니다. /// 5. 를 호출하여 텍스트, 아이콘 등 공통 시각 요소를 초기화합니다. /// 6. 를 호출하여 토글 상태에 따른 시각적 표현(예: 아이콘 변경)을 초기화합니다. /// public void SetupButtonInteractions(ToolbarButtonBase buttonModel, GameObject buttonUIObject, ToolbarView viewContext) { ToolbarToggleButton toggleModel = buttonModel as ToolbarToggleButton; if (toggleModel == null) return; Toggle toggleComponent = buttonUIObject.GetComponent(); 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); } /// /// 버튼 모델의 공통적인 상태(텍스트, 아이콘, 활성화 상태 등)가 변경되었을 때, /// 해당 버튼 UI의 시각적 요소를 업데이트합니다. /// 이 메서드는 를 호출하여 실제 업데이트 로직을 수행합니다. /// /// 상태가 변경된 버튼의 모델입니다. /// 업데이트할 UI GameObject입니다. /// 현재 의 컨텍스트입니다. public void UpdateCommonButtonVisuals(ToolbarButtonBase buttonModel, GameObject buttonUIObject, ToolbarView viewContext) { // ToolbarView에 있는 공통 업데이트 로직을 사용합니다. // 이 메서드는 텍스트, 기본 아이콘(토글 상태에 따라 다를 수 있음), 활성화 상태(interactable) 등을 업데이트합니다. viewContext.InternalUpdateCommonButtonVisuals(buttonModel, buttonUIObject); } /// /// 의 선택 상태()가 모델에서 변경되었을 때 호출됩니다. /// 해당 UI 컴포넌트의 `isOn` 상태를 모델의 값과 동기화하고, /// 선택 상태에 따른 아이콘 변경 등을 위해 를 호출합니다. /// /// IsSelected 상태가 변경된 토글 버튼의 모델입니다. /// 업데이트할 UI GameObject입니다. /// 모델의 새로운 값입니다. /// 현재 의 컨텍스트입니다. /// /// 이 메서드는 주로 모델의 이벤트에 의해 호출됩니다. /// UI의 컴포넌트 상태를 모델과 동기화하고, /// 선택/해제 상태에 따라 다른 아이콘을 사용한다면 가 이를 처리합니다. /// public void UpdateToggleStateVisuals(ToolbarToggleButton toggleButtonModel, GameObject buttonUIObject, bool isSelected, ToolbarView viewContext) { Toggle toggleComponent = buttonUIObject.GetComponent(); 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); } } }