Files
A-Fairytale-Gone-Bad-Briar-…/Assets/Scripts/UI/Codex/CodexWindow.cs

1289 lines
40 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using BriarQueen.Data.Identifiers;
using BriarQueen.Framework.Assets;
using BriarQueen.Framework.Managers.Interaction;
using BriarQueen.Framework.Managers.Player;
using BriarQueen.Framework.Managers.Player.Data;
using BriarQueen.Framework.Managers.UI.Base;
using BriarQueen.Framework.Registries;
using BriarQueen.UI.Menus;
using Cysharp.Threading.Tasks;
using PrimeTween;
using TMPro;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.UI;
using VContainer;
namespace BriarQueen.UI.Codex
{
public class CodexWindow : MonoBehaviour, IUIWindow
{
[Header("Root UI")]
[SerializeField]
private CanvasGroup _canvasGroup;
[SerializeField]
private RectTransform _windowRect;
[Header("Category Buttons")]
[SerializeField]
private CodexCategoryButton _booksButton;
[SerializeField]
private CodexCategoryButton _cluesButton;
[SerializeField]
private CodexCategoryButton _photosButton;
[Header("Left Panel - Location View")]
[SerializeField]
private CanvasGroup _locationListCanvasGroup;
[SerializeField]
private RectTransform _locationListContainer;
[SerializeField]
private VerticalScrollbar _locationScrollbar;
[Header("Left Panel - Entry View")]
[SerializeField]
private CanvasGroup _entryListCanvasGroup;
[SerializeField]
private RectTransform _entryListContainer;
[SerializeField]
private VerticalScrollbar _entryScrollbar;
[SerializeField]
private Button _backToLocationsButton;
[Header("Display Area")]
[SerializeField]
private CanvasGroup _displayCanvasGroup;
[SerializeField]
private RectTransform _displayLayoutRoot;
[SerializeField]
private TextMeshProUGUI _titleText;
[SerializeField]
private TextMeshProUGUI _bodyText;
[SerializeField]
private TextMeshProUGUI _photoDescription;
[SerializeField]
private TextMeshProUGUI _polaroidWriting;
[SerializeField]
private Image _polaroid;
[SerializeField]
private Image _displayImage;
[SerializeField]
private GameObject _contentRoot;
[SerializeField]
private GameObject _emptyStateRoot;
[Header("Tween Settings")]
[SerializeField]
private TweenSettings _windowTweenSettings = new()
{
duration = 1.2f,
ease = Ease.OutQuad,
useUnscaledTime = true
};
[SerializeField]
private TweenSettings _leftPanelFadeSettings = new()
{
duration = 0.3f,
ease = Ease.OutQuad,
useUnscaledTime = true
};
[SerializeField]
private TweenSettings _displayFadeSettings = new()
{
duration = 0.3f,
ease = Ease.OutQuad,
useUnscaledTime = true
};
[Header("Scale")]
[SerializeField]
private float _hiddenScale = 0.85f;
[Header("Internal")]
[SerializeField]
private GraphicRaycaster _graphicRaycaster;
private readonly List<CodexEntryButton> _activeEntryButtons = new();
private readonly List<CodexLocationButton> _activeLocationButtons = new();
private readonly Dictionary<string, float> _entryScrollByCategoryAndLocation = new();
private readonly Dictionary<CodexType, string> _lastSelectedEntryByCategory = new();
private readonly Dictionary<CodexType, Location> _lastSelectedLocationByCategory = new();
private readonly Dictionary<CodexType, float> _locationScrollByCategory = new();
private readonly Stack<CodexEntryButton> _pooledEntryButtons = new();
private readonly Stack<CodexLocationButton> _pooledLocationButtons = new();
private AddressableManager _addressableManager;
private AssetRegistry _assetRegistry;
private bool _cached;
private CodexType _currentCategory = CodexType.BookEntry;
private CodexEntrySo _currentEntry;
private Location _currentLocation = Location.None;
private Sequence _displaySequence;
private AssetReference _entryButtonReference;
private InteractManager _interactManager;
private Sequence _leftPanelSequence;
private AssetReference _locationButtonReference;
private CancellationTokenSource _operationCts;
private PlayerManager _playerManager;
private bool _raycasterRegistered;
private bool _showingEntriesForLocation;
private bool _started;
private Sequence _windowSequence;
public bool IsModal => true;
public WindowType WindowType => WindowType.CodexWindow;
private void Awake()
{
if (_canvasGroup != null)
{
_canvasGroup.alpha = 0f;
_canvasGroup.blocksRaycasts = false;
_canvasGroup.interactable = false;
}
if (_windowRect != null)
_windowRect.localScale = Vector3.one * _hiddenScale;
SetLocationPanelVisibleImmediate(true);
SetEntryPanelVisibleImmediate(false);
SetDisplayVisibleImmediate(false);
ClearDisplay();
SetEmptyStateVisible(true);
}
private void Start()
{
InitializeStaticUi();
_started = true;
}
private void OnEnable()
{
if (_booksButton != null)
_booksButton.OnCategoryClicked += OnCategoryClicked;
if (_cluesButton != null)
_cluesButton.OnCategoryClicked += OnCategoryClicked;
if (_photosButton != null)
_photosButton.OnCategoryClicked += OnCategoryClicked;
if (_backToLocationsButton != null)
_backToLocationsButton.onClick.AddListener(OnBackToLocationsClicked);
if (_started)
TryRegisterRaycaster();
}
private void OnDisable()
{
if (_booksButton != null)
_booksButton.OnCategoryClicked -= OnCategoryClicked;
if (_cluesButton != null)
_cluesButton.OnCategoryClicked -= OnCategoryClicked;
if (_photosButton != null)
_photosButton.OnCategoryClicked -= OnCategoryClicked;
if (_backToLocationsButton != null)
_backToLocationsButton.onClick.RemoveListener(OnBackToLocationsClicked);
ReleaseAllActiveLocationButtons();
ReleaseAllActiveEntryButtons();
CancelActiveOperations();
_interactManager?.ClearExclusiveRaycaster();
}
private void OnDestroy()
{
CancelActiveOperations();
}
public async UniTask Show()
{
Debug.Log("[CodexWindow] Showing CodexWindow");
Debug.Log($"[CodexWindow] Started - {_started}");
CacheButtonReferences();
TryRegisterRaycaster();
if (!_started)
await UniTask.WaitUntil(() => _started, cancellationToken: this.GetCancellationTokenOnDestroy());
if (_canvasGroup == null || _windowRect == null)
{
Debug.LogError("[CodexWindow] Missing CanvasGroup or WindowRect reference.", this);
gameObject.SetActive(true);
await RefreshCurrentCategoryAsync(false, false);
return;
}
ResetOperationCancellation();
gameObject.SetActive(true);
_canvasGroup.alpha = 0f;
_canvasGroup.blocksRaycasts = false;
_canvasGroup.interactable = false;
_windowRect.localScale = Vector3.one * _hiddenScale;
await RefreshCurrentCategoryAsync(false, false);
var token = _operationCts.Token;
_windowSequence = Sequence.Create(useUnscaledTime: true)
.Group(Tween.Alpha(_canvasGroup, new TweenSettings<float>
{
startValue = 0f,
endValue = 1f,
settings = _windowTweenSettings
}))
.Group(Tween.Scale(_windowRect, new TweenSettings<Vector3>
{
startValue = Vector3.one * _hiddenScale,
endValue = Vector3.one,
settings = _windowTweenSettings
}));
try
{
await _windowSequence.ToUniTask(cancellationToken: token);
}
catch (OperationCanceledException)
{
return;
}
finally
{
_windowSequence = default;
}
_canvasGroup.alpha = 1f;
_windowRect.localScale = Vector3.one;
_canvasGroup.blocksRaycasts = true;
_canvasGroup.interactable = true;
if (_currentEntry != null || IsShowingEmptyState())
{
await RefreshLayoutAsync(_displayLayoutRoot);
await FadeInDisplayAsync();
}
}
public async UniTask Hide()
{
if (_canvasGroup == null || _windowRect == null)
{
gameObject.SetActive(false);
return;
}
TryUnregisterRaycaster();
SaveCurrentScrollPosition();
ResetOperationCancellation();
_canvasGroup.blocksRaycasts = false;
_canvasGroup.interactable = false;
var token = _operationCts.Token;
_windowSequence = Sequence.Create(useUnscaledTime: true)
.Group(Tween.Alpha(_canvasGroup, new TweenSettings<float>
{
startValue = _canvasGroup.alpha,
endValue = 0f,
settings = _windowTweenSettings
}))
.Group(Tween.Scale(_windowRect, new TweenSettings<Vector3>
{
startValue = _windowRect.localScale,
endValue = Vector3.one * _hiddenScale,
settings = _windowTweenSettings
}));
try
{
await _windowSequence.ToUniTask(cancellationToken: token);
}
catch (OperationCanceledException)
{
return;
}
finally
{
_windowSequence = default;
}
_canvasGroup.alpha = 0f;
_windowRect.localScale = Vector3.one * _hiddenScale;
gameObject.SetActive(false);
}
[Inject]
public void Construct(
InteractManager interactManager,
PlayerManager playerManager,
AssetRegistry assetRegistry,
AddressableManager addressableManager)
{
_interactManager = interactManager;
_playerManager = playerManager;
_assetRegistry = assetRegistry;
_addressableManager = addressableManager;
}
private void InitializeStaticUi()
{
if (_booksButton != null)
_booksButton.Initialize(CodexType.BookEntry);
if (_cluesButton != null)
_cluesButton.Initialize(CodexType.PuzzleClue);
if (_photosButton != null)
_photosButton.Initialize(CodexType.Photo);
}
private void CacheButtonReferences()
{
if (_cached)
return;
if (_assetRegistry == null)
{
Debug.LogError("[CodexWindow] AssetRegistry was not injected before Start.", this);
return;
}
if (!_assetRegistry.TryGetReference(AssetKeyIdentifiers.Get(UIKey.CodexLocationButton),
out _locationButtonReference) ||
_locationButtonReference == null)
Debug.LogError("[CodexWindow] Failed to resolve location button addressable reference.", this);
if (!_assetRegistry.TryGetReference(AssetKeyIdentifiers.Get(UIKey.CodexEntryButton),
out _entryButtonReference) ||
_entryButtonReference == null)
Debug.LogError("[CodexWindow] Failed to resolve entry button addressable reference.", this);
_cached = true;
}
private void TryRegisterRaycaster()
{
if (_raycasterRegistered)
return;
if (_interactManager == null || _graphicRaycaster == null)
return;
_interactManager.AddUIRaycaster(_graphicRaycaster);
_interactManager?.SetExclusiveRaycaster(_graphicRaycaster);
_raycasterRegistered = true;
}
private void TryUnregisterRaycaster()
{
if (!_raycasterRegistered)
return;
if (_interactManager == null || _graphicRaycaster == null)
return;
_interactManager.RemoveUIRaycaster(_graphicRaycaster);
_interactManager?.ClearExclusiveRaycaster();
_raycasterRegistered = false;
}
private void OnCategoryClicked(CodexType category)
{
SetCategoryAsync(category).Forget();
}
private void OnBackToLocationsClicked()
{
ShowLocationListAsync().Forget();
}
private async UniTask SetCategoryAsync(CodexType category)
{
if (_currentCategory == category)
return;
SaveCurrentScrollPosition();
_currentCategory = category;
_currentLocation = Location.None;
_currentEntry = null;
_showingEntriesForLocation = false;
await RefreshCurrentCategoryAsync(true, true);
}
private async UniTask RefreshCurrentCategoryAsync(bool animateLeftPanel, bool animateDisplay)
{
if (_playerManager == null)
return;
if (_locationListContainer == null || _entryListContainer == null)
{
Debug.LogError("[CodexWindow] Missing left-panel container references.", this);
ClearDisplay();
SetEmptyStateVisible(true);
SetDisplayVisibleImmediate(true);
return;
}
if (animateLeftPanel && animateDisplay)
await UniTask.WhenAll(
FadeOutCurrentLeftPanelAsync(),
FadeOutDisplayAsync());
else if (animateLeftPanel)
await FadeOutCurrentLeftPanelAsync();
else if (animateDisplay) await FadeOutDisplayAsync();
UpdateCategoryButtonSelection();
var entriesInCategory = _playerManager
.GetDiscoveredCodexEntriesByType(_currentCategory)
.Where(entry => entry != null)
.OrderBy(entry => entry.Title)
.ToList();
ReleaseAllActiveLocationButtons();
ReleaseAllActiveEntryButtons();
if (entriesInCategory.Count == 0)
{
ShowEmptyState();
if (animateLeftPanel)
{
await RefreshLayoutAsync(_locationListContainer);
await FadeInLocationsPanelAsync();
}
if (animateDisplay)
{
await RefreshLayoutAsync(_displayLayoutRoot);
await FadeInDisplayAsync();
}
return;
}
SetEmptyStateVisible(false);
var locations = entriesInCategory
.Select(entry => entry.Location)
.Where(location => location != Location.None)
.Distinct()
.OrderBy(location => location.ToString())
.ToList();
if (locations.Count == 0)
{
ShowEmptyState();
if (animateLeftPanel)
{
await RefreshLayoutAsync(_locationListContainer);
await FadeInLocationsPanelAsync();
}
if (animateDisplay)
{
await RefreshLayoutAsync(_displayLayoutRoot);
await FadeInDisplayAsync();
}
return;
}
foreach (var location in locations)
{
var button = await GetOrCreateLocationButtonAsync();
if (button == null)
continue;
button.Initialize(location);
button.OnLocationClicked -= OnLocationClicked;
button.OnLocationClicked += OnLocationClicked;
_activeLocationButtons.Add(button);
}
_locationScrollbar?.Rebuild();
_currentLocation = GetLocationToSelect(locations);
await BuildEntryButtonsForCurrentLocationAsync();
ClearDisplaySelectionForBrowsing();
_showingEntriesForLocation = false;
SetEntryPanelVisibleImmediate(false);
SetLocationPanelVisibleImmediate(true);
RestoreLocationScrollPosition(1f);
await RefreshLayoutAsync(_locationListContainer);
if (animateLeftPanel)
await FadeInLocationsPanelAsync();
}
private void ShowEmptyState()
{
_currentLocation = Location.None;
_currentEntry = null;
_showingEntriesForLocation = false;
ClearDisplay();
SetEmptyStateVisible(true);
SetDisplayVisibleImmediate(true);
SetLocationPanelVisibleImmediate(true);
SetEntryPanelVisibleImmediate(false);
RestoreLocationScrollPosition(1f);
}
private bool IsShowingEmptyState()
{
return _emptyStateRoot != null && _emptyStateRoot.activeSelf;
}
private Location GetLocationToSelect(List<Location> locations)
{
if (_currentLocation != Location.None && locations.Contains(_currentLocation))
return _currentLocation;
if (_lastSelectedLocationByCategory.TryGetValue(_currentCategory, out var rememberedLocation) &&
rememberedLocation != Location.None &&
locations.Contains(rememberedLocation))
return rememberedLocation;
return locations[0];
}
private void OnLocationClicked(Location location)
{
OnLocationClickedAsync(location).Forget();
}
private async UniTask OnLocationClickedAsync(Location location)
{
if (location == Location.None)
return;
SaveCurrentScrollPosition();
_currentLocation = location;
_lastSelectedLocationByCategory[_currentCategory] = location;
_currentEntry = null;
await UniTask.WhenAll(
FadeOutLocationsPanelAsync(),
FadeOutDisplayAsync());
await BuildEntryButtonsForCurrentLocationAsync();
ClearDisplaySelectionForBrowsing();
await RefreshLayoutAsync(_entryListContainer);
await RefreshLayoutAsync(_displayLayoutRoot);
await FadeInEntriesPanelAsync();
}
private async UniTask BuildEntryButtonsForCurrentLocationAsync()
{
ReleaseAllActiveEntryButtons();
if (_currentLocation == Location.None || _playerManager == null)
return;
var entries = _playerManager
.GetDiscoveredCodexEntriesByType(_currentCategory)
.Where(entry => entry != null && entry.Location == _currentLocation)
.OrderBy(entry => entry.Title)
.ToList();
foreach (var entry in entries)
{
var button = await GetOrCreateEntryButtonAsync();
if (button == null)
continue;
button.Initialize(entry);
button.SetSelected(false);
button.OnEntryClicked -= OnEntryClicked;
button.OnEntryClicked += OnEntryClicked;
_activeEntryButtons.Add(button);
}
_entryScrollbar?.Rebuild();
}
private void ClearDisplaySelectionForBrowsing()
{
_currentEntry = null;
ClearDisplay();
SetEmptyStateVisible(false);
SetDisplayVisibleImmediate(false);
UpdateEntryButtonSelection();
}
private void OnEntryClicked(CodexEntrySo entry)
{
ChangeDisplayedEntryAsync(entry).Forget();
}
private async UniTask ChangeDisplayedEntryAsync(CodexEntrySo entry)
{
if (entry == null)
return;
var hasCurrentVisibleEntry = _currentEntry != null &&
_displayCanvasGroup != null &&
_displayCanvasGroup.alpha > 0.001f;
var isChangingEntry = _currentEntry != null &&
_currentEntry.UniqueID != entry.UniqueID;
if (hasCurrentVisibleEntry && isChangingEntry)
await FadeOutDisplayAsync();
ApplyEntryToDisplay(entry);
UpdateEntryButtonSelection();
await RefreshLayoutAsync(_displayLayoutRoot);
await FadeInDisplayAsync();
}
private void ApplyEntryToDisplay(CodexEntrySo entry)
{
_currentEntry = entry;
if (entry == null)
{
ClearDisplay();
SetDisplayVisibleImmediate(false);
return;
}
_lastSelectedEntryByCategory[_currentCategory] = entry.UniqueID;
if (_titleText != null)
_titleText.text = entry.Title;
var isPhoto = entry.EntryType == CodexType.Photo || entry.IsPhotoOverride;
var showImage = isPhoto && entry.DisplayImage != null;
var showText = !showImage && !string.IsNullOrWhiteSpace(entry.BodyText) || entry.IsBodyTextOverride;
var showPhotoDescription = isPhoto && !string.IsNullOrWhiteSpace(entry.PhotoDescription);
var showPolaroidWriting = isPhoto && !string.IsNullOrWhiteSpace(entry.PolaroidWriting);
if (_polaroid != null)
_polaroid.gameObject.SetActive(isPhoto);
if (_displayImage != null)
{
_displayImage.sprite = showImage ? entry.DisplayImage : null;
_displayImage.enabled = showImage;
_displayImage.gameObject.SetActive(showImage);
}
if (_photoDescription != null)
{
_photoDescription.text = showPhotoDescription ? entry.PhotoDescription : string.Empty;
_photoDescription.gameObject.SetActive(showPhotoDescription);
}
if (_polaroidWriting != null)
{
_polaroidWriting.text = showPolaroidWriting ? entry.PolaroidWriting : string.Empty;
_polaroidWriting.gameObject.SetActive(showPolaroidWriting);
}
if (_bodyText != null)
{
_bodyText.text = showText ? entry.BodyText : string.Empty;
_bodyText.gameObject.SetActive(showText);
}
SetEmptyStateVisible(false);
}
private void ClearDisplay()
{
if (_titleText != null)
_titleText.text = string.Empty;
if (_polaroid != null)
_polaroid.gameObject.SetActive(false);
if (_bodyText != null)
{
_bodyText.text = string.Empty;
_bodyText.gameObject.SetActive(false);
}
if (_displayImage != null)
{
_displayImage.sprite = null;
_displayImage.enabled = false;
_displayImage.gameObject.SetActive(false);
}
if (_photoDescription != null)
{
_photoDescription.text = string.Empty;
_photoDescription.gameObject.SetActive(false);
}
if (_polaroidWriting != null)
{
_polaroidWriting.text = string.Empty;
_polaroidWriting.gameObject.SetActive(false);
}
}
private void SetEmptyStateVisible(bool visible)
{
if (_emptyStateRoot != null)
_emptyStateRoot.SetActive(visible);
if (_contentRoot != null)
_contentRoot.SetActive(!visible);
}
private async UniTask<CodexLocationButton> GetOrCreateLocationButtonAsync()
{
if (_locationButtonReference == null)
return null;
if (_pooledLocationButtons.Count > 0)
{
var pooledButton = _pooledLocationButtons.Pop();
pooledButton.transform.SetParent(_locationListContainer, false);
pooledButton.gameObject.SetActive(true);
return pooledButton;
}
var instance =
await _addressableManager.InstantiateAsync(_locationButtonReference, parent: _locationListContainer);
if (instance == null)
return null;
var button = instance.GetComponent<CodexLocationButton>();
if (button == null)
{
Debug.LogError("[CodexWindow] Spawned location button is missing CodexLocationButton component.",
instance);
return null;
}
button.transform.SetParent(_locationListContainer, false);
button.gameObject.SetActive(true);
return button;
}
private async UniTask<CodexEntryButton> GetOrCreateEntryButtonAsync()
{
if (_entryButtonReference == null)
return null;
if (_pooledEntryButtons.Count > 0)
{
var pooledButton = _pooledEntryButtons.Pop();
pooledButton.transform.SetParent(_entryListContainer, false);
pooledButton.gameObject.SetActive(true);
return pooledButton;
}
var instance =
await _addressableManager.InstantiateAsync(_entryButtonReference, parent: _entryListContainer);
if (instance == null)
return null;
var button = instance.GetComponent<CodexEntryButton>();
if (button == null)
{
Debug.LogError("[CodexWindow] Spawned entry button is missing CodexEntryButton component.", instance);
return null;
}
button.transform.SetParent(_entryListContainer, false);
button.gameObject.SetActive(true);
return button;
}
private void ReleaseAllActiveLocationButtons()
{
foreach (var button in _activeLocationButtons)
{
if (button == null)
continue;
button.OnLocationClicked -= OnLocationClicked;
button.gameObject.SetActive(false);
_pooledLocationButtons.Push(button);
}
_activeLocationButtons.Clear();
_locationScrollbar?.Rebuild();
}
private void ReleaseAllActiveEntryButtons()
{
foreach (var button in _activeEntryButtons)
{
if (button == null)
continue;
button.OnEntryClicked -= OnEntryClicked;
button.SetSelected(false);
button.gameObject.SetActive(false);
_pooledEntryButtons.Push(button);
}
_activeEntryButtons.Clear();
_entryScrollbar?.Rebuild();
}
private void UpdateEntryButtonSelection()
{
foreach (var button in _activeEntryButtons)
{
if (button == null)
continue;
var isSelected = _currentEntry != null &&
button.Entry != null &&
button.Entry.UniqueID == _currentEntry.UniqueID;
button.SetSelected(isSelected);
}
}
private void UpdateCategoryButtonSelection()
{
if (_booksButton != null)
_booksButton.SetSelected(_currentCategory == CodexType.BookEntry);
if (_cluesButton != null)
_cluesButton.SetSelected(_currentCategory == CodexType.PuzzleClue);
if (_photosButton != null)
_photosButton.SetSelected(_currentCategory == CodexType.Photo);
}
private void SaveCurrentScrollPosition()
{
if (_showingEntriesForLocation)
{
if (_entryScrollbar != null && _currentLocation != Location.None)
_entryScrollByCategoryAndLocation[GetEntryScrollKey(_currentCategory, _currentLocation)] =
_entryScrollbar.Normalized;
}
else
{
if (_locationScrollbar != null)
_locationScrollByCategory[_currentCategory] = _locationScrollbar.Normalized;
}
}
private void RestoreLocationScrollPosition(float fallbackValue)
{
if (_locationScrollbar == null)
return;
_locationScrollbar.Rebuild();
var target = _locationScrollByCategory.TryGetValue(_currentCategory, out var saved)
? saved
: fallbackValue;
_locationScrollbar.SetNormalized(target);
}
private void RestoreEntryScrollPosition(float fallbackValue)
{
if (_entryScrollbar == null || _currentLocation == Location.None)
return;
_entryScrollbar.Rebuild();
var key = GetEntryScrollKey(_currentCategory, _currentLocation);
var target = _entryScrollByCategoryAndLocation.TryGetValue(key, out var saved)
? saved
: fallbackValue;
_entryScrollbar.SetNormalized(target);
}
private static string GetEntryScrollKey(CodexType category, Location location)
{
return $"{category}_{location}";
}
private async UniTask RefreshLayoutAsync(RectTransform layoutRoot)
{
Canvas.ForceUpdateCanvases();
if (layoutRoot != null)
LayoutRebuilder.ForceRebuildLayoutImmediate(layoutRoot);
await UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate);
Canvas.ForceUpdateCanvases();
if (layoutRoot != null)
LayoutRebuilder.ForceRebuildLayoutImmediate(layoutRoot);
}
private async UniTask FadeOutCurrentLeftPanelAsync()
{
if (_showingEntriesForLocation)
await FadeOutEntriesPanelAsync();
else
await FadeOutLocationsPanelAsync();
}
private async UniTask FadeOutLocationsPanelAsync()
{
if (_locationListCanvasGroup == null)
return;
ResetLeftPanelTweenOnly();
var token = _operationCts?.Token ?? this.GetCancellationTokenOnDestroy();
_leftPanelSequence = Sequence.Create(useUnscaledTime: true)
.Group(Tween.Alpha(_locationListCanvasGroup, new TweenSettings<float>
{
startValue = _locationListCanvasGroup.alpha,
endValue = 0f,
settings = _leftPanelFadeSettings
}));
try
{
await _leftPanelSequence.ToUniTask(cancellationToken: token);
}
catch (OperationCanceledException)
{
return;
}
finally
{
_leftPanelSequence = default;
}
SetLocationPanelVisibleImmediate(false);
}
private async UniTask FadeOutEntriesPanelAsync()
{
if (_entryListCanvasGroup == null)
return;
ResetLeftPanelTweenOnly();
var token = _operationCts?.Token ?? this.GetCancellationTokenOnDestroy();
_leftPanelSequence = Sequence.Create(useUnscaledTime: true)
.Group(Tween.Alpha(_entryListCanvasGroup, new TweenSettings<float>
{
startValue = _entryListCanvasGroup.alpha,
endValue = 0f,
settings = _leftPanelFadeSettings
}));
try
{
await _leftPanelSequence.ToUniTask(cancellationToken: token);
}
catch (OperationCanceledException)
{
return;
}
finally
{
_leftPanelSequence = default;
}
SetEntryPanelVisibleImmediate(false);
}
private async UniTask FadeInLocationsPanelAsync()
{
if (_locationListCanvasGroup == null)
return;
SetEntryPanelVisibleImmediate(false);
SetLocationPanelVisibleImmediate(true);
_showingEntriesForLocation = false;
ResetLeftPanelTweenOnly();
_locationListCanvasGroup.alpha = 0f;
var token = _operationCts?.Token ?? this.GetCancellationTokenOnDestroy();
_leftPanelSequence = Sequence.Create(useUnscaledTime: true)
.Group(Tween.Alpha(_locationListCanvasGroup, new TweenSettings<float>
{
startValue = 0f,
endValue = 1f,
settings = _leftPanelFadeSettings
}));
try
{
await _leftPanelSequence.ToUniTask(cancellationToken: token);
}
catch (OperationCanceledException)
{
}
finally
{
_leftPanelSequence = default;
}
RestoreLocationScrollPosition(1f);
}
private async UniTask FadeInEntriesPanelAsync()
{
if (_entryListCanvasGroup == null)
return;
SetLocationPanelVisibleImmediate(false);
SetEntryPanelVisibleImmediate(true);
_showingEntriesForLocation = true;
ResetLeftPanelTweenOnly();
_entryListCanvasGroup.alpha = 0f;
var token = _operationCts?.Token ?? this.GetCancellationTokenOnDestroy();
_leftPanelSequence = Sequence.Create(useUnscaledTime: true)
.Group(Tween.Alpha(_entryListCanvasGroup, new TweenSettings<float>
{
startValue = 0f,
endValue = 1f,
settings = _leftPanelFadeSettings
}));
try
{
await _leftPanelSequence.ToUniTask(cancellationToken: token);
}
catch (OperationCanceledException)
{
}
finally
{
_leftPanelSequence = default;
}
RestoreEntryScrollPosition(1f);
}
private async UniTask ShowLocationListAsync()
{
SaveCurrentScrollPosition();
_currentEntry = null;
await FadeOutEntriesPanelAsync();
await FadeOutDisplayAsync();
ClearDisplaySelectionForBrowsing();
await RefreshLayoutAsync(_locationListContainer);
await FadeInLocationsPanelAsync();
}
private async UniTask FadeOutDisplayAsync()
{
if (_displayCanvasGroup == null)
return;
ResetDisplayTweenOnly();
var token = _operationCts?.Token ?? this.GetCancellationTokenOnDestroy();
_displaySequence = Sequence.Create(useUnscaledTime: true)
.Group(Tween.Alpha(_displayCanvasGroup, new TweenSettings<float>
{
startValue = _displayCanvasGroup.alpha,
endValue = 0f,
settings = _displayFadeSettings
}));
try
{
await _displaySequence.ToUniTask(cancellationToken: token);
}
catch (OperationCanceledException)
{
return;
}
finally
{
_displaySequence = default;
}
SetDisplayVisibleImmediate(false);
}
private async UniTask FadeInDisplayAsync()
{
if (_displayCanvasGroup == null)
return;
SetDisplayVisibleImmediate(true);
ResetDisplayTweenOnly();
_displayCanvasGroup.alpha = 0f;
var token = _operationCts?.Token ?? this.GetCancellationTokenOnDestroy();
_displaySequence = Sequence.Create(useUnscaledTime: true)
.Group(Tween.Alpha(_displayCanvasGroup, new TweenSettings<float>
{
startValue = 0f,
endValue = 1f,
settings = _displayFadeSettings
}));
try
{
await _displaySequence.ToUniTask(cancellationToken: token);
}
catch (OperationCanceledException)
{
}
finally
{
_displaySequence = default;
}
}
private void SetLocationPanelVisibleImmediate(bool visible)
{
if (_locationListCanvasGroup == null)
return;
_locationListCanvasGroup.alpha = visible ? 1f : 0f;
_locationListCanvasGroup.blocksRaycasts = visible;
_locationListCanvasGroup.interactable = visible;
_locationListCanvasGroup.gameObject.SetActive(visible);
}
private void SetEntryPanelVisibleImmediate(bool visible)
{
if (_entryListCanvasGroup == null)
return;
_entryListCanvasGroup.alpha = visible ? 1f : 0f;
_entryListCanvasGroup.blocksRaycasts = visible;
_entryListCanvasGroup.interactable = visible;
_entryListCanvasGroup.gameObject.SetActive(visible);
if (_backToLocationsButton != null)
_backToLocationsButton.gameObject.SetActive(visible);
}
private void SetDisplayVisibleImmediate(bool visible)
{
if (_displayCanvasGroup == null)
return;
_displayCanvasGroup.alpha = visible ? 1f : 0f;
_displayCanvasGroup.blocksRaycasts = visible;
_displayCanvasGroup.interactable = visible;
_displayCanvasGroup.gameObject.SetActive(visible);
}
private void ResetLeftPanelTweenOnly()
{
if (_leftPanelSequence.isAlive)
_leftPanelSequence.Stop();
_leftPanelSequence = default;
}
private void ResetDisplayTweenOnly()
{
if (_displaySequence.isAlive)
_displaySequence.Stop();
_displaySequence = default;
}
private void CancelActiveOperations()
{
if (_windowSequence.isAlive)
_windowSequence.Stop();
ResetLeftPanelTweenOnly();
ResetDisplayTweenOnly();
if (_operationCts == null)
return;
try
{
_operationCts.Cancel();
}
catch
{
}
_operationCts.Dispose();
_operationCts = null;
}
private void ResetOperationCancellation()
{
CancelActiveOperations();
_operationCts = new CancellationTokenSource();
}
}
}