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 : IDisposable, IPopupsHandler where TWindow : Enum where TPopup : Enum { public static UiManager Instance { get; protected set; } /// /// Current area shown on screen. /// public IArea CurrentArea { get => currentArea; set { currentArea?.Close(); currentArea = value; currentArea?.Open(); } } /// /// Current window that is considered main (focused, lastly opened). Can be null. /// // public IWindow CurrentMainWindow => mainWindows.LastOrDefault(); // public PopupData CurrentPopup => currentShownPopup.Data; private IArea currentArea; private PopupDataExtended currentShownPopupData; private IPopup currentShownPopup; private readonly List 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 registeredPopups = new(); /// /// 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. /// /// popup title key /// popup content key /// text to show on ok button, empty for localized "Ok" /// additional action on ok pressed /// priority of popup (lower number -> show first) /// force show current popup only if currently shown has lower priority /// data for created popup, can be used to invalidate popup (will not show) public PopupDataHook ShowOkPopup(string title, string content, string okText = null, Action okPressed = null, int priority = 0, bool forceShow = false) { var data = NativePool.Get(); data.Title = title; data.Content = content; data.Options.Add((okText ?? OkLocalizationKey, okPressed)); return ShowPopup(DefaultPopup, data, priority, forceShow); } /// /// 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. /// /// popup title key /// popup content key /// text to show on yes button, empty for localized "Yes" /// text to show on no button, empty for localized "No" /// additional action on yes pressed /// additional action on no pressed /// priority of popup (lower number -> show first) /// force show current popup only if currently shown has lower priority /// data for created popup, can be used to invalidate popup (will not show) 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.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); } /// /// 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. /// /// popup title key /// popup content key /// list of actions /// priority of popup (lower number -> show first) /// force show current popup only if currently shown has lower priority /// data for created popup, can be used to invalidate popup (will not show) public PopupDataHook ShowPopup(string title, string content, ReadOnlySpan<(string, Action)> actions, int priority = 0, bool forceShow = false) { var data = NativePool.Get(); data.Title = title; data.Content = content; foreach (var action in actions) { data.Options.Add(action); } return ShowPopup(DefaultPopup, data, priority, forceShow); } /// /// Show popup if there is non other currently shown. Otherwise add current popup to ordered queue and show it later. /// /// popup type to show /// popup data object /// priority of popup (lower number -> show first) /// force show current popup only if currently shown has lower priority 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 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); // } } }