Neg_Utils/NEG/UI/UiManager.cs
2025-10-06 17:31:41 +02:00

292 lines
11 KiB
C#

using JetBrains.Annotations;
using NEG.UI.Area;
using NEG.UI.Popup;
using NEG.UI.Window;
using NegUtils.NEG.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using NegUtils;
using UnityEngine.Pool;
using Debug = UnityEngine.Debug;
namespace NEG.UI
{
[PublicAPI]
public abstract class UiManager<TWindow, TPopup> : IDisposable, IPopupsHandler where TWindow : Enum where TPopup : Enum
{
public static UiManager<TWindow, TPopup> Instance { get; protected set; }
/// <summary>
/// Current area shown on screen.
/// </summary>
public IArea CurrentArea
{
get => currentArea;
set
{
currentArea?.Close();
currentArea = value;
currentArea?.Open();
}
}
/// <summary>
/// Current window that is considered main (focused, lastly opened). Can be null.
/// </summary>
// public IWindow CurrentMainWindow => mainWindows.LastOrDefault();
// public PopupData CurrentPopup => currentShownPopup.Data;
private IArea currentArea;
private PopupDataExtended currentShownPopupData;
private IPopup currentShownPopup;
private readonly List<PopupDataExtended> popupsToShow = new();
protected abstract string OkLocalizationKey { get; }
protected abstract string YesLocalizationKey { get; }
protected abstract string NoLocalizationKey { get; }
protected abstract TPopup DefaultPopup { get; }
protected UiManager(IArea startArea)
{
if (Instance != null)
{
Debug.LogError("Only one instance od UiManager is allowed");
return;
}
Instance = this;
IPopupsHandler.instance = this;
CurrentArea = startArea;
}
public virtual void Dispose() => Instance = null;
#region Popups
private Dictionary<TPopup, IPopup> registeredPopups = new();
/// <summary>
/// Show popup if there is non other currently shown. Otherwise add current popup to ordered queue and show it later.
/// It will be closed after pressing ok button.
/// </summary>
/// <param name="title">popup title key</param>
/// <param name="content">popup content key</param>
/// <param name="okText">text to show on ok button, empty for localized "Ok"</param>
/// <param name="okPressed">additional action on ok pressed</param>
/// <param name="priority">priority of popup (lower number -> show first)</param>
/// <param name="forceShow">force show current popup only if currently shown has lower priority</param>
/// <returns>data for created popup, can be used to invalidate popup (will not show)</returns>
public PopupDataHook ShowOkPopup(string title, string content, string okText = null, Action okPressed = null,
int priority = 0, bool forceShow = false)
{
var data = NativePool<DefaultPopupData>.Get();
data.Title = title;
data.Content = content;
data.Options.Add((okText ?? OkLocalizationKey, okPressed));
return ShowPopup(DefaultPopup, data, priority, forceShow);
}
/// <summary>
/// Show popup if there is non other currently shown. Otherwise add current popup to ordered queue and show it later.
/// It will be closed after pressing yes or no button.
/// </summary>
/// <param name="title">popup title key</param>
/// <param name="content">popup content key</param>
/// <param name="yesText">text to show on yes button, empty for localized "Yes"</param>
/// <param name="noText">text to show on no button, empty for localized "No"</param>
/// <param name="yesPressed">additional action on yes pressed</param>
/// <param name="noPressed">additional action on no pressed</param>
/// <param name="priority">priority of popup (lower number -> show first)</param>
/// <param name="forceShow">force show current popup only if currently shown has lower priority</param>
/// <returns>data for created popup, can be used to invalidate popup (will not show)</returns>
public PopupDataHook ShowYesNoPopup(string title, string content, string yesText = null, string noText = null,
Action yesPressed = null, Action noPressed = null, int priority = 0, bool forceShow = false)
{
var data = NativePool<DefaultPopupData>.Get();
data.Title = title;
data.Content = content;
data.Options.Add((yesText ?? YesLocalizationKey, yesPressed));
data.Options.Add((noText ?? NoLocalizationKey, noPressed));
return ShowPopup(DefaultPopup, data, priority, forceShow);
}
/// <summary>
/// Show popup if there is non other currently shown. Otherwise add current popup to ordered queue and show it later.
/// It will be closed after pressing any button.
/// </summary>
/// <param name="title">popup title key</param>
/// <param name="content">popup content key</param>
/// <param name="actions">list of actions</param>
/// <param name="priority">priority of popup (lower number -> show first)</param>
/// <param name="forceShow">force show current popup only if currently shown has lower priority</param>
/// <returns>data for created popup, can be used to invalidate popup (will not show)</returns>
public PopupDataHook ShowPopup(string title, string content, ReadOnlySpan<(string, Action)> actions, int priority = 0, bool forceShow = false)
{
var data = NativePool<DefaultPopupData>.Get();
data.Title = title;
data.Content = content;
foreach (var action in actions)
{
data.Options.Add(action);
}
return ShowPopup(DefaultPopup, data, priority, forceShow);
}
/// <summary>
/// Show popup if there is non other currently shown. Otherwise add current popup to ordered queue and show it later.
/// </summary>
/// <param name="popupType">popup type to show</param>
/// <param name="data">popup data object</param>
/// <param name="priority">priority of popup (lower number -> show first)</param>
/// <param name="forceShow">force show current popup only if currently shown has lower priority</param>
public PopupDataHook ShowPopup(TPopup popupType, PopupData data, int priority = 0, bool forceShow = false)
{
var dataExtended = new PopupDataExtended(priority, popupType, data);
if (forceShow)
{
if (currentShownPopupData.Data != null && currentShownPopupData.Priority >= priority)
{
EnqueuePopup(dataExtended);
return new PopupDataHook(data);
}
if (currentShownPopupData.Data != null)
EnqueuePopup(currentShownPopupData, false);
ShowPopup(dataExtended);
return new PopupDataHook(data);
}
popupsToShow.Add(dataExtended);
RefreshPopups();
return new PopupDataHook(data);
}
public void RegisterPopup(TPopup popupType, IPopup popup)
{
registeredPopups.Add(popupType, popup);
}
public void RefreshPopups()
{
for (int i = popupsToShow.Count - 1; i >= 0; i--)
{
if(!popupsToShow[i].Data.IsValid)
popupsToShow.RemoveAt(i);
}
if(currentShownPopupData.Data?.IsValid == true)
return;
ClosePopupSilently();
if (popupsToShow.Count == 0)
return;
var data = popupsToShow[0];
popupsToShow.RemoveAt(0);
ShowPopup(data);
}
private void ShowPopup(PopupDataExtended data)
{
if (currentShownPopup != null)
ClosePopupSilently();
if (!registeredPopups.TryGetValue(data.PopupType, out var popup))
{
Debug.LogError($"Popup {data.PopupType} does not exist");
data.Data.Dispose();
return;
}
popup.Show(data.Data);
currentShownPopup = popup;
currentShownPopupData = data;
}
private void ClosePopupSilently()
{
currentShownPopup?.Close(true);
currentShownPopupData = default;
currentShownPopup = null;
}
private void EnqueuePopup(PopupDataExtended data, bool asLast = true)
{
for (int i = 0; i < popupsToShow.Count; i++)
{
var popup = popupsToShow[i];
if(popup.Priority > data.Priority || (asLast && popup.Priority == data.Priority))
continue;
popupsToShow.Insert(i, data);
return;
}
popupsToShow.Add(data);
}
private readonly struct PopupDataExtended
{
public readonly int Priority;
public readonly TPopup PopupType;
public readonly PopupData Data;
public PopupDataExtended(int priority, TPopup popupType, PopupData data)
{
Priority = priority;
PopupType = popupType;
Data = data;
}
}
#endregion
public void UseBack()
{
//Remove this ugly alocation
IControllable.BackUsed backUsed = new();
//CurrentMainWindow?.TryUseBack(ref backUsed);
if(backUsed.Used)
return;
CurrentArea.TryUseBack(ref backUsed);
}
#region Windows
private Dictionary<TWindow, IWindow> registeredWindows = new();
#endregion
// public void SetMainWindow(IWindow window) => mainWindows.Add(window);
//
// public void MainWindowClosed(IWindow window) => mainWindows.Remove(window);
//
// public void OnWindowClosed(IWindow window) => MainWindowClosed(window);
// //TODO: select new main window
// protected void PopupClosed(PopupData data)
// {
// if (currentShownPopup.data != data)
// {
// //Debug.LogError("Popup was not shown");
// return;
// }
// UpdatePopupsState(false);
// }
}
}