using UnityEngine; using DuloGames.UI.Tweens; using UnityEngine.Events; using UnityEngine.EventSystems; using System; using System.Collections.Generic; namespace DuloGames.UI { [DisallowMultipleComponent, ExecuteInEditMode, AddComponentMenu("UI/Window", 58), RequireComponent(typeof(CanvasGroup))] public class UIWindow : MonoBehaviour, IEventSystemHandler, ISelectHandler, IPointerDownHandler { public enum Transition { Instant, Fade } public enum VisualState { Shown, Hidden } public enum EscapeKeyAction { None, Hide, HideIfFocused, Toggle } [Serializable] public class TransitionBeginEvent : UnityEvent {} [Serializable] public class TransitionCompleteEvent : UnityEvent {} protected static UIWindow m_FucusedWindow; public static UIWindow FocusedWindow { get { return m_FucusedWindow; } private set { m_FucusedWindow = value; } } [SerializeField] private UIWindowID m_WindowId = UIWindowID.None; [SerializeField] private int m_CustomWindowId = 0; [SerializeField] private VisualState m_StartingState = VisualState.Hidden; [SerializeField] private EscapeKeyAction m_EscapeKeyAction = EscapeKeyAction.Hide; [SerializeField] private bool m_UseBlackOverlay = false; [SerializeField] private bool m_FocusAllowReparent = true; [SerializeField] private Transition m_Transition = Transition.Instant; [SerializeField] private TweenEasing m_TransitionEasing = TweenEasing.InOutQuint; [SerializeField] private float m_TransitionDuration = 0.1f; protected bool m_IsFocused = false; private VisualState m_CurrentVisualState = VisualState.Hidden; private CanvasGroup m_CanvasGroup; /// /// Gets or sets the window identifier. /// /// The window identifier. public UIWindowID ID { get { return this.m_WindowId; } set { this.m_WindowId = value; } } /// /// Gets or sets the custom window identifier. /// /// The custom window identifier. public int CustomID { get { return this.m_CustomWindowId; } set { this.m_CustomWindowId = value; } } /// /// Gets or sets the escape key action. /// public EscapeKeyAction escapeKeyAction { get { return this.m_EscapeKeyAction; } set { this.m_EscapeKeyAction = value; } } /// /// Gets or sets a value indicating whether this window should use the black overlay. /// public bool useBlackOverlay { get { return this.m_UseBlackOverlay; } set { this.m_UseBlackOverlay = value; if (Application.isPlaying && this.m_UseBlackOverlay && this.isActiveAndEnabled) { UIBlackOverlay overlay = UIBlackOverlay.GetOverlay(this.gameObject); if (overlay != null) { if (value) this.onTransitionBegin.AddListener(overlay.OnTransitionBegin); else this.onTransitionBegin.RemoveListener(overlay.OnTransitionBegin); } } } } /// /// Allow re-parenting on focus. /// public bool focusAllowReparent { get { return this.m_FocusAllowReparent; } set { this.m_FocusAllowReparent = value; } } /// /// Gets or sets the transition. /// /// The transition. public Transition transition { get { return this.m_Transition; } set { this.m_Transition = value; } } /// /// Gets or sets the transition easing. /// /// The transition easing. public TweenEasing transitionEasing { get { return this.m_TransitionEasing; } set { this.m_TransitionEasing = value; } } /// /// Gets or sets the duration of the transition. /// /// The duration of the transition. public float transitionDuration { get { return this.m_TransitionDuration; } set { this.m_TransitionDuration = value; } } /// /// The transition begin (invoked when a transition begins). /// public TransitionBeginEvent onTransitionBegin = new TransitionBeginEvent(); /// /// The transition complete event (invoked when a transition completes). /// public TransitionCompleteEvent onTransitionComplete = new TransitionCompleteEvent(); /// /// Gets a value indicating whether this window is visible. /// /// true if this instance is visible; otherwise, false. public bool IsVisible { get { return (this.m_CanvasGroup != null && this.m_CanvasGroup.alpha > 0f) ? true : false; } } /// /// Gets a value indicating whether this window is open. /// /// true if this instance is open; otherwise, false. public bool IsOpen { get { return (this.m_CurrentVisualState == VisualState.Shown); } } /// /// Gets a value indicating whether this instance is focused. /// /// true if this instance is focused; otherwise, false. public bool IsFocused { get { return this.m_IsFocused; } } // Tween controls [NonSerialized] private readonly TweenRunner m_FloatTweenRunner; // Called by Unity prior to deserialization, // should not be called by users protected UIWindow() { if (this.m_FloatTweenRunner == null) this.m_FloatTweenRunner = new TweenRunner(); this.m_FloatTweenRunner.Init(this); } protected virtual void Awake() { // Get the canvas group this.m_CanvasGroup = this.gameObject.GetComponent(); // Transition to the starting state if (Application.isPlaying) this.ApplyVisualState(this.m_StartingState); } protected virtual void Start() { // Assign new custom ID if (this.CustomID == 0) this.CustomID = UIWindow.NextUnusedCustomID; // Make sure we have a window manager in the scene if required if (this.m_EscapeKeyAction != EscapeKeyAction.None) { UIWindowManager manager = Component.FindObjectOfType(); // Add a manager if not present if (manager == null) { GameObject newObj = new GameObject("Window Manager"); newObj.AddComponent(); newObj.transform.SetAsFirstSibling(); } } } protected virtual void OnEnable() { if (Application.isPlaying && this.m_UseBlackOverlay) { UIBlackOverlay overlay = UIBlackOverlay.GetOverlay(this.gameObject); if (overlay != null) this.onTransitionBegin.AddListener(overlay.OnTransitionBegin); } } protected virtual void OnDisable() { if (Application.isPlaying && this.m_UseBlackOverlay) { UIBlackOverlay overlay = UIBlackOverlay.GetOverlay(this.gameObject); if (overlay != null) this.onTransitionBegin.RemoveListener(overlay.OnTransitionBegin); } } #if UNITY_EDITOR protected virtual void OnValidate() { this.m_TransitionDuration = Mathf.Max(this.m_TransitionDuration, 0f); } #endif /// /// Determines whether this window is active. /// /// true if this instance is active; otherwise, false. protected virtual bool IsActive() { return (this.enabled && this.gameObject.activeInHierarchy); } /// /// Raises the select event. /// /// Event data. public virtual void OnSelect(BaseEventData eventData) { // Focus the window this.Focus(); } /// /// Raises the pointer down event. /// /// Event data. public virtual void OnPointerDown(PointerEventData eventData) { // Focus the window this.Focus(); } /// /// Focuses this window. /// public virtual void Focus() { if (this.m_IsFocused) return; // Flag as focused this.m_IsFocused = true; // Call the static on focused window UIWindow.OnBeforeFocusWindow(this); // Bring the window forward this.BringToFront(); } /// /// Brings the window to the front. /// public void BringToFront() { UIUtility.BringToFront(this.gameObject, this.m_FocusAllowReparent); } /// /// Toggle the window Show/Hide. /// public virtual void Toggle() { if (this.m_CurrentVisualState == VisualState.Shown) this.Hide(); else this.Show(); } /// /// Show the window. /// public virtual void Show() { this.Show(false); } /// /// Show the window. /// /// If set to true instant. public virtual void Show(bool instant) { if (!this.IsActive()) return; // Focus the window this.Focus(); // Check if the window is already shown if (this.m_CurrentVisualState == VisualState.Shown) return; // Transition this.EvaluateAndTransitionToVisualState(VisualState.Shown, instant); } /// /// Hide the window. /// public virtual void Hide() { this.Hide(false); } /// /// Hide the window. /// /// If set to true instant. public virtual void Hide(bool instant) { if (!this.IsActive()) return; // Check if the window is already hidden if (this.m_CurrentVisualState == VisualState.Hidden) return; // Transition this.EvaluateAndTransitionToVisualState(VisualState.Hidden, instant); } /// /// Evaluates and transitions to the specified visual state. /// /// The state to transition to. /// If set to true instant. protected virtual void EvaluateAndTransitionToVisualState(VisualState state, bool instant) { float targetAlpha = (state == VisualState.Shown) ? 1f : 0f; // Call the transition begin event if (this.onTransitionBegin != null) this.onTransitionBegin.Invoke(this, state, (instant || this.m_Transition == Transition.Instant)); // Do the transition if (this.m_Transition == Transition.Fade) { float duration = (instant) ? 0f : this.m_TransitionDuration; // Tween the alpha this.StartAlphaTween(targetAlpha, duration, true); } else { // Set the alpha directly this.SetCanvasAlpha(targetAlpha); // Call the transition complete event, since it's instant if (this.onTransitionComplete != null) this.onTransitionComplete.Invoke(this, state); } // Save the state this.m_CurrentVisualState = state; // If we are transitioning to show, enable the canvas group raycast blocking if (state == VisualState.Shown) { this.m_CanvasGroup.blocksRaycasts = true; //this.m_CanvasGroup.interactable = true; } } /// /// Instantly applies the visual state. /// /// The state to transition to. public virtual void ApplyVisualState(VisualState state) { float targetAlpha = (state == VisualState.Shown) ? 1f : 0f; // Set the alpha directly this.SetCanvasAlpha(targetAlpha); // Save the state this.m_CurrentVisualState = state; // If we are transitioning to show, enable the canvas group raycast blocking if (state == VisualState.Shown) { this.m_CanvasGroup.blocksRaycasts = true; //this.m_CanvasGroup.interactable = true; } else { this.m_CanvasGroup.blocksRaycasts = false; //this.m_CanvasGroup.interactable = false; } } /// /// Starts alpha tween. /// /// Target alpha. /// Duration. /// If set to true ignore time scale. public void StartAlphaTween(float targetAlpha, float duration, bool ignoreTimeScale) { if (this.m_CanvasGroup == null) return; var floatTween = new FloatTween { duration = duration, startFloat = this.m_CanvasGroup.alpha, targetFloat = targetAlpha }; floatTween.AddOnChangedCallback(SetCanvasAlpha); floatTween.AddOnFinishCallback(OnTweenFinished); floatTween.ignoreTimeScale = ignoreTimeScale; floatTween.easing = this.m_TransitionEasing; this.m_FloatTweenRunner.StartTween(floatTween); } /// /// Sets the canvas alpha. /// /// Alpha. public void SetCanvasAlpha(float alpha) { if (this.m_CanvasGroup == null) return; // Set the alpha this.m_CanvasGroup.alpha = alpha; // If the alpha is zero, disable block raycasts // Enabling them back on is done in the transition method if (alpha == 0f) { this.m_CanvasGroup.blocksRaycasts = false; //this.m_CanvasGroup.interactable = false; } } /// /// Raises the list tween finished event. /// protected virtual void OnTweenFinished() { // Call the transition complete event if (this.onTransitionComplete != null) this.onTransitionComplete.Invoke(this, this.m_CurrentVisualState); } #region Static Methods /// /// Get all the windows in the scene (Including inactive). /// /// The windows. public static List GetWindows() { List windows = new List(); UIWindow[] ws = Resources.FindObjectsOfTypeAll(); foreach (UIWindow w in ws) { // Check if the window is active in the hierarchy if (w.gameObject.activeInHierarchy) windows.Add(w); } return windows; } public static int SortByCustomWindowID(UIWindow w1, UIWindow w2) { return w1.CustomID.CompareTo(w2.CustomID); } /// /// Gets the next unused custom ID for a window. /// /// The next unused ID. public static int NextUnusedCustomID { get { // Get the windows List windows = UIWindow.GetWindows(); if (GetWindows().Count > 0) { // Sort the windows by id windows.Sort(UIWindow.SortByCustomWindowID); // Return the last window id plus one return windows[windows.Count - 1].CustomID + 1; } // No windows, return 0 return 0; } } /// /// Gets the window with the given ID. /// /// The window. /// Identifier. public static UIWindow GetWindow(UIWindowID id) { // Get the windows and try finding the window with the given id foreach (UIWindow window in UIWindow.GetWindows()) if (window.ID == id) return window; return null; } /// /// Gets the window with the given custom ID. /// /// The window. /// The custom identifier. public static UIWindow GetWindowByCustomID(int customId) { // Get the windows and try finding the window with the given id foreach (UIWindow window in UIWindow.GetWindows()) if (window.CustomID == customId) return window; return null; } /// /// Focuses the window with the given ID. /// /// Identifier. public static void FocusWindow(UIWindowID id) { // Focus the window if (UIWindow.GetWindow(id) != null) UIWindow.GetWindow(id).Focus(); } /// /// Raises the before focus window event. /// /// The window. protected static void OnBeforeFocusWindow(UIWindow window) { if (m_FucusedWindow != null) m_FucusedWindow.m_IsFocused = false; m_FucusedWindow = window; } #endregion } }