-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: BREAKING CHANGE: Nuclear Windows Manager
- Loading branch information
Showing
108 changed files
with
2,976 additions
and
2,398 deletions.
There are no files selected for viewing
3 changes: 3 additions & 0 deletions
3
Assets/com.nuclearband.windowsmanager/Runtime/AdditionalUtilities.meta
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
41 changes: 41 additions & 0 deletions
41
Assets/com.nuclearband.windowsmanager/Runtime/AdditionalUtilities/StaticWindowsManager.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
#nullable enable | ||
using System; | ||
using UnityEngine; | ||
|
||
namespace Nuclear.WindowsManager | ||
{ | ||
public static class StaticWindowsManager | ||
{ | ||
private static IWindowsManager? _instance; | ||
|
||
public static void Init(WindowsManagerSettings settings) | ||
{ | ||
if (_instance != null) | ||
{ | ||
Debug.LogError("WindowsManager already initialized"); | ||
return; | ||
} | ||
|
||
_instance = new WindowsManager(settings); | ||
} | ||
|
||
public static Window CreateWindow(string path, Action<Window>? setupWindow = null) => | ||
_instance != null | ||
? _instance.CreateWindow(path, setupWindow) | ||
: throw new ArgumentException("WindowsManager not initialized"); | ||
|
||
public static void PrefetchWindow(string path) | ||
{ | ||
if (_instance != null) | ||
_instance.PrefetchWindow(path); | ||
else | ||
throw new ArgumentException("WindowsManager not initialized"); | ||
} | ||
|
||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] | ||
private static void Reset() | ||
{ | ||
_instance = null!; | ||
} | ||
} | ||
} |
3 changes: 3 additions & 0 deletions
3
...s/com.nuclearband.windowsmanager/Runtime/AdditionalUtilities/StaticWindowsManager.cs.meta
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
3 changes: 3 additions & 0 deletions
3
Assets/com.nuclearband.windowsmanager/Runtime/Implementation.meta
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
17 changes: 17 additions & 0 deletions
17
Assets/com.nuclearband.windowsmanager/Runtime/Implementation/BackButtonEventManager.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
#nullable enable | ||
using System; | ||
using UnityEngine; | ||
|
||
namespace NuclearBand | ||
{ | ||
public class BackButtonEventManager : MonoBehaviour | ||
{ | ||
public event Action OnBackButtonPressed = delegate { }; | ||
|
||
private void Update() | ||
{ | ||
if(Input.GetKeyDown(KeyCode.Escape) ) | ||
OnBackButtonPressed.Invoke(); | ||
} | ||
} | ||
} |
File renamed without changes.
112 changes: 112 additions & 0 deletions
112
Assets/com.nuclearband.windowsmanager/Runtime/Implementation/Window.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
#nullable enable | ||
using System; | ||
using UnityEngine; | ||
|
||
namespace Nuclear.WindowsManager | ||
{ | ||
public class Window : MonoBehaviour | ||
{ | ||
public event Action<Window> OnShown = delegate { }; | ||
public event Action<Window> OnHidden = delegate { }; | ||
public event Action<Window> OnStartShow = delegate { }; | ||
public event Action<Window> OnStartHide = delegate { }; | ||
|
||
private enum WindowState | ||
{ | ||
Opened, Closed, Opening, Closing | ||
} | ||
|
||
public bool WithInputBlockForBackground; | ||
public bool DestroyOnClose; | ||
public bool ProcessBackButton = true; | ||
|
||
[SerializeField] private WindowAnimation? _showAnimation; | ||
[SerializeField] private WindowAnimation? _hideAnimation; | ||
|
||
private WindowState _windowState = WindowState.Closed; | ||
|
||
private GameObject? _inputBlock; | ||
private Action? _customBackButtonAction; | ||
private Action _customDeInitActions = delegate { }; | ||
|
||
public virtual void Init() | ||
{ | ||
if (_showAnimation != null) | ||
_showAnimation.OnEnd += EndShowAnimationCallback; | ||
|
||
if (_hideAnimation != null) | ||
_hideAnimation.OnEnd += CloseInternal; | ||
} | ||
|
||
public void Show(bool immediate = false) | ||
{ | ||
OnStartShow.Invoke(this); | ||
_windowState = WindowState.Opening; | ||
if (_showAnimation != null && !immediate) | ||
_showAnimation.Play(); | ||
else | ||
EndShowAnimationCallback(); | ||
} | ||
|
||
public void Close() | ||
{ | ||
_windowState = WindowState.Closing; | ||
OnStartHide.Invoke(this); | ||
if (_hideAnimation != null) | ||
_hideAnimation.Play(); | ||
else | ||
CloseInternal(); | ||
} | ||
|
||
public void ProcessBackButtonPress() | ||
{ | ||
if (_windowState == WindowState.Closing) | ||
return; | ||
|
||
if (_customBackButtonAction != null) | ||
_customBackButtonAction.Invoke(); | ||
else | ||
Close(); | ||
} | ||
|
||
protected void SetCustomBackButtonAction(Action? action) => | ||
_customBackButtonAction = action; | ||
|
||
public void AddDeInitAction(Action action) => | ||
_customDeInitActions += action; | ||
|
||
private void EndShowAnimationCallback() | ||
{ | ||
OnShown.Invoke(this); | ||
_windowState = WindowState.Opened; | ||
} | ||
|
||
private void CloseInternal() | ||
{ | ||
_windowState = WindowState.Closed; | ||
var onHidden = OnHidden; | ||
DeInit(); | ||
if (DestroyOnClose) | ||
Destroy(gameObject); | ||
else | ||
gameObject.SetActive(false); | ||
onHidden(this); | ||
} | ||
|
||
private void DeInit() | ||
{ | ||
OnStartShow = delegate { }; | ||
OnShown = delegate { }; | ||
OnStartHide = delegate { }; | ||
OnHidden = delegate { }; | ||
if (_showAnimation != null) | ||
_showAnimation.OnEnd -= EndShowAnimationCallback; | ||
|
||
if (_hideAnimation != null) | ||
_hideAnimation.OnEnd -= CloseInternal; | ||
|
||
_customDeInitActions.Invoke(); | ||
_customDeInitActions = delegate { }; | ||
} | ||
} | ||
} |
File renamed without changes.
14 changes: 14 additions & 0 deletions
14
Assets/com.nuclearband.windowsmanager/Runtime/Implementation/WindowAnimation.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using System; | ||
using UnityEngine; | ||
|
||
namespace Nuclear.WindowsManager | ||
{ | ||
public abstract class WindowAnimation : MonoBehaviour | ||
{ | ||
public event Action OnEnd = delegate { }; | ||
|
||
public abstract void Play(); | ||
|
||
public void End() => OnEnd.Invoke(); | ||
} | ||
} |
3 changes: 3 additions & 0 deletions
3
Assets/com.nuclearband.windowsmanager/Runtime/Implementation/WindowAnimation.cs.meta
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
186 changes: 186 additions & 0 deletions
186
Assets/com.nuclearband.windowsmanager/Runtime/Implementation/WindowsManager.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
#nullable enable | ||
using UnityEngine; | ||
using System.Collections.Generic; | ||
using System; | ||
using System.Collections.ObjectModel; | ||
using NuclearBand; | ||
using Object = UnityEngine.Object; | ||
|
||
namespace Nuclear.WindowsManager | ||
{ | ||
public class WindowsManager : IWindowsManager | ||
{ | ||
public event Action<Window> OnWindowCreated = delegate { }; | ||
public event Action<Window> OnWindowClosed = delegate { }; | ||
|
||
private readonly ReadOnlyDictionary<string, Func<bool>> _suffixesWithPredicates; | ||
private readonly Transform _root ; | ||
private readonly GameObject _inputBlockPrefab; | ||
|
||
private readonly List<Window> _windows = new(); | ||
private readonly Dictionary<string, GameObject> _loadedWindowPrefabs = new(); | ||
private readonly Dictionary<string, List<Window>> _invisibleWindows = new(); | ||
|
||
public WindowsManager(WindowsManagerSettings settings) | ||
{ | ||
_suffixesWithPredicates = settings.SuffixesWithPredicates; | ||
|
||
_inputBlockPrefab = Resources.Load<GameObject>(settings.InputBlockPath) ?? | ||
throw new ArgumentException("WindowsManager: Wrong path to InputBlock"); | ||
|
||
var rootPrefab = Resources.Load<GameObject>(settings.RootPath) ?? | ||
throw new ArgumentException("WindowsManager: Wrong path to root"); | ||
|
||
_root = Object.Instantiate(rootPrefab).transform; | ||
_root.name = _root.name.Replace("(Clone)", string.Empty); | ||
var backButtonEventManager = _root.gameObject.AddComponent<BackButtonEventManager>(); | ||
backButtonEventManager.OnBackButtonPressed += OnBackButtonPressedCallback; | ||
Object.DontDestroyOnLoad(_root.gameObject); | ||
} | ||
|
||
Window IWindowsManager.CreateWindow(string path, Action<Window>? setupWindow) | ||
{ | ||
var suffix = FindSuffixFor(path); | ||
var window = InstantiateWindow(path + suffix); | ||
|
||
_windows.Add(window); | ||
setupWindow?.Invoke(window); | ||
window.OnHidden += WindowOnHidden; | ||
InstantiateInputBlockIfNeeded(window); | ||
window.Init(); | ||
|
||
window.Show(); | ||
OnWindowCreated(window); | ||
return window; | ||
} | ||
|
||
private void InstantiateInputBlockIfNeeded(Window window) | ||
{ | ||
if (!window.WithInputBlockForBackground) | ||
return; | ||
|
||
var inputBlock = Object.Instantiate(_inputBlockPrefab, window.transform); | ||
inputBlock.name = inputBlock.name.Replace("(Clone)", string.Empty); | ||
inputBlock.transform.SetAsFirstSibling(); | ||
} | ||
|
||
private void DestroyInputBlockIfNotNeeded(Window window) | ||
{ | ||
if (!window.WithInputBlockForBackground) | ||
return; | ||
|
||
var inputBlock = window.transform.GetChild(0); | ||
Object.Destroy(inputBlock.gameObject); | ||
} | ||
|
||
void IWindowsManager.PrefetchWindow(string path) | ||
{ | ||
var suffix = FindSuffixFor(path); | ||
var window = InstantiateWindow(path + suffix); | ||
window.gameObject.SetActive(false); | ||
} | ||
|
||
private Window InstantiateWindow(string fullWindowPath) | ||
{ | ||
Window window; | ||
var windowName = fullWindowPath.Split('/')[^1]; | ||
if (_invisibleWindows.ContainsKey(windowName)) | ||
{ | ||
var windowList = _invisibleWindows[windowName]; | ||
if (windowList.Count != 0) | ||
{ | ||
window = windowList[^1]; | ||
window.gameObject.SetActive(true); | ||
window.transform.SetAsLastSibling(); | ||
windowList.RemoveAt(windowList.Count - 1); | ||
return window; | ||
} | ||
} | ||
|
||
var windowPrefab = GetWindowPrefab(fullWindowPath); | ||
|
||
window = Object.Instantiate(windowPrefab, _root).GetComponent<Window>() ?? | ||
throw new ArgumentException($"WindowsManager: missing Window script on window {fullWindowPath}"); | ||
|
||
window.name = window.name.Replace("(Clone)", string.Empty); | ||
|
||
return window; | ||
} | ||
|
||
private void OnBackButtonPressedCallback() | ||
{ | ||
for (var i = _windows.Count - 1; i >= 0; --i) | ||
{ | ||
var curWindow = _windows[i]; | ||
if (!curWindow.ProcessBackButton) | ||
continue; | ||
curWindow.ProcessBackButtonPress(); | ||
break; | ||
} | ||
} | ||
|
||
private string FindSuffixFor(string windowPath) | ||
{ | ||
foreach (var (suffix, predicate) in _suffixesWithPredicates) | ||
{ | ||
if (!predicate.Invoke()) | ||
continue; | ||
|
||
var windowName = windowPath.Split('/')[^1]; | ||
if (_invisibleWindows.ContainsKey(windowName + suffix)) | ||
return suffix; | ||
|
||
if (CheckWindowPrefabExistence(windowPath + suffix)) | ||
return suffix; | ||
} | ||
|
||
return string.Empty; | ||
} | ||
|
||
private bool CheckWindowPrefabExistence(string path) | ||
{ | ||
if (_loadedWindowPrefabs.ContainsKey(path)) | ||
return true; | ||
return Resources.Load<GameObject>(path) != null; | ||
} | ||
|
||
private GameObject GetWindowPrefab(string path) | ||
{ | ||
GameObject windowPrefab; | ||
if (_loadedWindowPrefabs.ContainsKey(path)) | ||
windowPrefab = _loadedWindowPrefabs[path]; | ||
else | ||
{ | ||
windowPrefab = Resources.Load<GameObject>(path) ?? | ||
throw new ArgumentException($"Can't load prefab with such path {path}"); | ||
|
||
_loadedWindowPrefabs.Add(path, windowPrefab); | ||
} | ||
|
||
return windowPrefab; | ||
} | ||
|
||
private void WindowOnHidden(Window window) | ||
{ | ||
window.OnHidden -= WindowOnHidden; | ||
|
||
for (var i = 0; i < _windows.Count; ++i) | ||
{ | ||
if (_windows[i] != window) | ||
continue; | ||
if (!window.DestroyOnClose) | ||
{ | ||
DestroyInputBlockIfNotNeeded(window); | ||
if (!_invisibleWindows.ContainsKey(window.name)) | ||
_invisibleWindows.Add(window.name, new List<Window> {window}); | ||
else | ||
_invisibleWindows[window.name].Add(window); | ||
} | ||
|
||
_windows.RemoveAt(i); | ||
OnWindowClosed.Invoke(window); | ||
return; | ||
} | ||
} | ||
} | ||
} |
File renamed without changes.
Oops, something went wrong.