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);
}
}
}