diff --git a/Assets/_DDD/_Addressables/Prefabs/Uis/GameUi/PopupUis/RestaurantManagementUi.prefab b/Assets/_DDD/_Addressables/Prefabs/Uis/GameUi/PopupUis/RestaurantManagementUi.prefab
index 1a346f07d..1cb4e065e 100644
--- a/Assets/_DDD/_Addressables/Prefabs/Uis/GameUi/PopupUis/RestaurantManagementUi.prefab
+++ b/Assets/_DDD/_Addressables/Prefabs/Uis/GameUi/PopupUis/RestaurantManagementUi.prefab
@@ -6311,7 +6311,7 @@ PrefabInstance:
m_AddedComponents:
- targetCorrespondingSourceObject: {fileID: 6952779389930089995, guid: 4f2bf029cb06b084ba41defc8fc76731, type: 3}
insertIndex: -1
- addedObject: {fileID: 2438716745211137680}
+ addedObject: {fileID: 7197937761328488698}
m_SourcePrefab: {fileID: 100100000, guid: 4f2bf029cb06b084ba41defc8fc76731, type: 3}
--- !u!224 &4720669467062659157 stripped
RectTransform:
@@ -6323,7 +6323,7 @@ GameObject:
m_CorrespondingSourceObject: {fileID: 6952779389930089995, guid: 4f2bf029cb06b084ba41defc8fc76731, type: 3}
m_PrefabInstance: {fileID: 4463400116329503023}
m_PrefabAsset: {fileID: 0}
---- !u!114 &2438716745211137680
+--- !u!114 &7197937761328488698
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
@@ -6332,10 +6332,10 @@ MonoBehaviour:
m_GameObject: {fileID: 6740783381500491556}
m_Enabled: 1
m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 46c8396c996c804449b383960b44e812, type: 3}
+ m_Script: {fileID: 11500000, guid: 8a2e0954aa144633aad86e53dc80a46a, type: 3}
m_Name:
m_EditorClassIdentifier:
- _enableBlockImage: 1
+ _enableBlockImage: 0
_uiActionsInputBinding: {fileID: 11400000, guid: 8073fcaf56fc7c34e996d0d47044f146, type: 2}
_checklistView: {fileID: 7075966153492927588}
_inventoryView: {fileID: 3570087040626823091}
@@ -6346,7 +6346,6 @@ MonoBehaviour:
_menuCategoryTabs: {fileID: 6805049896193344908}
_cookwareCategoryTabs: {fileID: 6628923975427483430}
_completeBatchFilledImage: {fileID: 2965326806322860544}
- _holdCompleteTime: 0.5
--- !u!1001 &4530765275021007961
PrefabInstance:
m_ObjectHideFlags: 0
diff --git a/Assets/_DDD/_Scripts/GameUi/New/NewRestaurantManagementUi.cs b/Assets/_DDD/_Scripts/GameUi/New/NewRestaurantManagementUi.cs
new file mode 100644
index 000000000..6f7404022
--- /dev/null
+++ b/Assets/_DDD/_Scripts/GameUi/New/NewRestaurantManagementUi.cs
@@ -0,0 +1,275 @@
+using UnityEngine;
+using UnityEngine.UI;
+using UnityEngine.EventSystems;
+using UnityEngine.InputSystem;
+using DDD.MVVM;
+
+namespace DDD
+{
+ ///
+ /// MVVM 패턴을 적용한 새로운 레스토랑 관리 UI
+ /// 기존 RestaurantManagementUi의 기능을 ViewModel과 분리하여 구현
+ ///
+ public class NewRestaurantManagementUi : IntegratedBasePopupUi
+ {
+ [Header("Sub Views")]
+ [SerializeField] private ChecklistView _checklistView;
+ [SerializeField] private InventoryView _inventoryView;
+ [SerializeField] private ItemDetailView _itemDetailView;
+ [SerializeField] private TodayMenuView _todayMenuView;
+ [SerializeField] private TodayRestaurantStateView _todayRestaurantStateView;
+
+ [Header("Tab Groups")]
+ [SerializeField] private TabGroupUi _sectionTabs;
+ [SerializeField] private TabGroupUi _menuCategoryTabs;
+ [SerializeField] private TabGroupUi _cookwareCategoryTabs;
+
+ [Header("Hold Progress UI")]
+ [SerializeField, BindTo(nameof(RestaurantManagementViewModel.NormalizedHoldProgress))]
+ private Image _completeBatchFilledImage;
+
+ protected override void Awake()
+ {
+ base.Awake();
+
+ SetupViewModelEvents();
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ if (_viewModel != null && _viewModel.IsHolding)
+ {
+ _viewModel.UpdateHoldProgress();
+ }
+ }
+
+ public override void Open(OpenPopupUiEvent evt)
+ {
+ base.Open(evt);
+
+ InitializeViews();
+ SetupTabs();
+ }
+
+ protected override GameObject GetInitialSelected()
+ {
+ // ViewModel의 현재 상태에 따라 초기 선택 UI 결정
+ var inventoryInitialSelected = _inventoryView.GetInitialSelected();
+ if (inventoryInitialSelected) return inventoryInitialSelected;
+
+ var menuCategoryButton = _menuCategoryTabs.GetFirstInteractableButton();
+ if (menuCategoryButton != null && menuCategoryButton.activeInHierarchy)
+ return menuCategoryButton;
+
+ var cookwareCategoryButton = _cookwareCategoryTabs.GetFirstInteractableButton();
+ if (cookwareCategoryButton != null && cookwareCategoryButton.activeInHierarchy)
+ return cookwareCategoryButton;
+
+ return null;
+ }
+
+ protected override void SetupBindings()
+ {
+ // Attribute 기반 자동 바인딩이 처리됨
+ // 추가적인 수동 바인딩이 필요한 경우 여기에 구현
+ }
+
+ protected override void HandleCustomPropertyChanged(string propertyName)
+ {
+ switch (propertyName)
+ {
+ case nameof(RestaurantManagementViewModel.CurrentSection):
+ UpdateSectionTabs();
+ break;
+ case nameof(RestaurantManagementViewModel.CurrentCategory):
+ UpdateCategoryTabs();
+ break;
+ }
+ }
+
+ private void SetupViewModelEvents()
+ {
+ if (!_viewModel) return;
+
+ _viewModel.OnBatchCompleted = HandleBatchCompleted;
+ _viewModel.OnChecklistFailed = HandleChecklistFailed;
+ _viewModel.OnMenuSectionSelected = HandleMenuSectionSelected;
+ _viewModel.OnCookwareSectionSelected = HandleCookwareSectionSelected;
+ _viewModel.OnCategoryChanged = HandleCategoryChanged;
+ _viewModel.OnTabMoved = HandleTabMoved;
+ _viewModel.OnInteractRequested = HandleInteractRequested;
+ _viewModel.OnCloseRequested = HandleCloseRequested;
+ _viewModel.OnMenuCategorySelected = HandleMenuCategorySelected;
+ }
+
+ private void InitializeViews()
+ {
+ _checklistView.Initalize();
+ _inventoryView.Initialize();
+ _itemDetailView.Initialize();
+ _todayMenuView.Initialize();
+ _todayRestaurantStateView.Initialize();
+ }
+
+ private void SetupTabs()
+ {
+ SetupCategoryTabs();
+ InitializeTabGroups();
+ SelectInitialTabs();
+ }
+
+ private void SetupCategoryTabs()
+ {
+ _menuCategoryTabs.UseDefaultAllowedValues();
+ _cookwareCategoryTabs.UseDefaultAllowedValues();
+ }
+
+ private void InitializeTabGroups()
+ {
+ _sectionTabs.Initialize(OnSectionTabSelected);
+ _menuCategoryTabs.Initialize(OnCategoryTabSelected);
+ _cookwareCategoryTabs.Initialize(OnCategoryTabSelected);
+ }
+
+ private void SelectInitialTabs()
+ {
+ _sectionTabs.SelectFirstTab();
+ _menuCategoryTabs.SelectFirstTab();
+ }
+
+ private void UpdateSectionTabs()
+ {
+ if (_viewModel == null) return;
+ _sectionTabs.SelectTab((int)_viewModel.CurrentSection);
+ }
+
+ private void UpdateCategoryTabs()
+ {
+ if (_viewModel == null) return;
+
+ switch (_viewModel.CurrentSection)
+ {
+ case SectionButtonType.Menu:
+ _menuCategoryTabs.SelectTab((int)_viewModel.CurrentCategory);
+ break;
+ case SectionButtonType.Cookware:
+ _cookwareCategoryTabs.SelectTab((int)_viewModel.CurrentCategory);
+ break;
+ }
+ }
+
+ // ViewModel 이벤트 핸들러들
+ private void HandleBatchCompleted()
+ {
+ Close();
+ }
+
+ private void HandleChecklistFailed()
+ {
+ var evt = GameEvents.OpenPopupUiEvent;
+ evt.UiType = typeof(ConfirmUi);
+ evt.IsCancelButtonVisible = true;
+ evt.NewMessageKey = "checklist_failed_message";
+ evt.OnConfirm = ClosePanel;
+ EventBus.Broadcast(evt);
+ }
+
+ private void HandleMenuSectionSelected()
+ {
+ _menuCategoryTabs.SelectFirstTab();
+ }
+
+ private void HandleCookwareSectionSelected()
+ {
+ _cookwareCategoryTabs.SelectFirstTab();
+ }
+
+ private void HandleCategoryChanged(InventoryCategoryType category)
+ {
+ _inventoryView.UpdateCategoryView(category);
+ _itemDetailView.UpdateCategory(category);
+ }
+
+ private void HandleTabMoved(int direction)
+ {
+ _sectionTabs.Move(direction);
+ }
+
+ private void HandleInteractRequested()
+ {
+ var selected = EventSystem.current.currentSelectedGameObject;
+ var interactable = selected?.GetComponent();
+ interactable?.OnInteract();
+ }
+
+ private void HandleCloseRequested()
+ {
+ Close();
+ }
+
+ private void HandleMenuCategorySelected(InventoryCategoryType category)
+ {
+ _menuCategoryTabs.SelectTab((int)category);
+ }
+
+ // UI 이벤트 핸들러들 (TabGroupUi 콜백)
+ private void OnSectionTabSelected(int sectionValue)
+ {
+ _viewModel?.SetSection((SectionButtonType)sectionValue);
+ }
+
+ private void OnCategoryTabSelected(int categoryValue)
+ {
+ _viewModel?.SetCategory((InventoryCategoryType)categoryValue);
+ }
+
+ // 입력 처리 - ViewModel로 위임
+ protected override bool OnInputPerformed(RestaurantUiActions actionEnum, InputAction.CallbackContext context)
+ {
+ var isHandled = base.OnInputPerformed(actionEnum, context);
+
+ if (isHandled && _viewModel != null)
+ {
+ switch (actionEnum)
+ {
+ case RestaurantUiActions.Cancel:
+ _viewModel.CloseUi();
+ break;
+ case RestaurantUiActions.PreviousTab:
+ _viewModel.MoveTab(-1);
+ break;
+ case RestaurantUiActions.NextTab:
+ _viewModel.MoveTab(1);
+ break;
+ case RestaurantUiActions.Interact1:
+ _viewModel.InteractWithSelected();
+ break;
+ case RestaurantUiActions.Interact2:
+ _viewModel.StartHold();
+ break;
+ }
+ }
+
+ return isHandled;
+ }
+
+ protected override bool OnInputCanceled(RestaurantUiActions actionEnum, InputAction.CallbackContext context)
+ {
+ var isHandled = base.OnInputCanceled(actionEnum, context);
+
+ if (isHandled && _viewModel != null)
+ {
+ switch (actionEnum)
+ {
+ case RestaurantUiActions.Interact2:
+ _viewModel.CancelHold();
+ break;
+ }
+ }
+
+ return isHandled;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/_DDD/_Scripts/GameUi/New/NewRestaurantManagementUi.cs.meta b/Assets/_DDD/_Scripts/GameUi/New/NewRestaurantManagementUi.cs.meta
new file mode 100644
index 000000000..fb8636ee8
--- /dev/null
+++ b/Assets/_DDD/_Scripts/GameUi/New/NewRestaurantManagementUi.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 8a2e0954aa144633aad86e53dc80a46a
+timeCreated: 1755673386
\ No newline at end of file
diff --git a/Assets/_DDD/_Scripts/GameUi/New/RestaurantManagementViewModel.cs b/Assets/_DDD/_Scripts/GameUi/New/RestaurantManagementViewModel.cs
new file mode 100644
index 000000000..e88b79e83
--- /dev/null
+++ b/Assets/_DDD/_Scripts/GameUi/New/RestaurantManagementViewModel.cs
@@ -0,0 +1,241 @@
+using System.Linq;
+using DDD.MVVM;
+using UnityEngine;
+
+namespace DDD
+{
+ ///
+ /// 레스토랑 관리 UI의 ViewModel
+ /// 기존 RestaurantManagementUi의 상태 관리와 비즈니스 로직을 담당
+ ///
+ public class RestaurantManagementViewModel : SimpleViewModel, IEventHandler
+ {
+ // 홀드 진행 상태 관리
+ private bool _isHolding;
+ private float _elapsedTime;
+ private float _holdCompleteTime = 1f;
+
+ // 탭 상태 관리
+ private SectionButtonType _currentSection = SectionButtonType.Menu;
+ private InventoryCategoryType _currentCategory = InventoryCategoryType.Food;
+
+ ///
+ /// 현재 홀드 상태
+ ///
+ public bool IsHolding
+ {
+ get => _isHolding;
+ private set => SetField(ref _isHolding, value);
+ }
+
+ ///
+ /// 홀드 진행 시간 (0.0 ~ 1.0)
+ ///
+ public float HoldProgress
+ {
+ get => _elapsedTime;
+ private set => SetField(ref _elapsedTime, value);
+ }
+
+ ///
+ /// 홀드 완료에 필요한 시간
+ ///
+ public float HoldCompleteTime
+ {
+ get => _holdCompleteTime;
+ set => SetField(ref _holdCompleteTime, value);
+ }
+
+ ///
+ /// 현재 선택된 섹션
+ ///
+ public SectionButtonType CurrentSection
+ {
+ get => _currentSection;
+ set => SetField(ref _currentSection, value);
+ }
+
+ ///
+ /// 현재 선택된 카테고리
+ ///
+ public InventoryCategoryType CurrentCategory
+ {
+ get => _currentCategory;
+ set => SetField(ref _currentCategory, value);
+ }
+
+ ///
+ /// 배치 완료 가능 여부 (체크리스트 완료 상태)
+ ///
+ public bool CanCompleteBatch =>
+ RestaurantState.Instance.ManagementState.GetChecklistStates().All(state => state);
+
+ ///
+ /// 홀드 진행률을 0~1 범위로 변환한 값
+ ///
+ public float NormalizedHoldProgress => HoldCompleteTime <= 0f ? 1f : Mathf.Clamp01(HoldProgress / HoldCompleteTime);
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ RegisterEvents();
+ ResetHoldState();
+ }
+
+ public override void Cleanup()
+ {
+ base.Cleanup();
+ UnregisterEvents();
+ }
+
+ private void RegisterEvents()
+ {
+ EventBus.Register(this);
+ }
+
+ private void UnregisterEvents()
+ {
+ EventBus.Unregister(this);
+ }
+
+ ///
+ /// 홀드 진행 업데이트 (View에서 Update마다 호출)
+ ///
+ public void UpdateHoldProgress()
+ {
+ if (!IsHolding) return;
+
+ if (HoldCompleteTime <= 0f)
+ {
+ ProcessCompleteBatch();
+ return;
+ }
+
+ var deltaTime = Time.deltaTime;
+ HoldProgress += deltaTime;
+
+ if (HoldProgress >= HoldCompleteTime)
+ {
+ ProcessCompleteBatch();
+ }
+
+ // UI 업데이트를 위한 정규화된 진행률 알림
+ OnPropertyChanged(nameof(NormalizedHoldProgress));
+ }
+
+ ///
+ /// 홀드 시작
+ ///
+ public void StartHold()
+ {
+ IsHolding = true;
+ HoldProgress = 0f;
+ OnPropertyChanged(nameof(NormalizedHoldProgress));
+ }
+
+ ///
+ /// 홀드 취소
+ ///
+ public void CancelHold()
+ {
+ ResetHoldState();
+ }
+
+ private void ResetHoldState()
+ {
+ IsHolding = false;
+ HoldProgress = 0f;
+ OnPropertyChanged(nameof(NormalizedHoldProgress));
+ }
+
+ ///
+ /// 배치 완료 처리
+ ///
+ private void ProcessCompleteBatch()
+ {
+ ResetHoldState();
+
+ if (CanCompleteBatch)
+ {
+ // 배치 완료 - UI 닫기 이벤트 발생
+ OnBatchCompleted?.Invoke();
+ }
+ else
+ {
+ // 체크리스트 미완료 - 실패 팝업 표시 이벤트 발생
+ OnChecklistFailed?.Invoke();
+ }
+ }
+
+ ///
+ /// 섹션 탭 선택 처리
+ ///
+ public void SetSection(SectionButtonType section)
+ {
+ CurrentSection = section;
+
+ // 섹션 변경 시 해당 섹션의 첫 번째 카테고리로 설정
+ switch (section)
+ {
+ case SectionButtonType.Menu:
+ OnMenuSectionSelected?.Invoke();
+ break;
+ case SectionButtonType.Cookware:
+ OnCookwareSectionSelected?.Invoke();
+ break;
+ }
+ }
+
+ ///
+ /// 카테고리 탭 선택 처리
+ ///
+ public void SetCategory(InventoryCategoryType category)
+ {
+ CurrentCategory = category;
+ OnCategoryChanged?.Invoke(category);
+ }
+
+ ///
+ /// 탭 이동 (이전/다음)
+ ///
+ public void MoveTab(int direction)
+ {
+ OnTabMoved?.Invoke(direction);
+ }
+
+ ///
+ /// 현재 선택된 UI 요소와 상호작용
+ ///
+ public void InteractWithSelected()
+ {
+ OnInteractRequested?.Invoke();
+ }
+
+ ///
+ /// UI 닫기
+ ///
+ public void CloseUi()
+ {
+ OnCloseRequested?.Invoke();
+ }
+
+ // View에서 구독할 이벤트들
+ public System.Action OnBatchCompleted;
+ public System.Action OnChecklistFailed;
+ public System.Action OnMenuSectionSelected;
+ public System.Action OnCookwareSectionSelected;
+ public System.Action OnCategoryChanged;
+ public System.Action OnTabMoved;
+ public System.Action OnInteractRequested;
+ public System.Action OnCloseRequested;
+
+ // 이벤트 핸들러
+ public void Invoke(TodayMenuRemovedEvent evt)
+ {
+ SetCategory(evt.InventoryCategoryType);
+ OnMenuCategorySelected?.Invoke(evt.InventoryCategoryType);
+ }
+
+ public System.Action OnMenuCategorySelected;
+ }
+}
\ No newline at end of file
diff --git a/Assets/_DDD/_Scripts/GameUi/New/RestaurantManagementViewModel.cs.meta b/Assets/_DDD/_Scripts/GameUi/New/RestaurantManagementViewModel.cs.meta
new file mode 100644
index 000000000..10b0cc221
--- /dev/null
+++ b/Assets/_DDD/_Scripts/GameUi/New/RestaurantManagementViewModel.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 80dee5e1862248aab26236036049e5fc
+timeCreated: 1755673405
\ No newline at end of file
diff --git a/Assets/_DDD/_Scripts/GameUi/New/ViewModels/Base/SimpleViewModel.cs b/Assets/_DDD/_Scripts/GameUi/New/ViewModels/Base/SimpleViewModel.cs
index 4f7310265..4ab0a5655 100644
--- a/Assets/_DDD/_Scripts/GameUi/New/ViewModels/Base/SimpleViewModel.cs
+++ b/Assets/_DDD/_Scripts/GameUi/New/ViewModels/Base/SimpleViewModel.cs
@@ -3,7 +3,7 @@
using System.Runtime.CompilerServices;
using UnityEngine;
-namespace DDD.MVVM
+namespace DDD
{
public abstract class SimpleViewModel : MonoBehaviour, INotifyPropertyChanged
{
diff --git a/Assets/_DDD/_Scripts/GameUi/New/Views/Base/IntegratedBasePopupUi.cs b/Assets/_DDD/_Scripts/GameUi/New/Views/Base/IntegratedBasePopupUi.cs
index f5139fa52..6805b6df9 100644
--- a/Assets/_DDD/_Scripts/GameUi/New/Views/Base/IntegratedBasePopupUi.cs
+++ b/Assets/_DDD/_Scripts/GameUi/New/Views/Base/IntegratedBasePopupUi.cs
@@ -1,31 +1,43 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Reflection;
using UnityEngine;
using UnityEngine.EventSystems;
-using DDD.MVVM;
+using UnityEngine.UI;
using Sirenix.OdinInspector;
using UnityEngine.InputSystem;
+using DDD.MVVM;
namespace DDD
{
- public abstract class IntegratedBasePopupUi : IntegratedBaseUi
+ public abstract class IntegratedBasePopupUi : BasePopupUi
where TInputEnum : Enum
where TViewModel : SimpleViewModel
{
[SerializeField, Required] protected BaseUiActionsInputBinding _uiActionsInputBinding;
-
+
protected readonly List<(InputAction action, Action handler)> _registeredHandlers =
new();
+
+ // MVVM 기능들
+ protected BindingContext _bindingContext;
+ protected TViewModel _viewModel;
- public InputActionMaps InputActionMaps => _uiActionsInputBinding.InputActionMaps;
- public bool IsTopPopup => UiManager.Instance.PopupUiState.IsTopPopup(this as BasePopupUi);
+ public override InputActionMaps InputActionMaps => _uiActionsInputBinding.InputActionMaps;
+ public bool IsTopPopup => UiManager.Instance.PopupUiState.IsTopPopup(this);
protected override void Awake()
{
base.Awake();
-
- // BasePopupUi의 기본값 적용
+
_enableBlockImage = true;
+ _viewModel = GetComponent();
+
+ _bindingContext = new BindingContext();
+ SetupAutoBindings();
+ SetupBindings();
}
protected override void OnEnable()
@@ -36,8 +48,7 @@ protected override void OnEnable()
protected override void Update()
{
base.Update();
-
- // BasePopupUi의 Update 로직 구현
+
if (IsOpenPanel() == false) return;
var currentSelectedGameObject = EventSystem.current.currentSelectedGameObject;
@@ -50,8 +61,26 @@ protected override void Update()
}
}
}
+
+ public override void Open(OpenPopupUiEvent evt)
+ {
+ base.Open(evt);
- protected abstract GameObject GetInitialSelected();
+ var initialSelected = GetInitialSelected();
+ if (initialSelected != null)
+ {
+ EventSystem.current.SetSelectedGameObject(initialSelected);
+ }
+
+ transform.SetAsLastSibling();
+
+ if (IsTopPopup)
+ {
+ InputManager.Instance.SwitchCurrentActionMap(_uiActionsInputBinding.InputActionMaps);
+ }
+ }
+
+ protected override abstract GameObject GetInitialSelected();
protected override void TryRegister()
{
@@ -110,37 +139,114 @@ protected override void TryUnregister()
// 입력 처리 메서드들
protected virtual bool OnInputStarted(TInputEnum actionEnum, InputAction.CallbackContext context) => IsTopPopup;
+ protected virtual bool OnInputPerformed(TInputEnum actionEnum, InputAction.CallbackContext context) => IsTopPopup;
+ protected virtual bool OnInputCanceled(TInputEnum actionEnum, InputAction.CallbackContext context) => IsTopPopup;
- protected virtual bool OnInputPerformed(TInputEnum actionEnum, InputAction.CallbackContext context) =>
- IsTopPopup;
-
- protected virtual bool OnInputCanceled(TInputEnum actionEnum, InputAction.CallbackContext context) =>
- IsTopPopup;
-
-
- public virtual void Open(OpenPopupUiEvent evt)
+ ///
+ /// Attribute 기반 자동 바인딩 설정
+ ///
+ private void SetupAutoBindings()
{
- OpenPanel();
+ var fields = GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
+ .Where(f => f.GetCustomAttribute() != null);
- var initialSelected = GetInitialSelected();
- if (initialSelected != null)
+ foreach (var field in fields)
{
- EventSystem.current.SetSelectedGameObject(initialSelected);
+ var bindAttribute = field.GetCustomAttribute();
+ SetupBinding(field, bindAttribute);
}
- transform.SetAsLastSibling();
+ // 컬렉션 바인딩 설정
+ var collectionFields = GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
+ .Where(f => f.GetCustomAttribute() != null);
- if (IsTopPopup)
+ foreach (var field in collectionFields)
{
- InputManager.Instance.SwitchCurrentActionMap(_uiActionsInputBinding.InputActionMaps);
+ var bindAttribute = field.GetCustomAttribute();
+ SetupCollectionBinding(field, bindAttribute);
}
}
- public virtual void Close()
+ ///
+ /// 개별 필드의 바인딩 설정
+ ///
+ private void SetupBinding(FieldInfo field, BindToAttribute bindAttribute)
{
- var evt = GameEvents.ClosePopupUiEvent;
- evt.UiType = GetType();
- EventBus.Broadcast(evt);
+ var target = field.GetValue(this);
+
+ IValueConverter converter = null;
+ if (bindAttribute.ConverterType != null)
+ {
+ converter = Activator.CreateInstance(bindAttribute.ConverterType) as IValueConverter;
+ }
+
+ // UI 컴포넌트 타입별 바인딩 타겟 생성
+ IBindingTarget bindingTarget = target switch
+ {
+ Text text => new TextBindingTarget(text, bindAttribute.PropertyPath),
+ Image image => new ImageBindingTarget(image, bindAttribute.PropertyPath),
+ GameObject gameObject => new ActiveBindingTarget(gameObject, bindAttribute.PropertyPath),
+ Slider slider => new SliderBindingTarget(slider, bindAttribute.PropertyPath),
+ _ => null
+ };
+
+ if (bindingTarget != null)
+ {
+ _bindingContext.Bind(bindAttribute.PropertyPath, bindingTarget, converter);
+ }
+ }
+
+ ///
+ /// 컬렉션 바인딩 설정
+ ///
+ private void SetupCollectionBinding(FieldInfo field, BindCollectionAttribute bindAttribute)
+ {
+ var target = field.GetValue(this);
+
+ if (target is Transform parent)
+ {
+ // 컬렉션 바인딩 로직 (필요시 확장)
+ Debug.Log($"Collection binding for {bindAttribute.PropertyPath} is set up on {parent.name}");
+ }
+ }
+
+ ///
+ /// 추가 바인딩 설정 - 하위 클래스에서 구현
+ ///
+ protected virtual void SetupBindings() { }
+
+ ///
+ /// ViewModel 속성 변경 이벤트 핸들러
+ ///
+ protected virtual void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ HandleCustomPropertyChanged(e.PropertyName);
+ }
+
+ ///
+ /// 커스텀 속성 변경 처리 (하위 클래스에서 오버라이드)
+ ///
+ protected virtual void HandleCustomPropertyChanged(string propertyName)
+ {
+ // 하위 클래스에서 구현
+ }
+
+ ///
+ /// UI 패널 열기 오버라이드 (ViewModel 초기화 추가)
+ ///
+ public override void OpenPanel()
+ {
+ base.OpenPanel();
+ _viewModel?.Initialize();
+ }
+
+ ///
+ /// UI 패널 닫기 오버라이드 (ViewModel 정리 추가)
+ ///
+ public override void ClosePanel()
+ {
+ _viewModel?.Cleanup();
+ base.ClosePanel();
}
}
}
\ No newline at end of file