using UVC.UI.Commands; namespace UVC.UI.Toolbar.Model { /// /// 툴바의 전체적인 데이터 컨테이너 및 관리 클래스입니다. /// 툴바에 표시될 모든 항목(IToolbarItem)들의 목록을 관리하며, /// 각 항목의 생성 및 초기 설정을 위한 헬퍼 메서드들을 제공합니다. /// 이 모델은 UI 표현(View)과 로직(Controller)으로부터 분리되어 툴바의 순수한 데이터 구조를 나타냅니다. /// /// /// 역할: /// - 툴바 항목 저장: 툴바에 포함될 버튼, 구분선 등의 IToolbarItem 객체들을 리스트 형태로 보관합니다. /// - 툴바 항목 생성: AddStandardButton, AddToggleButton 등의 팩토리 메서드를 통해 특정 타입의 툴바 항목을 쉽게 생성하고 모델에 추가할 수 있도록 합니다. /// - 라디오 버튼 그룹 관리: 동일한 그룹 이름을 가진 ToolbarRadioButton들을 내부적으로 ToolbarRadioButtonGroup으로 묶어 관리합니다. /// /// 사용 흐름: /// 1. Toolbar (또는 유사한 관리 클래스)에서 ToolbarModel의 인스턴스를 생성합니다. /// 2. 다양한 AddChild[ButtonType] 메서드 또는 AddItem 메서드를 호출하여 툴바 항목 모델들을 생성하고 ToolbarModel에 추가합니다. /// 3. 설정이 완료된 ToolbarModel 객체를 ToolbarView의 Initialize 메서드에 전달하여 UI를 렌더링하도록 합니다. /// /// /// /// // Toolbar 등에서 ToolbarModel 사용 예시 /// /// // 1. ToolbarModel 인스턴스 생성 /// ToolbarModel myToolbar = new ToolbarModel(); /// /// // 2. 표준 버튼 추가 /// ICommand openCommand = new ActionCommand(() => Debug.Log("파일 열기 실행")); /// myToolbar.AddStandardButton("button_open_file", "icons/open", openCommand, "tooltip_open_file_description"); /// /// // 3. 구분선 추가 /// myToolbar.AddSeparator(); /// /// // 4. 토글 버튼 추가 /// myToolbar.AddToggleButton( /// "button_toggle_grid", /// false, // 초기 상태 (꺼짐) /// "icons/grid_on", // 켜짐 상태 아이콘 /// "icons/grid_off", // 꺼짐 상태 아이콘 /// (IsSelected) => Debug.Log($"그리드 표시: {IsSelected}"), // OnToggle 콜백 /// null, // ClickCommand (필요시 설정) /// "tooltip_toggle_grid_visibility" /// ); /// /// // 5. 라디오 버튼 그룹 추가 /// string viewModeGroup = "ViewModeSelection"; /// myToolbar.AddRadioButton(viewModeGroup, "button_2d_view", true, "icons/2d_view", null, /// (IsSelected) => { if(IsSelected) Debug.Log("2D 뷰 선택됨"); }, /// null, "tooltip_2d_view"); /// myToolbar.AddRadioButton(viewModeGroup, "button_3d_view", false, "icons/3d_view", null, /// (IsSelected) => { if(IsSelected) Debug.Log("3D 뷰 선택됨"); }, /// null, "tooltip_3d_view"); /// /// // 6. 설정된 모델을 ToolbarView에 전달 /// // toolbarView.Initialize(myToolbar); /// /// public class ToolbarModel { /// /// 툴바에 추가된 모든 항목(IToolbarItem)들의 리스트입니다. /// 이 리스트의 순서대로 툴바에 항목들이 표시됩니다. /// 외부에서는 읽기만 가능하도록 private set으로 설정되어 있으며, 항목 추가는 AddItem 또는 AddChild[ButtonType] 메서드를 통해 이루어집니다. /// public System.Collections.Generic.List Items { get; private set; } /// /// 라디오 버튼 그룹들을 관리하는 딕셔너리입니다. /// 키는 라디오 버튼의 GroupName이며, 값은 해당 그룹을 관리하는 ToolbarRadioButtonGroup 객체입니다. /// ToolbarRadioButton이 AddItem을 통해 추가될 때 내부적으로 사용됩니다. /// private System.Collections.Generic.Dictionary _radioGroups; /// /// ToolbarModel의 새 인스턴스를 초기화합니다. /// Items 리스트와 _radioGroups 딕셔너리를 빈 상태로 생성합니다. /// public ToolbarModel() { Items = new System.Collections.Generic.List(); _radioGroups = new System.Collections.Generic.Dictionary(); } /// /// 지정된 툴바 항목(IToolbarItem)을 모델에 추가합니다. /// 추가된 항목은 Items 리스트의 끝에 추가됩니다. /// 만약 추가되는 항목이 ToolbarRadioButton이라면, 해당 버튼을 적절한 ToolbarRadioButtonGroup에 등록하거나 새로 생성하여 관리합니다. /// /// 툴바에 추가할 IToolbarItem 객체입니다 (예: ToolbarStandardButton, ToolbarSeparator 등). public void AddItem(IToolbarItem item) { Items.Add(item); if (item is ToolbarRadioButton radioButton) { if (!_radioGroups.TryGetValue(radioButton.GroupName, out var group)) { group = new ToolbarRadioButtonGroup(); _radioGroups.Add(radioButton.GroupName, group); } group.RegisterButton(radioButton); radioButton.RadioGroup = group; // 버튼 모델에 그룹 참조 설정 // 그룹 내 초기 선택 상태 결정 (만약 이 버튼이 initialState=true이고, 그룹에 아직 선택된 버튼이 없다면) // 또는 모든 버튼이 추가된 후 그룹별로 InitializeSelection()을 호출할 수도 있습니다. // 현재는 AddRadioButton 메서드에서 initialState에 따른 SetSelected 호출로 처리하고 있습니다. } // UI 갱신 로직은 ToolbarModel 자체에서는 직접 호출하지 않습니다. // View가 Model의 변경 사항을 구독하거나, Controller가 View의 업데이트를 트리거합니다. // 혹은, Model에 아이템이 추가/제거될 때 이벤트를 발생시켜 View가 감지하도록 할 수 있습니다. } /// /// 새로운 ToolbarStandardButton을 생성하여 툴바 모델에 추가합니다. /// /// 버튼에 표시될 텍스트 또는 다국어 키입니다. /// 버튼 아이콘의 Resources 경로입니다 (선택 사항). /// 버튼 클릭 시 실행될 ICommand 객체입니다 (선택 사항). /// 버튼 툴팁의 텍스트 또는 다국어 키입니다 (선택 사항). /// 생성되고 추가된 ToolbarStandardButton 객체입니다. public ToolbarStandardButton AddStandardButton(string text, string iconSpritePath = null, ICommand command = null, string tooltip = null) { var button = new ToolbarStandardButton { Text = text, IconSpritePath = iconSpritePath, ClickCommand = command, Tooltip = tooltip }; AddItem(button); return button; } /// /// 새로운 ToolbarToggleButton을 생성하여 툴바 모델에 추가합니다. /// /// 버튼 텍스트 또는 다국어 키입니다. /// 버튼의 초기 선택 상태입니다 (기본값: false, 해제됨). /// 버튼이 선택(On) 상태일 때의 아이콘 경로입니다 (선택 사항). /// 버튼이 해제(Off) 상태일 때의 아이콘 경로입니다 (선택 사항). /// 버튼의 선택 상태가 변경될 때 호출될 콜백 함수입니다 (선택 사항). /// 버튼 클릭 시 실행될 ICommand 객체입니다 (선택 사항). /// 버튼 툴팁의 텍스트 또는 다국어 키입니다 (선택 사항). /// 생성되고 추가된 ToolbarToggleButton 객체입니다. public ToolbarToggleButton AddToggleButton(string text, bool initialState = false, string onIconSpritePath = null, string offIconSpritePath = null, System.Action onToggle = null, ICommand command = null, string tooltip = null) { // ToolbarToggleButton의 IconSpritePath는 '선택된 상태'의 아이콘을 의미합니다. var button = new ToolbarToggleButton { Text = text, IsSelected = initialState, IconSpritePath = onIconSpritePath, // 선택 시 아이콘 OffIconSpritePath = offIconSpritePath, // 해제 시 아이콘 OnToggle = onToggle, ClickCommand = command, Tooltip = tooltip }; AddItem(button); return button; } /// /// 새로운 ToolbarRadioButton을 생성하여 툴바 모델에 추가합니다. /// 동일한 groupName을 가진 라디오 버튼들은 하나의 그룹으로 동작합니다. /// /// 이 라디오 버튼이 속할 그룹의 이름입니다. /// 버튼 텍스트 또는 다국어 키입니다. /// 버튼의 초기 선택 상태입니다. 같은 그룹 내에서 하나의 버튼만 true로 설정해야 합니다. /// 버튼이 선택(On) 상태일 때의 아이콘 경로입니다 (선택 사항). /// 버튼이 해제(Off) 상태일 때의 아이콘 경로입니다 (선택 사항). /// 버튼의 선택 상태가 변경될 때 호출될 콜백 함수입니다 (선택 사항). /// 버튼 클릭(선택) 시 실행될 ICommand 객체입니다 (선택 사항). /// 버튼 툴팁의 텍스트 또는 다국어 키입니다 (선택 사항). /// 생성되고 추가된 ToolbarRadioButton 객체입니다. public ToolbarRadioButton AddRadioButton(string groupName, string text, bool initialState = false, string iconSpritePath = null, string offIconSpritePath = null, System.Action onToggle = null, ICommand command = null, string tooltip = null) { var button = new ToolbarRadioButton(groupName) { Text = text, IsSelected = initialState,// 초기 IsSelected 상태 설정 IconSpritePath = iconSpritePath,// 선택 시 아이콘 OffIconSpritePath = offIconSpritePath,// 해제 시 아이콘 OnToggle = onToggle, ClickCommand = command, Tooltip = tooltip }; // AddItem 내에서 그룹 등록 및 버튼에 그룹 참조 설정이 이루어집니다. AddItem(button); // initialState가 true인 경우, 해당 그룹 내에서 이 버튼을 명시적으로 선택된 상태로 만듭니다. // 이렇게 하면 여러 라디오 버튼 중 initialState가 true인 마지막 버튼이 최종 선택됩니다. // 또는, 그룹별로 모든 버튼이 추가된 후 한 번만 선택 로직을 실행하는 것도 방법입니다. if (initialState && _radioGroups.TryGetValue(groupName, out var group)) { group.SetSelected(button); } return button; } /// /// 새로운 ToolbarExpandableButton(확장 가능한 버튼)을 생성하여 툴바 모델에 추가합니다. /// /// 주 버튼에 표시될 텍스트 또는 다국어 키입니다. /// 주 버튼의 기본 아이콘 경로입니다 (선택 사항). /// 주 버튼 클릭 시 실행될 ICommand 객체입니다 (선택 사항, 하위 메뉴 토글과는 별개일 수 있음). /// 주 버튼 툴팁의 텍스트 또는 다국어 키입니다 (선택 사항). /// 서브 버튼 선택 시 주 버튼의 아이콘을 변경할지 여부입니다 (기본값: true). /// 생성되고 추가된 ToolbarExpandableButton 객체입니다. public ToolbarExpandableButton AddExpandableButton(string text, string iconSpritePath = null, ICommand command = null, string tooltip = null, bool updateIconOnSelection = true) { var button = new ToolbarExpandableButton { Text = text, IconSpritePath = iconSpritePath, ClickCommand = command, Tooltip = tooltip, UpdateIconOnSelection = updateIconOnSelection }; button.SetOriginalText(text); // 원본 텍스트 저장 (OnSubButtonSelectionChanged 이벤트에서 사용) AddItem(button); return button; } // /// 새로운 ToolbarSeparator(구분선)를 생성하여 툴바 모델에 추가합니다. /// public void AddSeparator() { AddItem(new ToolbarSeparator()); } /// /// 지정된 이름의 라디오 버튼 그룹을 가져옵니다. /// /// 라디오 버튼 그룹의 이름입니다. /// 해당 이름의 ToolbarRadioButtonGroup 객체입니다. 존재하지 않으면 null을 반환합니다. public ToolbarRadioButtonGroup GetRadioButtonGroup(string groupName) { if (_radioGroups.TryGetValue(groupName, out var group)) { return group; } return null; } /// /// 지정된 그룹 내에서 특정 라디오 버튼을 선택하거나, 모든 선택을 해제합니다. /// /// 라디오 버튼 그룹의 이름입니다. /// 선택할 라디오 버튼입니다. null을 전달하면 그룹 내 모든 버튼의 선택이 해제됩니다. /// true이면 상태 변경 이벤트(OnToggle, OnStateChanged 등)를 발생시키고, false이면 이벤트 없이 상태만 변경합니다. 기본값은 true입니다. /// 작업 성공 여부입니다. 그룹이 존재하지 않으면 false를 반환합니다. public bool SetRadioButtonSelection(string groupName, ToolbarRadioButton buttonToSelect, bool raiseEvent = true) { if (!_radioGroups.TryGetValue(groupName, out var group)) { UnityEngine.Debug.LogWarning($"SetRadioButtonSelection: 그룹 '{groupName}'을 찾을 수 없습니다."); return false; } if (buttonToSelect == null) { group.ClearSelection(raiseEvent); } else { group.SetSelected(buttonToSelect, raiseEvent); } return true; } /// /// 그룹 이름으로 라디오 버튼 그룹 내 모든 선택을 해제합니다. /// /// 라디오 버튼 그룹의 이름입니다. /// true이면 상태 변경 이벤트(OnToggle, OnStateChanged 등)를 발생시키고, false이면 이벤트 없이 상태만 변경합니다. 기본값은 true입니다. /// 작업 성공 여부입니다. 그룹이 존재하지 않으면 false를 반환합니다. public bool ClearRadioButtonSelection(string groupName, bool raiseEvent = true) { return SetRadioButtonSelection(groupName, null, raiseEvent); } /// /// 지정된 토글 버튼의 선택 상태를 설정합니다. /// /// 상태를 변경할 토글 버튼입니다. /// 설정할 선택 상태입니다. /// true이면 상태 변경 이벤트(OnToggle, OnStateChanged 등)를 발생시키고, false이면 이벤트 없이 상태만 변경합니다. 기본값은 true입니다. public void SetToggleButtonState(ToolbarToggleButton toggleButton, bool isSelected, bool raiseEvent = true) { if (toggleButton == null) { UnityEngine.Debug.LogWarning("SetToggleButtonState: toggleButton이 null입니다."); return; } toggleButton.SetSelected(isSelected, raiseEvent); } /// /// 텍스트로 라디오 버튼을 찾아 선택 상태를 설정합니다. /// /// 라디오 버튼 그룹의 이름입니다. /// 선택할 라디오 버튼의 텍스트입니다. null 또는 빈 문자열을 전달하면 그룹 내 모든 버튼의 선택이 해제됩니다. /// true이면 상태 변경 이벤트를 발생시키고, false이면 이벤트 없이 상태만 변경합니다. 기본값은 true입니다. /// 찾아서 선택한 라디오 버튼입니다. 그룹이 없거나 버튼을 찾지 못하면 null을 반환합니다. public ToolbarRadioButton SetRadioButtonSelectionByText(string groupName, string buttonText, bool raiseEvent = true) { if (!_radioGroups.TryGetValue(groupName, out var group)) { UnityEngine.Debug.LogWarning($"SetRadioButtonSelectionByText: 그룹 '{groupName}'을 찾을 수 없습니다."); return null; } if (string.IsNullOrEmpty(buttonText)) { group.ClearSelection(raiseEvent); return null; } var button = group.FindButtonByText(buttonText); if (button == null) { UnityEngine.Debug.LogWarning($"SetRadioButtonSelectionByText: 그룹 '{groupName}'에서 텍스트 '{buttonText}'인 버튼을 찾을 수 없습니다."); return null; } group.SetSelected(button, raiseEvent); return button; } /// /// 텍스트로 토글 버튼을 찾아 선택 상태를 설정합니다. /// /// 상태를 변경할 토글 버튼의 텍스트입니다. /// 설정할 선택 상태입니다. /// true이면 상태 변경 이벤트를 발생시키고, false이면 이벤트 없이 상태만 변경합니다. 기본값은 true입니다. /// 찾아서 상태를 변경한 토글 버튼입니다. 찾지 못하면 null을 반환합니다. public ToolbarToggleButton SetToggleButtonStateByText(string buttonText, bool isSelected, bool raiseEvent = true) { foreach (var item in Items) { if (item is ToolbarToggleButton toggleButton && toggleButton.Text == buttonText) { toggleButton.SetSelected(isSelected, raiseEvent); return toggleButton; } } UnityEngine.Debug.LogWarning($"SetToggleButtonStateByText: 텍스트 '{buttonText}'인 토글 버튼을 찾을 수 없습니다."); return null; } /// /// 텍스트로 토글 버튼을 찾아 반환합니다. /// /// 찾을 토글 버튼의 텍스트입니다. /// 찾은 토글 버튼입니다. 찾지 못하면 null을 반환합니다. public ToolbarToggleButton? GetToggleButtonByText(string buttonText) { foreach (var item in Items) { if (item is ToolbarToggleButton toggleButton && toggleButton.Text == buttonText) { return toggleButton; } } return null; } // 만약 모든 라디오 버튼 그룹의 초기 선택 상태를 한 번에 설정하고 싶다면 다음과 같은 메서드를 추가할 수 있습니다. // /// // /// 모든 라디오 버튼 그룹에 대해 초기 선택 상태를 설정합니다. // /// 각 그룹에서 IsSelected가 true로 설정된 버튼이 없다면 첫 번째 버튼을 선택합니다. // /// // public void FinalizeRadioGroupSelections() // { // foreach (var group in _radioGroups.Values) // { // group.InitializeSelection(); // } // } // 실제 UI 렌더링 및 상호작용 로직은 이 클래스 또는 별도의 UI View 클래스에서 처리됩니다. // (예: Unity UI GameObject 생성, 이벤트 연결 등) } }