#nullable enable using UnityEngine; using UVC.Locale; using UVC.UI.Commands; using UVC.UI.Toolbar.Model; using UVC.UI.Toolbar.View; using System; namespace UVC.UI.Toolbar { /// /// 툴바 액션 타입을 나타내는 열거형입니다. /// public enum ToolbarActionType { /// /// 일반 버튼 클릭 /// Standard, /// /// 라디오 버튼 선택 변경 /// Radio, /// /// 토글 버튼 상태 변경 /// Toggle, /// /// 확장 버튼의 하위 버튼 선택 변경 /// Expandable } /// /// 툴바 액션 정보를 담는 클래스입니다. /// public class ToolbarActionEventArgs { /// /// 액션을 발생시킨 툴바 아이템의 텍스트입니다. /// public string Text { get; set; } /// /// 액션에 따른 값입니다. /// Standard: null /// Radio: 선택된 항목의 텍스트 (전부 해제 시 null) /// Toggle: true/false /// Expandable: 선택된 하위 버튼의 텍스트 /// public object? Value { get; set; } /// /// 액션 타입입니다. /// public ToolbarActionType ActionType { get; set; } } public class Toolbar : MonoBehaviour { protected ToolbarModel model; protected ToolbarView view; /// /// 툴바의 버튼이 클릭되거나 상태가 변경될 때 발생하는 이벤트입니다. /// raiseEvent가 true일 때만 호출됩니다. /// public event Action? OnAction; /// /// OnAction 이벤트를 발생시킵니다. /// /// 액션을 발생시킨 툴바 아이템의 텍스트 /// 액션 타입 /// 액션에 따른 값 (Standard: null, Radio: 선택된 항목 텍스트 또는 null, Toggle: bool, Expandable: 선택된 하위 버튼 텍스트) protected void RaiseOnAction(string text, ToolbarActionType actionType, object? value = null) { OnAction?.Invoke(new ToolbarActionEventArgs { Text = text, ActionType = actionType, Value = value }); } protected void Awake() { // 1. 이 GameObject에 연결된 ToolbarView 컴포넌트를 찾습니다. view = GetComponent(); // 2. 만약 현재 GameObject에 없다면, 자식 GameObject들 중에서 ToolbarView 컴포넌트를 찾습니다. if (view == null) { view = GetComponentInChildren(); } } /// /// 툴바 모델을 설정합니다. /// /// public void SetData(ToolbarModel toolbarModel) { if (view == null) { Debug.LogError("ToolbarView가 할당되지 않았습니다."); return; } model = toolbarModel; } /// /// 툴바를 초기화합니다. /// public void Initialize() { if (view == null) { Debug.LogError("ToolbarView가 할당되지 않았습니다."); return; } if (model == null) { Debug.LogError("ToolbarModel이 할당되지 않았습니다."); return; } // 모든 버튼 타입의 이벤트 구독 SubscribeButtonEvents(); // ToolbarView 초기화 및 렌더링 view.Initialize(model); } /// /// 모델 내의 모든 버튼에 대해 이벤트를 구독합니다. /// private void SubscribeButtonEvents() { if (model?.Items == null) return; foreach (var item in model.Items) { switch (item) { case ToolbarRadioButton radioButton: // 라디오 버튼의 선택 상태 변경 이벤트 구독 radioButton.OnToggleStateChanged += (isSelected) => { if (isSelected) { RaiseOnAction(radioButton.GroupName, ToolbarActionType.Radio, radioButton.Text); } }; break; case ToolbarToggleButton toggleButton: // 토글 버튼의 상태 변경 이벤트 구독 toggleButton.OnToggleStateChanged += (isSelected) => { RaiseOnAction(toggleButton.Text, ToolbarActionType.Toggle, isSelected); }; break; case ToolbarExpandableButton expandableButton: // 확장 버튼의 하위 버튼 선택 변경 이벤트 구독 expandableButton.OnSubButtonSelectionChanged += (expandableText, selectedSubButtonText) => { RaiseOnAction(expandableText, ToolbarActionType.Expandable, selectedSubButtonText); }; break; case ToolbarStandardButton standardButton: // 표준 버튼의 클릭 이벤트 구독 standardButton.OnClicked += () => { RaiseOnAction(standardButton.Text, ToolbarActionType.Standard, null); }; break; } } } private ToolbarModel generateModel() { var toolbarModel = new ToolbarModel(); // --- 툴바 모델 구성 --- // 컴포넌트 목록 toolbarModel.AddStandardButton("컴포넌트 목록", "Prefabs/UI/Toolbar/images/ic_menu_elements", new ActionCommand(() => { // if (!ComponentListWindow.Instance.gameObject.activeSelf) ComponentListWindow.Instance.Show(); // var infos = ComponentList.GenerateData(); // ComponentListWindow.Instance.SetupData(infos); }), "컴포넌트 목록 창을 엽니다."); // playback toolbarModel.AddStandardButton("Playback", "Prefabs/UI/Toolbar/images/ic_menu_playback", new ActionCommand(() => Debug.Log("Playback 버튼 클릭됨")), "Playback을 실행 시킵니다."); // 화면 캡처 toolbarModel.AddStandardButton("button_capture_screen", "Prefabs/UI/Toolbar/images/ic_menu_capture", new ActionCommand(() => Debug.Log("화면 캡처 버튼 클릭됨")), "tooltip_capture_screen"); // 화면 녹화 시작/중지 (ToggleButton) toolbarModel.AddToggleButton("button_record_screen", false, "Prefabs/UI/Toolbar/images/ic_menu_camera_on", "Prefabs/UI/Toolbar/images/ic_menu_camera_off", (isSelected) => Debug.Log($"화면 녹화 상태: {(isSelected ? "녹화 중" : "중지")} (OnToggle 콜백)"), new ActionCommand((isRecording) => Debug.Log($"화면 녹화 Command 실행: {(isRecording ? "녹화 시작" : "녹화 중지")}")), "tooltip_record_screen"); // 화면 확대 toolbarModel.AddStandardButton("화면 확대", "Prefabs/UI/Toolbar/images/ic_menu_zoom_in", new ActionCommand(() => Debug.Log("화면 확대 버튼 클릭됨")), "화면을 한 단계 확대 합니다."); //화면 축소 toolbarModel.AddStandardButton("화면 축소", "Prefabs/UI/Toolbar/images/ic_menu_zoom_out", new ActionCommand(() => Debug.Log("화면 축소 버튼 클릭됨")), "화면을 한 단계 축소 합니다."); // 구분선 toolbarModel.AddSeparator(); // RadioButtonGroup 샘플 toolbarModel.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 시점으로 변경합니다."); toolbarModel.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 시점으로 변경합니다."); toolbarModel.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 시점으로 변경합니다."); // 구분선 toolbarModel.AddSeparator(); // 기존 확장 버튼 (예시로 남겨두거나 필요에 따라 수정/제거) var expandableBtnModel = toolbarModel.AddExpandableButton("button_brush_size", "Prefabs/UI/Toolbar/images/ic_brush_default_white", new ActionCommand(() => Debug.Log("브러시 크기 주 버튼 클릭됨 (Command)")), "붓 사이즈 선택 합니다."); var smallBrushCmd = new ActionCommand(() => Debug.Log($"작은 브러시 선택됨")); var smallBrush = new ToolbarStandardButton { Text = "brush_size_small", IconSpritePath = "Prefabs/UI/Toolbar/images/ic_brush_small_white", Tooltip = "tooltip_brush_small", ClickCommand = smallBrushCmd }; expandableBtnModel.SubButtons.Add(smallBrush); var mediumBrush = new ToolbarStandardButton { Text = "brush_size_medium", IconSpritePath = "Prefabs/UI/Toolbar/images/ic_brush_medium_white", Tooltip = "tooltip_brush_medium", ClickCommand = new ActionCommand(() => Debug.Log("중간 브러시 선택됨 (Sub-Command 실행)")) }; expandableBtnModel.SubButtons.Add(mediumBrush); expandableBtnModel.OnSubButtonSelected = (selectedSubButtonModel) => { string localizedSubButtonText = LocalizationManager.Instance != null ? LocalizationManager.Instance.GetString(selectedSubButtonModel.Text) : selectedSubButtonModel.Text; Debug.Log($"브러시 크기 '{localizedSubButtonText}' 선택됨 (OnSubButtonSelected 콜백). 주 버튼 업데이트 로직 실행 가능."); }; // --- 툴바 모델 구성 끝 --- return toolbarModel; } /// /// 지정된 그룹 내에서 특정 라디오 버튼을 선택하거나, 모든 선택을 해제합니다. /// /// 라디오 버튼 그룹의 이름입니다. /// 선택할 라디오 버튼입니다. null을 전달하면 그룹 내 모든 버튼의 선택이 해제됩니다. /// true이면 OnAction 이벤트를 발생시키고, false이면 UI만 업데이트합니다. 기본값은 true입니다. /// 작업 성공 여부입니다. 모델이 없거나 그룹이 존재하지 않으면 false를 반환합니다. public bool SetRadioButtonSelection(string groupName, ToolbarRadioButton buttonToSelect, bool raiseEvent = false) { if (model == null) { Debug.LogError("Toolbar: ToolbarModel이 설정되지 않았습니다."); return false; } bool result = model.SetRadioButtonSelection(groupName, buttonToSelect, raiseEvent); if (result) { // Icon의 ImageColorChangeBehaviour 색상 상태 업데이트 if (view != null) { view.UpdateRadioGroupIconColors(groupName, buttonToSelect); } if (raiseEvent) { RaiseOnAction(groupName, ToolbarActionType.Radio, buttonToSelect?.Text); } } return result; } /// /// 그룹 이름으로 라디오 버튼 그룹 내 모든 선택을 해제합니다. /// /// 라디오 버튼 그룹의 이름입니다. /// true이면 OnAction 이벤트를 발생시키고, false이면 UI만 업데이트합니다. 기본값은 true입니다. /// 작업 성공 여부입니다. 모델이 없거나 그룹이 존재하지 않으면 false를 반환합니다. public bool ClearRadioButtonSelection(string groupName, bool raiseEvent = false) { if (model == null) { Debug.LogError("Toolbar: ToolbarModel이 설정되지 않았습니다."); return false; } bool result = model.ClearRadioButtonSelection(groupName, raiseEvent); if (result) { // Icon의 ImageColorChangeBehaviour 색상 상태 업데이트 (모두 비선택) if (view != null) { view.UpdateRadioGroupIconColors(groupName, null); } if (raiseEvent) { RaiseOnAction(groupName, ToolbarActionType.Radio, null); } } return result; } /// /// 지정된 토글 버튼의 선택 상태를 설정합니다. /// /// 상태를 변경할 토글 버튼입니다. /// 설정할 선택 상태입니다. /// true이면 OnAction 이벤트를 발생시키고, false이면 UI만 업데이트합니다. 기본값은 true입니다. public void SetToggleButtonState(ToolbarToggleButton toggleButton, bool isSelected, bool raiseEvent = false) { if (model == null) { Debug.LogError("Toolbar: ToolbarModel이 설정되지 않았습니다."); return; } model.SetToggleButtonState(toggleButton, isSelected, raiseEvent); // Icon의 ImageColorChangeBehaviour 색상 상태 업데이트 if (view != null) { view.UpdateIconColorState(toggleButton, isSelected); } if (raiseEvent) { RaiseOnAction(toggleButton.Text, ToolbarActionType.Toggle, isSelected); } } /// /// 텍스트로 라디오 버튼을 찾아 선택 상태를 설정합니다. /// /// 라디오 버튼 그룹의 이름입니다. /// 선택할 라디오 버튼의 텍스트입니다. null 또는 빈 문자열을 전달하면 그룹 내 모든 버튼의 선택이 해제됩니다. /// true이면 OnAction 이벤트를 발생시키고, false이면 UI만 업데이트합니다. 기본값은 true입니다. /// 작업 성공 여부입니다. 모델이 없거나 버튼을 찾지 못하면 false를 반환합니다. public bool SetRadioButtonSelection(string groupName, string buttonText, bool raiseEvent = false) { if (model == null) { Debug.LogError("Toolbar: ToolbarModel이 설정되지 않았습니다."); return false; } var button = model.SetRadioButtonSelectionByText(groupName, buttonText, raiseEvent); bool result = button != null || string.IsNullOrEmpty(buttonText); if (result) { // Icon의 ImageColorChangeBehaviour 색상 상태 업데이트 if (view != null) { view.UpdateRadioGroupIconColors(groupName, button); } if (raiseEvent) { RaiseOnAction(groupName, ToolbarActionType.Radio, button?.Text); } } return result; } /// /// 텍스트로 토글 버튼을 찾아 선택 상태를 설정합니다. /// /// 상태를 변경할 토글 버튼의 텍스트입니다. /// 설정할 선택 상태입니다. /// true이면 OnAction 이벤트를 발생시키고, false이면 UI만 업데이트합니다. 기본값은 true입니다. /// 작업 성공 여부입니다. 모델이 없거나 버튼을 찾지 못하면 false를 반환합니다. public bool SetToggleButtonState(string buttonText, bool isSelected, bool raiseEvent = false) { if (model == null) { Debug.LogError("Toolbar: ToolbarModel이 설정되지 않았습니다."); return false; } var button = model.SetToggleButtonStateByText(buttonText, isSelected, raiseEvent); if (button != null) { // Icon의 ImageColorChangeBehaviour 색상 상태 업데이트 if (view != null) { view.UpdateIconColorState(button, isSelected); } if (raiseEvent) { RaiseOnAction(buttonText, ToolbarActionType.Toggle, isSelected); } } return button != null; } protected void OnDestroy() { model = null; view = null; } // System.Collections.IEnumerator TestModelChange(ToolbarStandardButton standard, ToolbarToggleButton toggle) // { // yield return new WaitForSeconds(2f); // Debug.Log("모델 변경 테스트: 저장 버튼 비활성화 및 텍스트 변경"); // standard.Text = "저장됨"; // standard.IsEnabled = false; // // yield return new WaitForSeconds(2f); // Debug.Log("모델 변경 테스트: 음소거 토글 상태 변경"); // toggle.IsSelected = true; // } } }