First commit for private source control. Older commits available on Github.
This commit is contained in:
434
Assets/Scripts/Framework/Managers/UI/UIManager.cs
Normal file
434
Assets/Scripts/Framework/Managers/UI/UIManager.cs
Normal file
@@ -0,0 +1,434 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using BriarQueen.Data.Identifiers;
|
||||
using BriarQueen.Framework.Coordinators.Events;
|
||||
using BriarQueen.Framework.Events.UI;
|
||||
using BriarQueen.Framework.Managers.Interaction;
|
||||
using BriarQueen.Framework.Managers.IO;
|
||||
using BriarQueen.Framework.Managers.UI.Base;
|
||||
using BriarQueen.Framework.Managers.UI.Events;
|
||||
using BriarQueen.Framework.Services.Settings;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using VContainer;
|
||||
|
||||
namespace BriarQueen.Framework.Managers.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// UIManager:
|
||||
/// - Modal windows use the window stack
|
||||
/// - Non-modal UI (popups / fader) does not use the stack
|
||||
/// - HUD visibility is event-driven
|
||||
/// - Concrete UI implementations are hidden behind interfaces
|
||||
/// </summary>
|
||||
public class UIManager : IDisposable, IManager
|
||||
{
|
||||
private readonly EventCoordinator _eventCoordinator;
|
||||
private readonly InteractManager _interactManager;
|
||||
private readonly SaveManager _saveManager;
|
||||
private readonly SettingsService _settingsService;
|
||||
|
||||
private readonly Dictionary<WindowType, IUIWindow> _windows = new();
|
||||
private readonly Stack<IUIWindow> _windowStack = new();
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public bool Initialized { get; private set; }
|
||||
|
||||
private IHud _hudContainer;
|
||||
private IPopup _infoPopup;
|
||||
private IPopup _tutorialPopup;
|
||||
private IScreenFader _screenFader;
|
||||
|
||||
[Inject]
|
||||
public UIManager(
|
||||
EventCoordinator eventCoordinator,
|
||||
InteractManager interactManager,
|
||||
SettingsService settingsService,
|
||||
SaveManager saveManager)
|
||||
{
|
||||
_eventCoordinator = eventCoordinator;
|
||||
_interactManager = interactManager;
|
||||
_settingsService = settingsService;
|
||||
_saveManager = saveManager;
|
||||
}
|
||||
|
||||
private IUIWindow ActiveWindow => _windowStack.Count > 0 ? _windowStack.Peek() : null;
|
||||
|
||||
public bool IsAnyUIOpen => _windowStack.Count > 0;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
if (Initialized)
|
||||
return;
|
||||
|
||||
_disposed = false;
|
||||
SubscribeToEvents();
|
||||
Initialized = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!Initialized || _disposed)
|
||||
return;
|
||||
|
||||
_disposed = true;
|
||||
|
||||
UnsubscribeFromEvents();
|
||||
ResetUIStateHard();
|
||||
|
||||
Initialized = false;
|
||||
}
|
||||
|
||||
private void SubscribeToEvents()
|
||||
{
|
||||
_eventCoordinator.Subscribe<PauseButtonClickedEvent>(OnPauseClickReceived);
|
||||
_eventCoordinator.Subscribe<ToggleCodexEvent>(ToggleCodexWindow);
|
||||
_eventCoordinator.Subscribe<UIToggleSettingsWindow>(ToggleSettingsWindow);
|
||||
_eventCoordinator.Subscribe<FadeEvent>(OnFadeEvent);
|
||||
_eventCoordinator.Subscribe<DisplayInteractEvent>(OnDisplayInteractText);
|
||||
_eventCoordinator.Subscribe<DisplayTutorialPopupEvent>(OnTutorialDisplayPopup);
|
||||
_eventCoordinator.Subscribe<CodexChangedEvent>(OnCodexChangedEvent);
|
||||
_eventCoordinator.Subscribe<UIToggleHudEvent>(OnHudToggleEvent);
|
||||
_eventCoordinator.Subscribe<ToolbeltChangedEvent>(OnToolbeltChangedEvent);
|
||||
}
|
||||
|
||||
private void UnsubscribeFromEvents()
|
||||
{
|
||||
_eventCoordinator.Unsubscribe<PauseButtonClickedEvent>(OnPauseClickReceived);
|
||||
_eventCoordinator.Unsubscribe<ToggleCodexEvent>(ToggleCodexWindow);
|
||||
_eventCoordinator.Unsubscribe<UIToggleSettingsWindow>(ToggleSettingsWindow);
|
||||
_eventCoordinator.Unsubscribe<FadeEvent>(OnFadeEvent);
|
||||
_eventCoordinator.Unsubscribe<DisplayInteractEvent>(OnDisplayInteractText);
|
||||
_eventCoordinator.Unsubscribe<DisplayTutorialPopupEvent>(OnTutorialDisplayPopup);
|
||||
_eventCoordinator.Unsubscribe<CodexChangedEvent>(OnCodexChangedEvent);
|
||||
_eventCoordinator.Unsubscribe<UIToggleHudEvent>(OnHudToggleEvent);
|
||||
_eventCoordinator.Unsubscribe<ToolbeltChangedEvent>(OnToolbeltChangedEvent);
|
||||
}
|
||||
|
||||
public void RegisterWindow(IUIWindow window)
|
||||
{
|
||||
if (window == null)
|
||||
return;
|
||||
|
||||
_windows[window.WindowType] = window;
|
||||
window.Hide().Forget();
|
||||
}
|
||||
|
||||
public void RegisterHUD(IHud hudContainer)
|
||||
{
|
||||
_hudContainer = hudContainer;
|
||||
|
||||
if (_hudContainer != null)
|
||||
{
|
||||
_hudContainer.Hide().Forget();
|
||||
_interactManager.AddUIRaycaster(_hudContainer.Raycaster);
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterInfoPopup(IPopup infoPopup)
|
||||
{
|
||||
_infoPopup = infoPopup;
|
||||
|
||||
if (_infoPopup != null)
|
||||
_infoPopup.Hide().Forget();
|
||||
}
|
||||
|
||||
public void RegisterTutorialPopup(IPopup tutorialPopup)
|
||||
{
|
||||
_tutorialPopup = tutorialPopup;
|
||||
|
||||
if (_tutorialPopup != null)
|
||||
_tutorialPopup.Hide().Forget();
|
||||
}
|
||||
|
||||
public void RegisterScreenFader(IScreenFader screenFader)
|
||||
{
|
||||
_screenFader = screenFader;
|
||||
}
|
||||
|
||||
private IUIWindow GetWindow(WindowType windowType)
|
||||
{
|
||||
return _windows.TryGetValue(windowType, out var window) ? window : null;
|
||||
}
|
||||
|
||||
private async UniTask ApplyHudVisibility(bool visible)
|
||||
{
|
||||
if (_disposed || _hudContainer == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (visible)
|
||||
await _hudContainer.Show();
|
||||
else
|
||||
await _hudContainer.Hide();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"[UIManager] ApplyHudVisibility error: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPauseClickReceived(PauseButtonClickedEvent _)
|
||||
{
|
||||
if (_windowStack.Count > 0)
|
||||
{
|
||||
CloseTopWindow();
|
||||
return;
|
||||
}
|
||||
|
||||
OpenWindow(WindowType.PauseMenuWindow);
|
||||
}
|
||||
|
||||
private void ToggleSettingsWindow(UIToggleSettingsWindow eventData)
|
||||
{
|
||||
if (eventData.Show)
|
||||
OpenWindow(WindowType.SettingsWindow);
|
||||
else
|
||||
CloseWindow(WindowType.SettingsWindow);
|
||||
}
|
||||
|
||||
private void ToggleCodexWindow(ToggleCodexEvent eventData)
|
||||
{
|
||||
if (eventData.Shown)
|
||||
OpenWindow(WindowType.CodexWindow);
|
||||
else
|
||||
CloseWindow(WindowType.CodexWindow);
|
||||
}
|
||||
|
||||
private void OnCodexChangedEvent(CodexChangedEvent eventData)
|
||||
{
|
||||
if (_infoPopup == null)
|
||||
return;
|
||||
|
||||
var duration = _settingsService?.Game?.PopupDisplayDuration ?? 3f;
|
||||
var codexText = GetCodexTextForEntry(eventData.EntryType);
|
||||
|
||||
_infoPopup.Play(codexText, duration).Forget();
|
||||
}
|
||||
|
||||
private void OnToolbeltChangedEvent(ToolbeltChangedEvent eventData)
|
||||
{
|
||||
if (_infoPopup == null)
|
||||
return;
|
||||
|
||||
var duration = _settingsService?.Game?.PopupDisplayDuration ?? 3f;
|
||||
var toolText = GetToolbeltTextForEntry(eventData.ToolID, eventData.Lost);
|
||||
|
||||
_infoPopup.Play(toolText, duration).Forget();
|
||||
}
|
||||
|
||||
private string GetToolbeltTextForEntry(ToolID toolID, bool lost)
|
||||
{
|
||||
if (lost)
|
||||
return $"You lost the {toolID.ToString()}.";
|
||||
else
|
||||
return $"You gained the {toolID.ToString()}.";
|
||||
}
|
||||
|
||||
private string GetCodexTextForEntry(CodexType codexType)
|
||||
{
|
||||
return codexType switch
|
||||
{
|
||||
CodexType.BookEntry => "You've acquired a new book entry.",
|
||||
CodexType.PuzzleClue => "You've acquired a new puzzle clue.",
|
||||
CodexType.Photo => "You've acquired a new photo.",
|
||||
_ => string.Empty
|
||||
};
|
||||
}
|
||||
|
||||
private void OnTutorialDisplayPopup(DisplayTutorialPopupEvent eventData)
|
||||
{
|
||||
if (_tutorialPopup == null)
|
||||
return;
|
||||
|
||||
if (!_settingsService.AreTutorialsEnabled())
|
||||
return;
|
||||
|
||||
var duration = 3f;
|
||||
var tutorialText = TutorialPopupTexts.AllPopups[eventData.TutorialID];
|
||||
|
||||
_tutorialPopup.Play(tutorialText, duration).Forget();
|
||||
}
|
||||
|
||||
private void OnDisplayInteractText(DisplayInteractEvent eventData)
|
||||
{
|
||||
if (_hudContainer == null)
|
||||
return;
|
||||
|
||||
_hudContainer.DisplayInteractText(eventData.Message).Forget();
|
||||
}
|
||||
|
||||
private void OnFadeEvent(FadeEvent eventData)
|
||||
{
|
||||
if (_screenFader == null)
|
||||
return;
|
||||
|
||||
if (eventData.Hidden)
|
||||
_screenFader.FadeFromAsync(eventData.Duration).Forget();
|
||||
else
|
||||
_screenFader.FadeToAsync(eventData.Duration).Forget();
|
||||
}
|
||||
|
||||
private void OnHudToggleEvent(UIToggleHudEvent eventData)
|
||||
{
|
||||
ApplyHudVisibility(eventData.Show).Forget();
|
||||
}
|
||||
|
||||
public void OpenWindow(WindowType windowType)
|
||||
{
|
||||
OpenWindowInternal(windowType).Forget();
|
||||
}
|
||||
|
||||
private async UniTask OpenWindowInternal(WindowType windowType)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
var window = GetWindow(windowType);
|
||||
if (window == null)
|
||||
{
|
||||
Debug.LogError($"[UIManager] Window of type {windowType} not registered.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ActiveWindow == window)
|
||||
return;
|
||||
|
||||
if (ActiveWindow != null)
|
||||
await ActiveWindow.Hide();
|
||||
|
||||
_windowStack.Push(window);
|
||||
await window.Show();
|
||||
|
||||
NotifyUIStackChanged();
|
||||
}
|
||||
|
||||
public void CloseWindow(WindowType windowType)
|
||||
{
|
||||
CloseWindowInternal(windowType).Forget();
|
||||
}
|
||||
|
||||
private async UniTask CloseWindowInternal(WindowType windowType)
|
||||
{
|
||||
if (_disposed || _windowStack.Count == 0)
|
||||
return;
|
||||
|
||||
var target = GetWindow(windowType);
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
while (_windowStack.Count > 0)
|
||||
{
|
||||
var current = _windowStack.Pop();
|
||||
if (current != null)
|
||||
await current.Hide();
|
||||
|
||||
if (current == target)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ActiveWindow != null)
|
||||
await ActiveWindow.Show();
|
||||
|
||||
NotifyUIStackChanged();
|
||||
}
|
||||
|
||||
public void CloseTopWindow()
|
||||
{
|
||||
CloseTopWindowInternal().Forget();
|
||||
}
|
||||
|
||||
private async UniTask CloseTopWindowInternal()
|
||||
{
|
||||
if (_disposed || _windowStack.Count == 0)
|
||||
return;
|
||||
|
||||
var top = _windowStack.Pop();
|
||||
|
||||
if (top != null)
|
||||
await top.Hide();
|
||||
|
||||
if (ActiveWindow != null)
|
||||
await ActiveWindow.Show();
|
||||
|
||||
NotifyUIStackChanged();
|
||||
}
|
||||
|
||||
public void ResetUIState()
|
||||
{
|
||||
ResetUIStateAsync().Forget();
|
||||
}
|
||||
|
||||
public async UniTask ResetUIStateAsync()
|
||||
{
|
||||
while (_windowStack.Count > 0)
|
||||
{
|
||||
var window = _windowStack.Pop();
|
||||
if (window != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await window.Hide();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_tutorialPopup != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _tutorialPopup.Hide();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (_infoPopup != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _infoPopup.Hide();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
NotifyUIStackChanged();
|
||||
}
|
||||
|
||||
private void ResetUIStateHard()
|
||||
{
|
||||
foreach (var kv in _windows)
|
||||
{
|
||||
if (kv.Value is Component component && component != null)
|
||||
component.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
if (_tutorialPopup?.GameObject != null)
|
||||
_tutorialPopup.GameObject.SetActive(false);
|
||||
|
||||
if (_infoPopup?.GameObject != null)
|
||||
_infoPopup.GameObject.SetActive(false);
|
||||
|
||||
if (_hudContainer is Component hudComponent && hudComponent != null)
|
||||
hudComponent.gameObject.SetActive(false);
|
||||
|
||||
if (_screenFader is Component faderComponent && faderComponent != null)
|
||||
faderComponent.gameObject.SetActive(false);
|
||||
|
||||
_windowStack.Clear();
|
||||
}
|
||||
|
||||
private void NotifyUIStackChanged()
|
||||
{
|
||||
_eventCoordinator.Publish(new UIStackChangedEvent(_windowStack.Count > 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user