天津23维预案
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

617 lines
15 KiB

2 years ago
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Linq;
namespace UIWidgets
{
/// <summary>
/// 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
/// <example>
/// notifyPrefab.Template().Show("Sticky Notification. Click on the × above to close.");
/// </example>
/// 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.
/// </summary>
[AddComponentMenu("UI/UIWidgets/Notify")]
public class Notify : MonoBehaviour, ITemplatable
{
[SerializeField]
Button hideButton;
/// <summary>
/// Gets or sets the button that close current notification.
/// </summary>
/// <value>The hide button.</value>
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;
/// <summary>
/// Gets or sets the text component.
/// </summary>
/// <value>The text.</value>
public Text Text {
get {
return text;
}
set {
text = value;
}
}
[SerializeField]
float HideDelay = 10f;
[SerializeField]
bool unscaledTime;
bool isTemplate = true;
/// <summary>
/// Gets a value indicating whether this instance is template.
/// </summary>
/// <value><c>true</c> if this instance is template; otherwise, <c>false</c>.</value>
public bool IsTemplate {
get {
return isTemplate;
}
set {
isTemplate = value;
}
}
/// <summary>
/// Gets the name of the template.
/// </summary>
/// <value>The name of the template.</value>
public string TemplateName {
get;
set;
}
static Templates<Notify> templates;
/// <summary>
/// Notify templates.
/// </summary>
public static Templates<Notify> Templates {
get {
if (templates==null)
{
templates = new Templates<Notify>(AddCloseCallback);
}
return templates;
}
set {
templates = value;
}
}
/// <summary>
/// Function used to run show animation.
/// </summary>
public Func<Notify,IEnumerator> ShowAnimation;
/// <summary>
/// Function used to run hide animation.
/// </summary>
public Func<Notify,IEnumerator> HideAnimation;
Func<Notify,IEnumerator> oldShowAnimation;
Func<Notify,IEnumerator> oldHideAnimation;
IEnumerator showCorutine;
IEnumerator hideCorutine;
/// <summary>
/// Start slide up animations after hide current notification. Turn it off if its managed with HideAnimation.
/// </summary>
public bool SlideUpOnHide = true;
void Awake()
{
if (IsTemplate)
{
gameObject.SetActive(false);
}
}
/// <summary>
/// Finds the templates.
/// </summary>
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);
}
}
/// <summary>
/// Clears the cached instance of templates.
/// </summary>
static public void ClearCache()
{
Templates.ClearCache();
}
/// <summary>
/// Clears the cached instance of specified template.
/// </summary>
/// <param name="templateName">Template name.</param>
static public void ClearCache(string templateName)
{
Templates.ClearCache(templateName);
}
/// <summary>
/// Gets the template by name.
/// </summary>
/// <returns>The template.</returns>
/// <param name="template">Template name.</param>
static public Notify GetTemplate(string template)
{
return Templates.Get(template);
}
/// <summary>
/// Deletes the template by name.
/// </summary>
/// <param name="template">Template.</param>
static public void DeleteTemplate(string template)
{
Templates.Delete(template);
}
/// <summary>
/// Adds the template.
/// </summary>
/// <param name="template">Template name.</param>
/// <param name="notifyTemplate">Notify template object.</param>
/// <param name="replace">If set to <c>true</c> replace.</param>
static public void AddTemplate(string template, Notify notifyTemplate, bool replace = true)
{
Templates.Add(template, notifyTemplate, replace);
}
/// <summary>
/// Return notification by the specified template name.
/// </summary>
/// <param name="template">Template name.</param>
static public Notify Template(string template)
{
return Templates.Instance(template);
}
/// <summary>
/// Return Notify instance using current instance as template.
/// </summary>
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);
}
/// <summary>
/// Adds the close callback.
/// </summary>
/// <param name="notify">Notify.</param>
static void AddCloseCallback(Notify notify)
{
if (notify.hideButton==null)
{
return ;
}
notify.hideButton.onClick.AddListener(notify.Hide);
}
/// <summary>
/// Time between previous notification was hidden and next will be showed.
/// </summary>
public float SequenceDelay;
/// <summary>
/// The notify manager.
/// </summary>
static NotifySequenceManager notifyManager;
/// <summary>
/// Gets the notify manager.
/// </summary>
/// <value>The notify manager.</value>
static public NotifySequenceManager NotifyManager {
get {
if (notifyManager==null)
{
var go = new GameObject("NotifySequenceManager");
notifyManager = go.AddComponent<NotifySequenceManager>();
}
return notifyManager;
}
}
/// <summary>
/// Show the notification.
/// </summary>
/// <param name="message">Message.</param>
/// <param name="customHideDelay">Custom hide delay.</param>
/// <param name="container">Container. Parent object for current notification.</param>
/// <param name="showAnimation">Function used to run show animation.</param>
/// <param name="hideAnimation">Function used to run hide animation.</param>
/// <param name="slideUpOnHide">Start slide up animations after hide current notification.</param>
/// <param name="sequenceType">Add notification to sequence and display in order according specified sequenceType.</param>
/// <param name="sequenceDelay">Time between previous notification was hidden and next will be showed.</param>
/// <param name="clearSequence">Clear notifications sequence and hide current notification.</param>
/// <param name="newUnscaledTime">Use unscaled time.</param>
public void Show(string message = null,
float? customHideDelay = null,
Transform container = null,
Func<Notify,IEnumerator> showAnimation = null,
Func<Notify,IEnumerator> 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;
/// <summary>
/// Display notification.
/// </summary>
/// <param name="onHideCallback">On hide callback.</param>
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();
}
/// <summary>
/// Hide notification.
/// </summary>
public void Hide()
{
if (SlideUpOnHide)
{
SlideUp();
}
if (OnHideCallback!=null)
{
OnHideCallback();
}
Return();
}
/// <summary>
/// Return this instance to cache.
/// </summary>
public void Return()
{
Templates.ToCache(this);
ShowAnimation = oldShowAnimation;
HideAnimation = oldHideAnimation;
SetMessage(Templates.Get(TemplateName).text.text);
}
static Stack<RectTransform> slides = new Stack<RectTransform>();
RectTransform GetSlide()
{
RectTransform rect;
if (slides.Count==0)
{
var obj = new GameObject("SlideUp");
obj.SetActive(false);
rect = obj.AddComponent<RectTransform>();
obj.AddComponent<SlideUp>();
//change height don't work without graphic component
var image = obj.AddComponent<Image>();
image.color = Color.clear;
}
else
{
do
{
rect = (slides.Count > 0) ? slides.Pop() : GetSlide();
}
while (rect==null);
}
return rect;
}
/// <summary>
/// Slides up.
/// </summary>
void SlideUp()
{
var rect = GetSlide();
SlideUp slide = rect.GetComponent<SlideUp>();
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();
}
/// <summary>
/// Returns slide to cache.
/// </summary>
/// <param name="slide">Slide.</param>
public static void FreeSlide(RectTransform slide)
{
slides.Push(slide);
}
/// <summary>
/// Rotate animation.
/// </summary>
/// <param name="notify">Notify.</param>
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);
}
/// <summary>
/// Rotate animation.
/// </summary>
/// <param name="notify">Notify.</param>
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);
}
/// <summary>
/// Collapse animation.
/// </summary>
/// <param name="notify">Notify.</param>
static public IEnumerator AnimationCollapse(Notify notify)
{
var rect = notify.transform as RectTransform;
var layout = notify.GetComponentInParent<EasyLayout.EasyLayout>();
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);
}
/// <summary>
/// Collapse animation.
/// </summary>
/// <param name="notify">Notify.</param>
static public IEnumerator AnimationCollapseUnscaledTime(Notify notify)
{
var rect = notify.transform as RectTransform;
var layout = notify.GetComponentInParent<EasyLayout.EasyLayout>();
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);
}
}
}