using UnityEngine; using UnityEngine.UI; using System.Collections; using System.Collections.Generic; using System; using System.Linq; namespace UIWidgets { /// /// Notify. /// Manage notifications. /// /// How to use: /// 1. Create container or containers with layout component. Notifications will be shown in those containers. You can check how it works with NotifyContainer in sample scene. /// 2. Create template for notification with Notify component. /// 3. If you want change text in runtime set Text property in Notify component. /// 4. If you want close notification by button set Hide button property in Notify component. /// 5. Write code to show notification /// /// notifyPrefab.Template().Show("Sticky Notification. Click on the × above to close."); /// /// notifyPrefab.Template() - return the notification instance by template name. /// Show("Sticky Notification. Click on the × above to close.") - show notification with following text; /// or /// Show(message: "Simple Notification.", customHideDelay = 4.5f, hideAnimation = UIWidgets.Notify.AnimationCollapse, slideUpOnHide = false); /// Show notification with following text, hide it after 4.5 seconds, run specified animation on hide without SlideUpOnHide. /// [AddComponentMenu("UI/UIWidgets/Notify")] public class Notify : MonoBehaviour, ITemplatable { [SerializeField] Button hideButton; /// /// Gets or sets the button that close current notification. /// /// The hide button. public Button HideButton { get { return hideButton; } set { if (hideButton!=null) { hideButton.onClick.RemoveListener(Hide); } hideButton = value; if (hideButton!=null) { hideButton.onClick.AddListener(Hide); } } } [SerializeField] Text text; /// /// Gets or sets the text component. /// /// The text. public Text Text { get { return text; } set { text = value; } } [SerializeField] float HideDelay = 10f; [SerializeField] bool unscaledTime; bool isTemplate = true; /// /// Gets a value indicating whether this instance is template. /// /// true if this instance is template; otherwise, false. public bool IsTemplate { get { return isTemplate; } set { isTemplate = value; } } /// /// Gets the name of the template. /// /// The name of the template. public string TemplateName { get; set; } static Templates templates; /// /// Notify templates. /// public static Templates Templates { get { if (templates==null) { templates = new Templates(AddCloseCallback); } return templates; } set { templates = value; } } /// /// Function used to run show animation. /// public Func ShowAnimation; /// /// Function used to run hide animation. /// public Func HideAnimation; Func oldShowAnimation; Func oldHideAnimation; IEnumerator showCorutine; IEnumerator hideCorutine; /// /// Start slide up animations after hide current notification. Turn it off if its managed with HideAnimation. /// public bool SlideUpOnHide = true; void Awake() { if (IsTemplate) { gameObject.SetActive(false); } } /// /// Finds the templates. /// static void FindTemplates() { Templates.FindTemplates(); } void OnDestroy() { HideButton = null; if (!IsTemplate) { templates = null; return ; } //if FindTemplates never called than TemplateName==null if (TemplateName!=null) { DeleteTemplate(TemplateName); } } /// /// Clears the cached instance of templates. /// static public void ClearCache() { Templates.ClearCache(); } /// /// Clears the cached instance of specified template. /// /// Template name. static public void ClearCache(string templateName) { Templates.ClearCache(templateName); } /// /// Gets the template by name. /// /// The template. /// Template name. static public Notify GetTemplate(string template) { return Templates.Get(template); } /// /// Deletes the template by name. /// /// Template. static public void DeleteTemplate(string template) { Templates.Delete(template); } /// /// Adds the template. /// /// Template name. /// Notify template object. /// If set to true replace. static public void AddTemplate(string template, Notify notifyTemplate, bool replace = true) { Templates.Add(template, notifyTemplate, replace); } /// /// Return notification by the specified template name. /// /// Template name. static public Notify Template(string template) { return Templates.Instance(template); } /// /// Return Notify instance using current instance as template. /// public Notify Template() { if ((TemplateName!=null) && Templates.Exists(TemplateName)) { //do nothing } else if (!Templates.Exists(gameObject.name)) { Templates.Add(gameObject.name, this); } else if (Templates.Get(gameObject.name)!=this) { Templates.Add(gameObject.name, this); } return Templates.Instance(gameObject.name); } /// /// Adds the close callback. /// /// Notify. static void AddCloseCallback(Notify notify) { if (notify.hideButton==null) { return ; } notify.hideButton.onClick.AddListener(notify.Hide); } /// /// Time between previous notification was hidden and next will be showed. /// public float SequenceDelay; /// /// The notify manager. /// static NotifySequenceManager notifyManager; /// /// Gets the notify manager. /// /// The notify manager. static public NotifySequenceManager NotifyManager { get { if (notifyManager==null) { var go = new GameObject("NotifySequenceManager"); notifyManager = go.AddComponent(); } return notifyManager; } } /// /// Show the notification. /// /// Message. /// Custom hide delay. /// Container. Parent object for current notification. /// Function used to run show animation. /// Function used to run hide animation. /// Start slide up animations after hide current notification. /// Add notification to sequence and display in order according specified sequenceType. /// Time between previous notification was hidden and next will be showed. /// Clear notifications sequence and hide current notification. /// Use unscaled time. public void Show(string message = null, float? customHideDelay = null, Transform container = null, Func showAnimation = null, Func hideAnimation = null, bool? slideUpOnHide = null, NotifySequence sequenceType = NotifySequence.None, float sequenceDelay = 0.3f, bool clearSequence = false, bool? newUnscaledTime = null) { if (clearSequence) { NotifyManager.Clear(); } SequenceDelay = sequenceDelay; oldShowAnimation = ShowAnimation; oldHideAnimation = HideAnimation; SetMessage(message); if (container!=null) { transform.SetParent(container, false); } if (newUnscaledTime!=null) { unscaledTime = (bool)newUnscaledTime; } if (customHideDelay!=null) { HideDelay = (float)customHideDelay; } if (slideUpOnHide!=null) { SlideUpOnHide = (bool)slideUpOnHide; } if (showAnimation!=null) { ShowAnimation = showAnimation; } if (hideAnimation!=null) { HideAnimation = hideAnimation; } if (sequenceType!=NotifySequence.None) { NotifyManager.Add(this, sequenceType); } else { Display(); } } public virtual void SetMessage(string message) { if ((message!=null) && (Text!=null)) { Text.text = message; } } Action OnHideCallback; /// /// Display notification. /// /// On hide callback. public void Display(Action onHideCallback=null) { transform.SetAsLastSibling(); gameObject.SetActive(true); OnHideCallback = onHideCallback; if (ShowAnimation!=null) { showCorutine = ShowAnimation(this); StartCoroutine(showCorutine); } else { showCorutine = null; } if (HideDelay > 0.0f) { hideCorutine = HideCorutine(); StartCoroutine(hideCorutine); } else { hideCorutine = null; } } IEnumerator HideCorutine() { yield return new WaitForSeconds(HideDelay); if (HideAnimation!=null) { yield return StartCoroutine(HideAnimation(this)); } Hide(); } /// /// Hide notification. /// public void Hide() { if (SlideUpOnHide) { SlideUp(); } if (OnHideCallback!=null) { OnHideCallback(); } Return(); } /// /// Return this instance to cache. /// public void Return() { Templates.ToCache(this); ShowAnimation = oldShowAnimation; HideAnimation = oldHideAnimation; SetMessage(Templates.Get(TemplateName).text.text); } static Stack slides = new Stack(); RectTransform GetSlide() { RectTransform rect; if (slides.Count==0) { var obj = new GameObject("SlideUp"); obj.SetActive(false); rect = obj.AddComponent(); obj.AddComponent(); //change height don't work without graphic component var image = obj.AddComponent(); image.color = Color.clear; } else { do { rect = (slides.Count > 0) ? slides.Pop() : GetSlide(); } while (rect==null); } return rect; } /// /// Slides up. /// void SlideUp() { var rect = GetSlide(); SlideUp slide = rect.GetComponent(); slide.UnscaledTime = unscaledTime; var sourceRect = transform as RectTransform; rect.localRotation = sourceRect.localRotation; rect.localPosition = sourceRect.localPosition; rect.localScale = sourceRect.localScale; rect.anchorMin = sourceRect.anchorMin; rect.anchorMax = sourceRect.anchorMax; rect.anchoredPosition = sourceRect.anchoredPosition; rect.anchoredPosition3D = sourceRect.anchoredPosition3D; rect.sizeDelta = sourceRect.sizeDelta; rect.pivot = sourceRect.pivot; rect.transform.SetParent(transform.parent, false); rect.transform.SetSiblingIndex(transform.GetSiblingIndex()); rect.gameObject.SetActive(true); slide.Run(); } /// /// Returns slide to cache. /// /// Slide. public static void FreeSlide(RectTransform slide) { slides.Push(slide); } /// /// Rotate animation. /// /// Notify. static public IEnumerator AnimationRotate(Notify notify) { var rect = notify.transform as RectTransform; var start_rotarion = rect.localRotation.eulerAngles; var length = 0.5f; var end_time = Time.time + length; while (Time.time <= end_time) { var rotation_x = Mathf.Lerp(0, 90, 1 - (end_time - Time.time) / length); rect.localRotation = Quaternion.Euler(rotation_x, start_rotarion.y, start_rotarion.z); yield return null; } //return rotation back for future use rect.localRotation = Quaternion.Euler(start_rotarion); } /// /// Rotate animation. /// /// Notify. static public IEnumerator AnimationRotateUnscaledTime(Notify notify) { var rect = notify.transform as RectTransform; var start_rotarion = rect.localRotation.eulerAngles; var length = 0.5f; var end_time = Time.unscaledTime + length; while (Time.unscaledTime <= end_time) { var rotation_x = Mathf.Lerp(0, 90, 1 - (end_time - Time.unscaledTime) / length); rect.localRotation = Quaternion.Euler(rotation_x, start_rotarion.y, start_rotarion.z); yield return null; } //return rotation back for future use rect.localRotation = Quaternion.Euler(start_rotarion); } /// /// Collapse animation. /// /// Notify. static public IEnumerator AnimationCollapse(Notify notify) { var rect = notify.transform as RectTransform; var layout = notify.GetComponentInParent(); var max_height = rect.rect.height; var speed = 200f; var time = max_height / speed; var end_time = Time.time + time; while (Time.time <= end_time) { var height = Mathf.Lerp(max_height, 0, 1 - (end_time - Time.time) / time); rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, height); if (layout!=null) { layout.UpdateLayout(); } yield return null; } //return height back for future use rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, max_height); } /// /// Collapse animation. /// /// Notify. static public IEnumerator AnimationCollapseUnscaledTime(Notify notify) { var rect = notify.transform as RectTransform; var layout = notify.GetComponentInParent(); var max_height = rect.rect.height; var speed = 200f; var time = max_height / speed; var end_time = Time.unscaledTime + time; while (Time.unscaledTime <= end_time) { var height = Mathf.Lerp(max_height, 0, 1 - (end_time - Time.unscaledTime) / time); rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, height); if (layout!=null) { layout.UpdateLayout(); } yield return null; } //return height back for future use rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, max_height); } } }