using EasyTalk.Animation; using EasyTalk.Controller; using EasyTalk.Localization; using EasyTalk.Utils; using System.Collections; using UnityEngine; using UnityEngine.Events; namespace EasyTalk.Display { /// /// The DialoguePanel is an abstract component which provides a set of core features for components/panels which make up a dialogue display. Specifically, this class /// provides functionality for showing and hiding a dialogue panel and handling the animation associated with those actions. /// public abstract class DialoguePanel : DialogueListener, LocalizableComponent { /// /// The display ID of the panel. /// [Tooltip("An identifier for the panel.")] [SerializeField] private string displayID = null; /// /// Whether the Display uses standard Text components rather than TextMeshPro components, even with TextMeshPro installed. /// [Tooltip("Forces the display to use standard Unity text components rather than TextMeshPro, even when TextMeshPro is installed.")] [SerializeField] protected bool forceStandardText = false; /// /// The animation type to use when hiding/showing the panel. /// [Tooltip("The type of animation to use when hiding and showing the panel." + "\nNONE: No animation, immediately shows/hides the panel." + "\nFADE: Fades all image and text components alpha channels to 0 when hiding, and back when showing." + "\nSLIDE_LEFT: Slides the panel to the left out of the canvas/screen when hiding, and slides in from the left when showing." + "\nSLIDE_RIGHT: Slides the panel to the right out of the canvas/screen when hiding, and slides in from the right when showing." + "\nSLIDE_TOP: Slides the panel to the top out of the canvas/screen when hiding, and slides in from the top when showing." + "\nSLIDE_BOTTOM: Slides the panel to the bottom out of the canvas/screen when hiding, and slides in from the bottom when showing.")] [SerializeField] private UIAnimator.Animation animationType = UIAnimator.Animation.NONE; /// /// The animation curve to use for controling the timing of the show/hide animations for the panel. /// [Tooltip("The animation curve to use when hiding and showing the panel.")] [SerializeField] private AnimationCurve animationCurve = AnimationCurve.EaseInOut(0.0f, 0.0f, 1.0f, 1.0f); /// /// The amount of time that show/hide animations should take to complete. /// [Tooltip("The amount of time to the hide/show transition animation should take to complete.")] [SerializeField] private float animationTime = 0.4f; /// /// Whether the panel should return to its original starting position when shown (only applicable when using sliding animation modes). If false, /// the panel will just move into the canvas until it is fully visible when shown. /// [Tooltip("If set to true, the panel will return to its starting position when being shown; otherwise, the panel will attempt to " + "move into the canvas/screen and stop once fully visible")] [SerializeField] private bool returnToOriginalPosition = true; /// /// Whether the panel should override the base dialogue font size settings for its child text components. /// When this is true, the font auto-sizing applied to each child text component will come from the min and max /// font size settings set on this component. /// [Tooltip("If set to true, this component will apply the configured minimum and maximum font sizes to all child text components whenever they are in " + "auto-size mode.")] [SerializeField] private bool overrideFontSizeSettings = false; /// /// Whenever this is set to a LanguageFontOverrides asset, then whenever the language is switched, the fonts and font size /// settings used on child components of this component will be based on the settings in the LanguageFontOverrides. /// [Tooltip("Optional. When this value is set to a LanguageFontOverrides asset, all child text components will have their fonts and font sizes set " + "based on the language being used and the settings in the asset. If the font settings are also being overridden in this component, then the overridden " + "minimum and maximum font sizes take priority when a text component is in auto-size mode.")] [SerializeField] private LanguageFontOverrides languageFontOverrides; /// /// The minimum font size to use when auto-sizing child text components. /// [Tooltip("The minimum font size to use on all child text components when in font auto-sizing mode.")] [SerializeField] private int minFontSize = 0; /// /// The maximum font size to use when auto-sizing child text components. /// [Tooltip("The maximum font size to use on all child text components when in font auto-sizing mode.")] [SerializeField] private int maxFontSize = 64; /// /// An event which is triggered whenever the panel starts being hidden. /// [SerializeField] [HideInInspector] public UnityEvent onHideStart = new UnityEvent(); /// /// An event which is triggered whenever the panel has finished transitioning to a hidden state. /// [SerializeField] [HideInInspector] public UnityEvent onHideComplete = new UnityEvent(); /// /// An event which is triggered whenever the panel starts being shown. /// [SerializeField] [HideInInspector] public UnityEvent onShowStart = new UnityEvent(); /// /// An event which is triggered whenever the panel has finished transitioning to a shown state. /// [SerializeField] [HideInInspector] public UnityEvent onShowComplete = new UnityEvent(); /// /// The original position of the panel. /// protected Vector3 originalPosition = Vector3.zero; /// /// Whether the panel is currently hidden. /// protected bool isHidden = false; /// /// A coroutine which runs when transitioning the panel to be shown. /// private Coroutine showCoroutine; /// /// A cortouine which runs when transitioning the panel to a be hidden. /// private Coroutine hideCoroutine; /// /// Initializes the panel by storing the original position. /// public virtual void Init() { this.originalPosition = this.transform.localPosition; this.onShowStart.AddListener(OnShowStart); this.onShowComplete.AddListener(OnShowComplete); this.onHideStart.AddListener(OnHideStart); this.onHideComplete.AddListener(OnHideComplete); } /// /// Enables and disables TextMeshPro components and Unity standard Text components based on whether TextMeshPro is installed. /// private void OnValidate() { SetupUtils.SetUpTextComponents(this.gameObject, forceStandardText); } /// /// Gets or sets the Display ID of the panel. /// public string DisplayID { get { return this.displayID; } set { this.displayID = value; } } /// /// Gets whether the panel is currently hidden. /// public bool IsHidden { get { return isHidden; } } /// /// Sets the panel to be active, and recursively sets all parent game objects to be active. /// public void Activate() { this.gameObject.SetActive(true); Transform parent = this.transform; while ((parent = parent.transform.parent) != null) { if (!parent.gameObject.activeSelf) { parent.gameObject.SetActive(true); } } } /// /// Makes the panel inactive. /// public void Deactivate() { this.gameObject.SetActive(false); } /// /// Gets the original position of the panel. /// public Vector3 OriginalPosition { get { return originalPosition; } } /// /// Returns whether or not the panel should force standard text component usage, even when TextMesh Pro is available. /// /// public bool ForceStandardText() { return forceStandardText; } /// /// Asynchronously slides the display out of its Canvas' bounds. /// /// Whether the panel should be set to inactive after the slide out animation has completed. /// protected IEnumerator SlideDisplayOut(bool deactivateAfterSlide = true) { WaitForAnimation(); this.isHidden = true; if (onHideStart != null) { onHideStart.Invoke(); } yield return UIAnimator.SlideOutComponent( this.GetComponentInParent(), this.GetComponent(), UIAnimator.GetSlideDirection(animationType), animationTime, animationCurve); if (deactivateAfterSlide) { this.Deactivate(); } if (onHideComplete != null) { onHideComplete.Invoke(); } } /// /// Asynchronously slides the display into its Canvas' bounds. If returnToOriginalPosition is set to true, then the panel will slide to its original position. /// /// protected IEnumerator SlideDisplayIn() { WaitForAnimation(); this.Activate(); this.isHidden = false; if (onShowStart != null) { onShowStart.Invoke(); } if (returnToOriginalPosition) { yield return UIAnimator.SlideComponentToPosition( this.GetComponent(), this.OriginalPosition, animationTime, animationCurve); } else { yield return UIAnimator.SlideInComponent( this.GetComponentInParent(), this.GetComponent(), animationTime, animationCurve); } if (onShowComplete != null) { onShowComplete.Invoke(); } } /// /// Asynchronously fades the display and all of its child text and image components in to their original transparency values. /// /// protected IEnumerator FadeDisplayIn() { WaitForAnimation(); this.Activate(); this.isHidden = false; if (onShowStart != null) { onShowStart.Invoke(); } yield return UIAnimator.FadeIn(this.gameObject, animationTime, animationCurve); if (onShowComplete != null) { onShowComplete.Invoke(); } } /// /// Asynchronously fades the display and all of its child text and image components out to be completely transparent. /// /// Whether the panel should be set to inactive after the fade out animation has completed. /// protected IEnumerator FadeDisplayOut(bool deactivateAfterFade = true) { WaitForAnimation(); if (onHideStart != null) { onHideStart.Invoke(); } this.isHidden = true; yield return UIAnimator.FadeOut(this.gameObject, animationTime, animationCurve); if (onHideComplete != null) { onHideComplete.Invoke(); } if (deactivateAfterFade) { this.Deactivate(); } } /// /// Asynchronously waits for the currently running animation on this panel to complete. /// /// public IEnumerator WaitForAnimation() { while (UIAnimator.IsItemAnimating(this.gameObject.GetInstanceID())) { yield return new WaitForEndOfFrame(); } } /// /// Hides the panel. /// /// Whether the panel should be set to inactive after it is hidden. public virtual void Hide(bool deactivateAfterHide = true) { if (!this.isHidden) { Activate(); if (gameObject.activeSelf) { CancelHideRoutine(); if (animationType != UIAnimator.Animation.NONE) { this.isHidden = true; if (animationType == UIAnimator.Animation.FADE) { StartCoroutine(FadeDisplayOut(deactivateAfterHide)); } else { StartCoroutine(SlideDisplayOut(deactivateAfterHide)); } } else { HideImmediately(); if (deactivateAfterHide) { this.Deactivate(); } } } } } /// /// Hides the panel immediately (doesn't use a transition animation). /// /// Whether the panel should be set to inactive after it is hidden. public void HideImmediately(bool deactivateAfterHide = true) { CancelHideRoutine(); this.isHidden = true; if (onHideStart != null) { onHideStart.Invoke(); } if (animationType != UIAnimator.Animation.NONE) { if (animationType == UIAnimator.Animation.FADE) { UIAnimator.FadeOutImmediately(this.gameObject); } else { UIAnimator.SlideOutComponentImmediately( this.GetComponentInParent(), this.GetComponent(), UIAnimator.GetSlideDirection(animationType)); } } if (onHideComplete != null) { onHideComplete.Invoke(); } if (deactivateAfterHide) { this.Deactivate(); } } /// /// Shows the panel immediately (doesn't use a transition animation). /// public void ShowImmediately() { CancelShowRoutine(); if (onShowStart != null) { onShowStart.Invoke(); } this.Activate(); this.isHidden = false; if (onShowComplete != null) { onShowComplete.Invoke(); } } /// /// Shows the panel. /// public virtual void Show() { if (!this.isActiveAndEnabled) { Activate(); } if (gameObject.activeSelf) { CancelShowRoutine(); if (animationType == UIAnimator.Animation.NONE) { this.ShowImmediately(); } else if (animationType == UIAnimator.Animation.FADE) { StartCoroutine(this.FadeDisplayIn()); } else { StartCoroutine(this.SlideDisplayIn()); } } } /// /// Stops the coroutine for showing the panel, if it's running. /// private void CancelShowRoutine() { if (showCoroutine != null) { StopCoroutine(showCoroutine); showCoroutine = null; } } /// /// Stops the coroutine for hiding the panel, if it's running. /// private void CancelHideRoutine() { if (hideCoroutine != null) { StopCoroutine(hideCoroutine); hideCoroutine = null; } } /// /// Callback for when the panel begines to be shown. /// protected virtual void OnShowStart() { } /// /// Callback for when the panel is finished being shown (when all animations are complete). /// protected virtual void OnShowComplete() { } /// /// Callback for when the panel begins to be hidden. /// protected virtual void OnHideStart() { } /// /// Callback for when the panel has finished being hidden (when all animations are complete). /// protected virtual void OnHideComplete() { } /// /// Gets or sets the animation type used for showing and hiding the panel. /// public UIAnimator.Animation AnimationType { get { return animationType; } set { this.animationType = value; } } /// /// Gets or sets whether the panel should override the default language-specific minimum and maximum font sizes when the language is updated. /// public bool OverrideFontSizes { get { return overrideFontSizeSettings; } set { overrideFontSizeSettings = value; } } /// /// Gets or sets the LanguageFontOverrides which are used to change fonts when the language is changed. /// public LanguageFontOverrides LanguageFontOverrides { get { return languageFontOverrides; } set { languageFontOverrides = value; } } /// /// Gets or sets the minimum font size to use. /// public int MinFontSize { get { return minFontSize; } set { minFontSize = value; } } /// /// Gets or sets the maximum font size to use. /// public int MaxFontSize { get { return maxFontSize; } set { maxFontSize = value; } } } }