447 lines
12 KiB
C#
447 lines
12 KiB
C#
|
using System;
|
|||
|
using System.Collections;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Linq;
|
|||
|
using Cysharp.Threading.Tasks;
|
|||
|
using UnityEngine;
|
|||
|
using UnityEngine.UI;
|
|||
|
|
|||
|
public class WindowsController : MonoBehaviour
|
|||
|
{
|
|||
|
[SerializeField] private Canvas _canvas = null;
|
|||
|
[SerializeField] private GameObject _windowContainer = null;
|
|||
|
|
|||
|
[Space]
|
|||
|
[Header("Background:")]
|
|||
|
[SerializeField] private RectTransform _background = null;
|
|||
|
[SerializeField] private BackgroundController _backgroundController = null;
|
|||
|
[SerializeField] private ClickHandlerMediator _backgroundClicker = null;
|
|||
|
|
|||
|
public IReadOnlyList<Window> GetVisibleStack() => _visibleWindows;
|
|||
|
|
|||
|
private List<Window> _visibleWindows = new List<Window>();
|
|||
|
private readonly Dictionary<Type, Window> _windows = new Dictionary<Type, Window>();
|
|||
|
private readonly List<Window> _hidingWindows = new List<Window>();
|
|||
|
private readonly LinkedList<Action> _windowQueue = new LinkedList<Action>();
|
|||
|
private readonly List<Window> _availableWindows = new List<Window>();
|
|||
|
|
|||
|
public Canvas Canvas => _canvas;
|
|||
|
|
|||
|
public void Init()
|
|||
|
{
|
|||
|
GetAvailableWindows();
|
|||
|
SetCanvasSettings();
|
|||
|
|
|||
|
_backgroundClicker.OnClick += TryCloseActiveWindow;
|
|||
|
_backgroundController.Init();
|
|||
|
}
|
|||
|
|
|||
|
private void GetAvailableWindows()
|
|||
|
{
|
|||
|
Window[] windows = Assets.LoadAll<Window>("UI/Windows");
|
|||
|
_availableWindows.AddRange(windows);
|
|||
|
}
|
|||
|
|
|||
|
private void SetCanvasSettings()
|
|||
|
{
|
|||
|
_canvas.planeDistance = 1f;
|
|||
|
_canvas.sortingOrder = 10;
|
|||
|
}
|
|||
|
|
|||
|
private void TryCloseActiveWindow()
|
|||
|
{
|
|||
|
var window = GetTopmostWindow();
|
|||
|
|
|||
|
if (window == null)
|
|||
|
return;
|
|||
|
|
|||
|
if (window.CanBeClosedByBackground && window.NeedShowBackground)
|
|||
|
window.Close();
|
|||
|
}
|
|||
|
|
|||
|
public T ShowWindow<T>() where T : ParameterlessWindow
|
|||
|
{
|
|||
|
var wnd = GetOrCreateWindow<T>();
|
|||
|
wnd.Show();
|
|||
|
return wnd;
|
|||
|
}
|
|||
|
|
|||
|
public T ShowWindow<T, A1>(A1 a1) where T : ParameterWindow<A1>
|
|||
|
{
|
|||
|
var wnd = GetOrCreateWindow<T>();
|
|||
|
wnd.Show(a1);
|
|||
|
|
|||
|
return wnd;
|
|||
|
}
|
|||
|
|
|||
|
public T ShowWindow<T, A1, A2>(A1 a1, A2 a2) where T : ParameterWindow<A1, A2>
|
|||
|
{
|
|||
|
var wnd = GetOrCreateWindow<T>();
|
|||
|
wnd.Show(a1, a2);
|
|||
|
return wnd;
|
|||
|
}
|
|||
|
|
|||
|
public bool IsWindowVisible<T>() where T : Window
|
|||
|
{
|
|||
|
return _visibleWindows.Find(x => x.GetType() == typeof(T)) != null;
|
|||
|
}
|
|||
|
|
|||
|
public bool HasAnyVisible()
|
|||
|
{
|
|||
|
return _visibleWindows.Count > 0;
|
|||
|
}
|
|||
|
|
|||
|
private void SortWindows()
|
|||
|
{
|
|||
|
if (IsUnsorted(_visibleWindows))
|
|||
|
{
|
|||
|
_visibleWindows = _visibleWindows.OrderByDescending(w => w.ZOrder).ToList();
|
|||
|
}
|
|||
|
|
|||
|
for (var i = 0; i < _visibleWindows.Count; i++)
|
|||
|
{
|
|||
|
_visibleWindows[i].transform.SetSiblingIndex(i + 1);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private static bool IsUnsorted(IList<Window> windows_list)
|
|||
|
{
|
|||
|
var prev_z = int.MinValue;
|
|||
|
for (var index = 0; index < windows_list.Count; index++)
|
|||
|
{
|
|||
|
var current_z = windows_list[index].ZOrder;
|
|||
|
if (current_z > prev_z)
|
|||
|
return true;
|
|||
|
prev_z = current_z;
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
private void OnWindowShow(Window wnd)
|
|||
|
{
|
|||
|
if (!_visibleWindows.Contains(wnd))
|
|||
|
_visibleWindows.Add(wnd);
|
|||
|
|
|||
|
SortWindows();
|
|||
|
Log.Debug($"window show ID={wnd}, sibling_index={wnd.transform.GetSiblingIndex()}");
|
|||
|
UpdateBackground(GetTopmostWindow());
|
|||
|
}
|
|||
|
|
|||
|
private void OnWindowHide(Window wnd)
|
|||
|
{
|
|||
|
_visibleWindows.Remove(wnd);
|
|||
|
_hidingWindows.Add(wnd);
|
|||
|
|
|||
|
wnd.OnShow += window => _hidingWindows.Remove(wnd); //для тех случаев, когда это же окно будет вновь открыто
|
|||
|
wnd.OnAfterClose += window => _hidingWindows.Remove(wnd);
|
|||
|
|
|||
|
Log.Debug($"window hide ID={wnd}, window_stack= {ReportWindowStack()}");
|
|||
|
|
|||
|
UpdateBackground(GetTopmostWindow());
|
|||
|
}
|
|||
|
|
|||
|
private void OnWindowClose(Window wnd)
|
|||
|
{
|
|||
|
_windows.Remove(wnd.GetType());
|
|||
|
_visibleWindows.Remove(wnd);
|
|||
|
}
|
|||
|
|
|||
|
private string ReportWindowStack()
|
|||
|
{
|
|||
|
var stack_report = "";
|
|||
|
foreach (var wnd in _visibleWindows)
|
|||
|
{
|
|||
|
if (stack_report != "")
|
|||
|
stack_report += ", ";
|
|||
|
stack_report += wnd;
|
|||
|
}
|
|||
|
|
|||
|
return stack_report == "" ? "EMPTY" : stack_report;
|
|||
|
}
|
|||
|
|
|||
|
private Window GetTopmostWindow()
|
|||
|
{
|
|||
|
if (_visibleWindows.Count == 0)
|
|||
|
return null;
|
|||
|
return Enumerable.Last(_visibleWindows);
|
|||
|
}
|
|||
|
|
|||
|
public string GetTopmostWindowName()
|
|||
|
{
|
|||
|
return GetTopmostWindow() != null ? GetTopmostWindow().name : null;
|
|||
|
}
|
|||
|
|
|||
|
private void UpdateBackground(Window wnd)
|
|||
|
{
|
|||
|
if (wnd == null && _windowQueue.Count > 0)
|
|||
|
return;
|
|||
|
|
|||
|
if (wnd == null || !wnd.NeedShowBackground)
|
|||
|
{
|
|||
|
HideBackground();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
PushBackgroundUnder(wnd);
|
|||
|
StretchBackground();
|
|||
|
ShowBackground();
|
|||
|
}
|
|||
|
|
|||
|
private void HideBackground()
|
|||
|
{
|
|||
|
_backgroundController.Hide();
|
|||
|
}
|
|||
|
|
|||
|
private void ShowBackground()
|
|||
|
{
|
|||
|
_backgroundController.Show();
|
|||
|
}
|
|||
|
|
|||
|
private void StretchBackground()
|
|||
|
{
|
|||
|
const float stretch_overshoot = 100f; //for IPhone X, for other 5f is enough
|
|||
|
_background.offsetMin = new Vector2(-stretch_overshoot, -stretch_overshoot);
|
|||
|
_background.offsetMax = new Vector2(stretch_overshoot, stretch_overshoot);
|
|||
|
}
|
|||
|
|
|||
|
private void PushBackgroundUnder(Window wnd)
|
|||
|
{
|
|||
|
_background.SetSiblingIndex(0);
|
|||
|
_background.SetSiblingIndex(Math.Max(0, wnd.transform.GetSiblingIndex() - 1));
|
|||
|
_background.anchoredPosition3D = Vector3.forward * wnd.ZOrder;
|
|||
|
}
|
|||
|
|
|||
|
public T GetOrCreateWindow<T>() where T : Window
|
|||
|
{
|
|||
|
var window_type = typeof(T);
|
|||
|
T window;
|
|||
|
|
|||
|
if (_windows.ContainsKey(window_type))
|
|||
|
window = _windows[window_type] as T;
|
|||
|
else
|
|||
|
{
|
|||
|
window = CreateWindow<T>();
|
|||
|
window.OnShow += OnWindowShow;
|
|||
|
window.OnHide += OnWindowHide;
|
|||
|
window.OnClose += OnWindowClose;
|
|||
|
window.OnAfterClose += OnAfterWindowClose;
|
|||
|
window.gameObject.SetActive(false);
|
|||
|
}
|
|||
|
|
|||
|
if (window == null)
|
|||
|
{
|
|||
|
Log.Error("Failed to create window TYPE={" + window_type + "}");
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
return window;
|
|||
|
}
|
|||
|
|
|||
|
private void OnAfterWindowClose(Window wnd)
|
|||
|
{
|
|||
|
if (_windowQueue.Count == 0 || GetTopmostWindow() != null)
|
|||
|
return;
|
|||
|
|
|||
|
var show_next_window = _windowQueue.First.Value;
|
|||
|
_windowQueue.RemoveFirst();
|
|||
|
show_next_window();
|
|||
|
}
|
|||
|
|
|||
|
private T CreateWindow<T>() where T : Window
|
|||
|
{
|
|||
|
var resource = _availableWindows.Find(w => w as T);
|
|||
|
var window = Assets.Create(resource as T);
|
|||
|
|
|||
|
var id = typeof(T);
|
|||
|
_windows.Add(id, window);
|
|||
|
window.gameObject.SetParent(_windowContainer.transform, false);
|
|||
|
window.name = id.Name;
|
|||
|
window.Init();
|
|||
|
|
|||
|
return window;
|
|||
|
}
|
|||
|
|
|||
|
public void CloseVisibleStack()
|
|||
|
{
|
|||
|
Window[] to_close = new Window[_visibleWindows.Count];
|
|||
|
_visibleWindows.CopyTo(to_close);
|
|||
|
|
|||
|
foreach (var wnd in to_close)
|
|||
|
{
|
|||
|
wnd.OnShow -= OnWindowShow;
|
|||
|
wnd.OnHide -= OnWindowHide;
|
|||
|
wnd.OnClose -= OnWindowClose;
|
|||
|
wnd.OnAfterClose -= OnAfterWindowClose;
|
|||
|
|
|||
|
wnd.Close();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void HideVisibleStack()
|
|||
|
{
|
|||
|
Window[] to_hide = new Window[_visibleWindows.Count];
|
|||
|
_visibleWindows.CopyTo(to_hide);
|
|||
|
|
|||
|
foreach (var wnd in to_hide)
|
|||
|
{
|
|||
|
wnd.Hide();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public T GetWindow<T>() where T : Window
|
|||
|
{
|
|||
|
var window = GetOr(_windows, typeof(T));
|
|||
|
return window as T;
|
|||
|
}
|
|||
|
public static V GetOr<K, V>(IDictionary<K, V> self, K key, V default_value = default(V))
|
|||
|
{
|
|||
|
V val;
|
|||
|
return self.TryGetValue(key, out val) ? val : default_value;
|
|||
|
}
|
|||
|
|
|||
|
public IEnumerator ShowWithDelay<T>(float delay = .2f) where T : ParameterlessWindow
|
|||
|
{
|
|||
|
var window = GetOrCreateWindow<T>();
|
|||
|
yield return new WaitForSeconds(delay);
|
|||
|
window.Show();
|
|||
|
}
|
|||
|
|
|||
|
public void CloseWindow<T>() where T : Window
|
|||
|
{
|
|||
|
var window = GetWindow<T>();
|
|||
|
if (window == null)
|
|||
|
return;
|
|||
|
|
|||
|
window.Close();
|
|||
|
}
|
|||
|
|
|||
|
public IEnumerator WaitForWindowClose<T>() where T : Window
|
|||
|
{
|
|||
|
var window = GetWindow<T>();
|
|||
|
if (window == null)
|
|||
|
yield break;
|
|||
|
|
|||
|
var is_closed = false;
|
|||
|
window.OnAfterClose += w => is_closed = true;
|
|||
|
yield return new WaitUntil(() => is_closed);
|
|||
|
}
|
|||
|
|
|||
|
public IEnumerator CloseWindowAndWait<T>() where T : Window
|
|||
|
{
|
|||
|
var window = GetWindow<T>();
|
|||
|
if (window == null)
|
|||
|
yield break;
|
|||
|
|
|||
|
window.Close();
|
|||
|
yield return WaitForWindowClose<T>();
|
|||
|
}
|
|||
|
|
|||
|
public IEnumerator WaitForWindow<T>() where T : Window
|
|||
|
{
|
|||
|
while (GetWindow<T>() == null)
|
|||
|
yield return null;
|
|||
|
}
|
|||
|
|
|||
|
public IEnumerator WaitForWindowThenWaitForClose<T>() where T : Window
|
|||
|
{
|
|||
|
yield return WaitForWindow<T>();
|
|||
|
|
|||
|
yield return WaitForWindowClose<T>();
|
|||
|
}
|
|||
|
|
|||
|
public IEnumerator WaitForWindowForTimeThenWaitForClose<T>(float time_seconds = 2) where T : Window
|
|||
|
{
|
|||
|
yield return WaitForWindowForTime<T>(time_seconds);
|
|||
|
|
|||
|
yield return WaitForWindowClose<T>();
|
|||
|
}
|
|||
|
|
|||
|
public IEnumerator WaitForWindowForTime<T>(float time_seconds = 2) where T : Window
|
|||
|
{
|
|||
|
var time = 0f;
|
|||
|
while (time < time_seconds && GetWindow<T>() == null)
|
|||
|
{
|
|||
|
time += Time.deltaTime;
|
|||
|
yield return null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void ShowAfter<T>() where T : ParameterlessWindow
|
|||
|
{
|
|||
|
if (HasAnyVisible())
|
|||
|
{
|
|||
|
_windowQueue.AddLast(() => ShowWindow<T>());
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
ShowWindow<T>();
|
|||
|
}
|
|||
|
|
|||
|
public void ShowAfter<T>(Action<T> action) where T : ParameterlessWindow
|
|||
|
{
|
|||
|
_windowQueue.AddLast(() =>
|
|||
|
{
|
|||
|
Debug.LogError("ShowAfter invoke");
|
|||
|
var window = GetOrCreateWindow<T>();
|
|||
|
action(window);
|
|||
|
window.Show();
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
public void ShowAfter<T, A1>(A1 a1) where T : ParameterWindow<A1>
|
|||
|
{
|
|||
|
if (HasAnyVisible())
|
|||
|
{
|
|||
|
_windowQueue.AddLast(() => ShowWindow<T, A1>(a1));
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
ShowWindow<T, A1>(a1);
|
|||
|
}
|
|||
|
|
|||
|
public void ShowAfter<T, A1, A2>(A1 a1, A2 a2) where T : ParameterWindow<A1, A2>
|
|||
|
{
|
|||
|
if (HasAnyVisible())
|
|||
|
{
|
|||
|
_windowQueue.AddLast(() => ShowWindow<T, A1, A2>(a1, a2));
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
ShowWindow<T, A1, A2>(a1, a2);
|
|||
|
}
|
|||
|
|
|||
|
public void ShowAfter<T, A1, A2>(A1 a1, A2 a2, Action<Window> on_close) where T : ParameterWindow<A1, A2>
|
|||
|
{
|
|||
|
Action show_window = () =>
|
|||
|
{
|
|||
|
var window = ShowWindow<T, A1, A2>(a1, a2);
|
|||
|
window.OnAfterClose += on_close;
|
|||
|
};
|
|||
|
|
|||
|
if (HasAnyVisible())
|
|||
|
{
|
|||
|
_windowQueue.AddLast(show_window);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
show_window();
|
|||
|
}
|
|||
|
|
|||
|
public bool ThereIsNoHidingWindows()
|
|||
|
{
|
|||
|
return _hidingWindows.Count == 0;
|
|||
|
}
|
|||
|
|
|||
|
public bool HasAnyOf(List<Type> check_types)
|
|||
|
{
|
|||
|
foreach (var window in _visibleWindows)
|
|||
|
{
|
|||
|
var index = check_types.IndexOf(window.GetType());
|
|||
|
if (index > -1)
|
|||
|
return true;
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|