테스트 내용 저장
This commit is contained in:
parent
832c369073
commit
0cce2efe62
@ -6311,7 +6311,7 @@ PrefabInstance:
|
|||||||
m_AddedComponents:
|
m_AddedComponents:
|
||||||
- targetCorrespondingSourceObject: {fileID: 6952779389930089995, guid: 4f2bf029cb06b084ba41defc8fc76731, type: 3}
|
- targetCorrespondingSourceObject: {fileID: 6952779389930089995, guid: 4f2bf029cb06b084ba41defc8fc76731, type: 3}
|
||||||
insertIndex: -1
|
insertIndex: -1
|
||||||
addedObject: {fileID: 2438716745211137680}
|
addedObject: {fileID: 7197937761328488698}
|
||||||
m_SourcePrefab: {fileID: 100100000, guid: 4f2bf029cb06b084ba41defc8fc76731, type: 3}
|
m_SourcePrefab: {fileID: 100100000, guid: 4f2bf029cb06b084ba41defc8fc76731, type: 3}
|
||||||
--- !u!224 &4720669467062659157 stripped
|
--- !u!224 &4720669467062659157 stripped
|
||||||
RectTransform:
|
RectTransform:
|
||||||
@ -6323,7 +6323,7 @@ GameObject:
|
|||||||
m_CorrespondingSourceObject: {fileID: 6952779389930089995, guid: 4f2bf029cb06b084ba41defc8fc76731, type: 3}
|
m_CorrespondingSourceObject: {fileID: 6952779389930089995, guid: 4f2bf029cb06b084ba41defc8fc76731, type: 3}
|
||||||
m_PrefabInstance: {fileID: 4463400116329503023}
|
m_PrefabInstance: {fileID: 4463400116329503023}
|
||||||
m_PrefabAsset: {fileID: 0}
|
m_PrefabAsset: {fileID: 0}
|
||||||
--- !u!114 &2438716745211137680
|
--- !u!114 &7197937761328488698
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
@ -6332,10 +6332,10 @@ MonoBehaviour:
|
|||||||
m_GameObject: {fileID: 6740783381500491556}
|
m_GameObject: {fileID: 6740783381500491556}
|
||||||
m_Enabled: 1
|
m_Enabled: 1
|
||||||
m_EditorHideFlags: 0
|
m_EditorHideFlags: 0
|
||||||
m_Script: {fileID: 11500000, guid: 46c8396c996c804449b383960b44e812, type: 3}
|
m_Script: {fileID: 11500000, guid: 8a2e0954aa144633aad86e53dc80a46a, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier:
|
m_EditorClassIdentifier:
|
||||||
_enableBlockImage: 1
|
_enableBlockImage: 0
|
||||||
_uiActionsInputBinding: {fileID: 11400000, guid: 8073fcaf56fc7c34e996d0d47044f146, type: 2}
|
_uiActionsInputBinding: {fileID: 11400000, guid: 8073fcaf56fc7c34e996d0d47044f146, type: 2}
|
||||||
_checklistView: {fileID: 7075966153492927588}
|
_checklistView: {fileID: 7075966153492927588}
|
||||||
_inventoryView: {fileID: 3570087040626823091}
|
_inventoryView: {fileID: 3570087040626823091}
|
||||||
@ -6346,7 +6346,6 @@ MonoBehaviour:
|
|||||||
_menuCategoryTabs: {fileID: 6805049896193344908}
|
_menuCategoryTabs: {fileID: 6805049896193344908}
|
||||||
_cookwareCategoryTabs: {fileID: 6628923975427483430}
|
_cookwareCategoryTabs: {fileID: 6628923975427483430}
|
||||||
_completeBatchFilledImage: {fileID: 2965326806322860544}
|
_completeBatchFilledImage: {fileID: 2965326806322860544}
|
||||||
_holdCompleteTime: 0.5
|
|
||||||
--- !u!1001 &4530765275021007961
|
--- !u!1001 &4530765275021007961
|
||||||
PrefabInstance:
|
PrefabInstance:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
275
Assets/_DDD/_Scripts/GameUi/New/NewRestaurantManagementUi.cs
Normal file
275
Assets/_DDD/_Scripts/GameUi/New/NewRestaurantManagementUi.cs
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using UnityEngine.EventSystems;
|
||||||
|
using UnityEngine.InputSystem;
|
||||||
|
using DDD.MVVM;
|
||||||
|
|
||||||
|
namespace DDD
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// MVVM 패턴을 적용한 새로운 레스토랑 관리 UI
|
||||||
|
/// 기존 RestaurantManagementUi의 기능을 ViewModel과 분리하여 구현
|
||||||
|
/// </summary>
|
||||||
|
public class NewRestaurantManagementUi : IntegratedBasePopupUi<RestaurantUiActions, RestaurantManagementViewModel>
|
||||||
|
{
|
||||||
|
[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<IInteractableUi>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8a2e0954aa144633aad86e53dc80a46a
|
||||||
|
timeCreated: 1755673386
|
241
Assets/_DDD/_Scripts/GameUi/New/RestaurantManagementViewModel.cs
Normal file
241
Assets/_DDD/_Scripts/GameUi/New/RestaurantManagementViewModel.cs
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using DDD.MVVM;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace DDD
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 레스토랑 관리 UI의 ViewModel
|
||||||
|
/// 기존 RestaurantManagementUi의 상태 관리와 비즈니스 로직을 담당
|
||||||
|
/// </summary>
|
||||||
|
public class RestaurantManagementViewModel : SimpleViewModel, IEventHandler<TodayMenuRemovedEvent>
|
||||||
|
{
|
||||||
|
// 홀드 진행 상태 관리
|
||||||
|
private bool _isHolding;
|
||||||
|
private float _elapsedTime;
|
||||||
|
private float _holdCompleteTime = 1f;
|
||||||
|
|
||||||
|
// 탭 상태 관리
|
||||||
|
private SectionButtonType _currentSection = SectionButtonType.Menu;
|
||||||
|
private InventoryCategoryType _currentCategory = InventoryCategoryType.Food;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 현재 홀드 상태
|
||||||
|
/// </summary>
|
||||||
|
public bool IsHolding
|
||||||
|
{
|
||||||
|
get => _isHolding;
|
||||||
|
private set => SetField(ref _isHolding, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 홀드 진행 시간 (0.0 ~ 1.0)
|
||||||
|
/// </summary>
|
||||||
|
public float HoldProgress
|
||||||
|
{
|
||||||
|
get => _elapsedTime;
|
||||||
|
private set => SetField(ref _elapsedTime, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 홀드 완료에 필요한 시간
|
||||||
|
/// </summary>
|
||||||
|
public float HoldCompleteTime
|
||||||
|
{
|
||||||
|
get => _holdCompleteTime;
|
||||||
|
set => SetField(ref _holdCompleteTime, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 현재 선택된 섹션
|
||||||
|
/// </summary>
|
||||||
|
public SectionButtonType CurrentSection
|
||||||
|
{
|
||||||
|
get => _currentSection;
|
||||||
|
set => SetField(ref _currentSection, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 현재 선택된 카테고리
|
||||||
|
/// </summary>
|
||||||
|
public InventoryCategoryType CurrentCategory
|
||||||
|
{
|
||||||
|
get => _currentCategory;
|
||||||
|
set => SetField(ref _currentCategory, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 배치 완료 가능 여부 (체크리스트 완료 상태)
|
||||||
|
/// </summary>
|
||||||
|
public bool CanCompleteBatch =>
|
||||||
|
RestaurantState.Instance.ManagementState.GetChecklistStates().All(state => state);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 홀드 진행률을 0~1 범위로 변환한 값
|
||||||
|
/// </summary>
|
||||||
|
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<TodayMenuRemovedEvent>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnregisterEvents()
|
||||||
|
{
|
||||||
|
EventBus.Unregister<TodayMenuRemovedEvent>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 홀드 진행 업데이트 (View에서 Update마다 호출)
|
||||||
|
/// </summary>
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 홀드 시작
|
||||||
|
/// </summary>
|
||||||
|
public void StartHold()
|
||||||
|
{
|
||||||
|
IsHolding = true;
|
||||||
|
HoldProgress = 0f;
|
||||||
|
OnPropertyChanged(nameof(NormalizedHoldProgress));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 홀드 취소
|
||||||
|
/// </summary>
|
||||||
|
public void CancelHold()
|
||||||
|
{
|
||||||
|
ResetHoldState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetHoldState()
|
||||||
|
{
|
||||||
|
IsHolding = false;
|
||||||
|
HoldProgress = 0f;
|
||||||
|
OnPropertyChanged(nameof(NormalizedHoldProgress));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 배치 완료 처리
|
||||||
|
/// </summary>
|
||||||
|
private void ProcessCompleteBatch()
|
||||||
|
{
|
||||||
|
ResetHoldState();
|
||||||
|
|
||||||
|
if (CanCompleteBatch)
|
||||||
|
{
|
||||||
|
// 배치 완료 - UI 닫기 이벤트 발생
|
||||||
|
OnBatchCompleted?.Invoke();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 체크리스트 미완료 - 실패 팝업 표시 이벤트 발생
|
||||||
|
OnChecklistFailed?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 섹션 탭 선택 처리
|
||||||
|
/// </summary>
|
||||||
|
public void SetSection(SectionButtonType section)
|
||||||
|
{
|
||||||
|
CurrentSection = section;
|
||||||
|
|
||||||
|
// 섹션 변경 시 해당 섹션의 첫 번째 카테고리로 설정
|
||||||
|
switch (section)
|
||||||
|
{
|
||||||
|
case SectionButtonType.Menu:
|
||||||
|
OnMenuSectionSelected?.Invoke();
|
||||||
|
break;
|
||||||
|
case SectionButtonType.Cookware:
|
||||||
|
OnCookwareSectionSelected?.Invoke();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 카테고리 탭 선택 처리
|
||||||
|
/// </summary>
|
||||||
|
public void SetCategory(InventoryCategoryType category)
|
||||||
|
{
|
||||||
|
CurrentCategory = category;
|
||||||
|
OnCategoryChanged?.Invoke(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 탭 이동 (이전/다음)
|
||||||
|
/// </summary>
|
||||||
|
public void MoveTab(int direction)
|
||||||
|
{
|
||||||
|
OnTabMoved?.Invoke(direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 현재 선택된 UI 요소와 상호작용
|
||||||
|
/// </summary>
|
||||||
|
public void InteractWithSelected()
|
||||||
|
{
|
||||||
|
OnInteractRequested?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// UI 닫기
|
||||||
|
/// </summary>
|
||||||
|
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<InventoryCategoryType> OnCategoryChanged;
|
||||||
|
public System.Action<int> OnTabMoved;
|
||||||
|
public System.Action OnInteractRequested;
|
||||||
|
public System.Action OnCloseRequested;
|
||||||
|
|
||||||
|
// 이벤트 핸들러
|
||||||
|
public void Invoke(TodayMenuRemovedEvent evt)
|
||||||
|
{
|
||||||
|
SetCategory(evt.InventoryCategoryType);
|
||||||
|
OnMenuCategorySelected?.Invoke(evt.InventoryCategoryType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public System.Action<InventoryCategoryType> OnMenuCategorySelected;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 80dee5e1862248aab26236036049e5fc
|
||||||
|
timeCreated: 1755673405
|
@ -3,7 +3,7 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace DDD.MVVM
|
namespace DDD
|
||||||
{
|
{
|
||||||
public abstract class SimpleViewModel : MonoBehaviour, INotifyPropertyChanged
|
public abstract class SimpleViewModel : MonoBehaviour, INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
|
@ -1,31 +1,43 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
using DDD.MVVM;
|
using UnityEngine.UI;
|
||||||
using Sirenix.OdinInspector;
|
using Sirenix.OdinInspector;
|
||||||
using UnityEngine.InputSystem;
|
using UnityEngine.InputSystem;
|
||||||
|
using DDD.MVVM;
|
||||||
|
|
||||||
namespace DDD
|
namespace DDD
|
||||||
{
|
{
|
||||||
public abstract class IntegratedBasePopupUi<TInputEnum, TViewModel> : IntegratedBaseUi<TViewModel>
|
public abstract class IntegratedBasePopupUi<TInputEnum, TViewModel> : BasePopupUi
|
||||||
where TInputEnum : Enum
|
where TInputEnum : Enum
|
||||||
where TViewModel : SimpleViewModel
|
where TViewModel : SimpleViewModel
|
||||||
{
|
{
|
||||||
[SerializeField, Required] protected BaseUiActionsInputBinding<TInputEnum> _uiActionsInputBinding;
|
[SerializeField, Required] protected BaseUiActionsInputBinding<TInputEnum> _uiActionsInputBinding;
|
||||||
|
|
||||||
protected readonly List<(InputAction action, Action<InputAction.CallbackContext> handler)> _registeredHandlers =
|
protected readonly List<(InputAction action, Action<InputAction.CallbackContext> handler)> _registeredHandlers =
|
||||||
new();
|
new();
|
||||||
|
|
||||||
|
// MVVM 기능들
|
||||||
|
protected BindingContext _bindingContext;
|
||||||
|
protected TViewModel _viewModel;
|
||||||
|
|
||||||
public InputActionMaps InputActionMaps => _uiActionsInputBinding.InputActionMaps;
|
public override InputActionMaps InputActionMaps => _uiActionsInputBinding.InputActionMaps;
|
||||||
public bool IsTopPopup => UiManager.Instance.PopupUiState.IsTopPopup(this as BasePopupUi);
|
public bool IsTopPopup => UiManager.Instance.PopupUiState.IsTopPopup(this);
|
||||||
|
|
||||||
protected override void Awake()
|
protected override void Awake()
|
||||||
{
|
{
|
||||||
base.Awake();
|
base.Awake();
|
||||||
|
|
||||||
// BasePopupUi의 기본값 적용
|
|
||||||
_enableBlockImage = true;
|
_enableBlockImage = true;
|
||||||
|
_viewModel = GetComponent<TViewModel>();
|
||||||
|
|
||||||
|
_bindingContext = new BindingContext();
|
||||||
|
SetupAutoBindings();
|
||||||
|
SetupBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnEnable()
|
protected override void OnEnable()
|
||||||
@ -36,8 +48,7 @@ protected override void OnEnable()
|
|||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
// BasePopupUi의 Update 로직 구현
|
|
||||||
if (IsOpenPanel() == false) return;
|
if (IsOpenPanel() == false) return;
|
||||||
|
|
||||||
var currentSelectedGameObject = EventSystem.current.currentSelectedGameObject;
|
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()
|
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 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) =>
|
/// <summary>
|
||||||
IsTopPopup;
|
/// Attribute 기반 자동 바인딩 설정
|
||||||
|
/// </summary>
|
||||||
protected virtual bool OnInputCanceled(TInputEnum actionEnum, InputAction.CallbackContext context) =>
|
private void SetupAutoBindings()
|
||||||
IsTopPopup;
|
|
||||||
|
|
||||||
|
|
||||||
public virtual void Open(OpenPopupUiEvent evt)
|
|
||||||
{
|
{
|
||||||
OpenPanel();
|
var fields = GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
|
||||||
|
.Where(f => f.GetCustomAttribute<BindToAttribute>() != null);
|
||||||
|
|
||||||
var initialSelected = GetInitialSelected();
|
foreach (var field in fields)
|
||||||
if (initialSelected != null)
|
|
||||||
{
|
{
|
||||||
EventSystem.current.SetSelectedGameObject(initialSelected);
|
var bindAttribute = field.GetCustomAttribute<BindToAttribute>();
|
||||||
|
SetupBinding(field, bindAttribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
transform.SetAsLastSibling();
|
// 컬렉션 바인딩 설정
|
||||||
|
var collectionFields = GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
|
||||||
|
.Where(f => f.GetCustomAttribute<BindCollectionAttribute>() != null);
|
||||||
|
|
||||||
if (IsTopPopup)
|
foreach (var field in collectionFields)
|
||||||
{
|
{
|
||||||
InputManager.Instance.SwitchCurrentActionMap(_uiActionsInputBinding.InputActionMaps);
|
var bindAttribute = field.GetCustomAttribute<BindCollectionAttribute>();
|
||||||
|
SetupCollectionBinding(field, bindAttribute);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Close()
|
/// <summary>
|
||||||
|
/// 개별 필드의 바인딩 설정
|
||||||
|
/// </summary>
|
||||||
|
private void SetupBinding(FieldInfo field, BindToAttribute bindAttribute)
|
||||||
{
|
{
|
||||||
var evt = GameEvents.ClosePopupUiEvent;
|
var target = field.GetValue(this);
|
||||||
evt.UiType = GetType();
|
|
||||||
EventBus.Broadcast(evt);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 컬렉션 바인딩 설정
|
||||||
|
/// </summary>
|
||||||
|
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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 추가 바인딩 설정 - 하위 클래스에서 구현
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void SetupBindings() { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ViewModel 속성 변경 이벤트 핸들러
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
HandleCustomPropertyChanged(e.PropertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 커스텀 속성 변경 처리 (하위 클래스에서 오버라이드)
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void HandleCustomPropertyChanged(string propertyName)
|
||||||
|
{
|
||||||
|
// 하위 클래스에서 구현
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// UI 패널 열기 오버라이드 (ViewModel 초기화 추가)
|
||||||
|
/// </summary>
|
||||||
|
public override void OpenPanel()
|
||||||
|
{
|
||||||
|
base.OpenPanel();
|
||||||
|
_viewModel?.Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// UI 패널 닫기 오버라이드 (ViewModel 정리 추가)
|
||||||
|
/// </summary>
|
||||||
|
public override void ClosePanel()
|
||||||
|
{
|
||||||
|
_viewModel?.Cleanup();
|
||||||
|
base.ClosePanel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user