// Copyright (c) 2015 - 2023 Doozy Entertainment. All Rights Reserved. // This code can only be used under the standard Unity Asset Store End User License Agreement // A Copy of the EULA APPENDIX 1 is available at http://unity3d.com/company/legal/as_terms using System.Collections; using System.Collections.Generic; using Doozy.Runtime.Common.Attributes; using Doozy.Runtime.Common.Extensions; using Doozy.Runtime.Mody; using Doozy.Runtime.UIManager.Containers; using Doozy.Runtime.UIManager.ScriptableObjects; using UnityEngine; using UnityEngine.Events; using UnityEngine.EventSystems; // ReSharper disable ClassWithVirtualMembersNeverInherited.Global // ReSharper disable MemberCanBePrivate.Global // ReSharper disable MemberCanBeProtected.Global // ReSharper disable PartialTypeWithSinglePart namespace Doozy.Runtime.UIManager.Triggers { /// /// Specialized trigger used to show a UITooltip /// [DisallowMultipleComponent] public partial class UITooltipTrigger : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler { [ClearOnReload] public static HashSet database { get; } = new HashSet(); /// /// Name of the UITooltip that will be triggered by this trigger /// public string UITooltipName = UITooltip.k_DefaultTooltipName; /// /// Show the tooltip when the pointer enters the trigger /// public bool ShowOnPointerEnter = true; /// /// Hide the tooltip when the pointer exits the trigger (if the tooltip is shown) /// public bool HideOnPointerExit = true; /// /// Show the tooltip when the pointer clicks the trigger /// public bool ShowOnPointerClick; /// /// Hide the tooltip when the pointer clicks the trigger (if the tooltip is shown) /// public bool HideOnPointerClick; /// /// Override tooltip's ParentMode to use the value from this trigger instead of the one set in the tooltip /// public bool OverrideParentMode; /// /// Set where a tooltip should be instantiated under. /// Only used when OverrideParentMode is true. /// public UITooltip.Parenting ParentMode = UITooltip.Parenting.TooltipsCanvas; /// /// Override tooltip's TrackingMode to use the value from this trigger instead of the one set in the tooltip /// public bool OverrideTrackingMode; /// /// Set how the tooltip behaves when it is shown. /// Only used when OverrideTrackingMode is true. /// public UITooltip.Tracking TrackingMode = UITooltip.Tracking.Disabled; /// /// Override tooltip's PositionMode to use the value from this trigger instead of the one set in the tooltip /// public bool OverridePositionMode; /// /// Set where the tooltip should be positioned relative to the tracked target. /// Only used when OverridePositionMode is true. /// public UITooltip.Positioning PositionMode = UITooltip.Positioning.MiddleCenter; /// /// Id used to identify the designated parent where the tooltip should be parented under, /// after is has been instantiated /// public UITagId ParentTag; /// /// Id used to identify the follow target when the tracking mode is set to FollowTarget /// public UITagId FollowTag; /// /// Override tooltip's PositionOffset to use the value from this trigger instead of the one set in the tooltip /// public bool OverridePositionOffset; /// /// Set the offset applied to the tooltip position. /// Only used when OverridePositionOffset is true. /// public Vector3 PositionOffset = Vector3.zero; /// /// Override tooltip's MaximumWidth to use the value from this trigger instead of the one set in the tooltip /// public bool OverrideMaximumWidth; /// /// Set the maximum width of the tooltip. /// Only used when OverrideMaximumWidth is true. /// public float MaximumWidth; [SerializeField] private float ShowDelay; /// How long (in seconds), after the pointer enters the trigger, should the tooltip be shown public float showDelay { get => ShowDelay; set => ShowDelay = Mathf.Max(0, value); } /// /// Texts set to the TextMeshProUGUI labels attached to the target UITooltip /// public List Texts = new List(); /// /// Sprites set to the Images attached to the target UITooltip /// public List Sprites = new List(); /// /// UnityEvents triggered by clicking the UIButtons attached to the target UITooltip /// public List Events = new List(); /// Callback invoked when the trigger calls for the tooltip to be shown public ModyEvent OnShowCallback = new ModyEvent(); /// Callback invoked when the trigger calls for the tooltip to be hidden public ModyEvent OnHideCallback = new ModyEvent(); /// /// Internal variable used to store the UITooltip that is currently being triggered by this trigger /// public UITooltip tooltip { get; private set; } /// Internal flag used to determine if the tooltip name is valid private bool isValid { get; set; } /// Internal coroutine used to show the tooltip after the ShowDelay has passed private Coroutine showDelayCoroutine { get; set; } /// Flag used to keep track if the tooltip trigger is currently waiting to show its target tooltip public bool isWaitingToShow => showDelayCoroutine != null; private void Validate() { isValid = UITooltipDatabase.instance.GetPrefab(UITooltipName) != null; } private void Awake() { database.Add(this); } private void OnEnable() { database.Remove(null); Validate(); if (isValid) return; Debug.LogWarning($"UITooltipTrigger - {name} - The UITooltip name '{UITooltipName}' is not valid. Please make sure it is spelled correctly in the UITooltipDatabase.", this); } private void OnDisable() { StopShowDelayCoroutine(); } private void OnDestroy() { database.Remove(this); database.Remove(null); } public void OnPointerEnter(PointerEventData eventData) { StopShowDelayCoroutine(); if (!isValid) return; if (!ShowOnPointerEnter) return; if (showDelay > 0) { showDelayCoroutine = StartCoroutine(ShowDelayCoroutine()); return; } ShowTooltip(); } public void OnPointerExit(PointerEventData eventData) { StopShowDelayCoroutine(); if (!isValid) return; if (HideOnPointerExit) HideTooltip(); } public void OnPointerClick(PointerEventData eventData) { StopShowDelayCoroutine(); if (!isValid) return; if (ShowOnPointerClick && tooltip == null) { ShowTooltip(); return; } if (HideOnPointerClick) HideTooltip(); } private IEnumerator ShowDelayCoroutine() { yield return new WaitForSecondsRealtime(showDelay); ShowTooltip(); StopShowDelayCoroutine(); } private void StopShowDelayCoroutine() { if (showDelayCoroutine == null) return; StopCoroutine(showDelayCoroutine); showDelayCoroutine = null; } /// Show the tooltip public virtual void ShowTooltip() { if (tooltip != null) { if (tooltip.isVisible || tooltip.isShowing) return; tooltip.InstantHide(); if (tooltip != null) { Destroy(tooltip.gameObject); tooltip = null; } } tooltip = UITooltip.Get(UITooltipName); // Get the tooltip from the database if (tooltip == null) return; //Tooltip not found in the database tooltip.SetTrigger(this); //Set the trigger to the tooltip so it can access the trigger's properties //Parent Mode if (OverrideParentMode) { tooltip.ParentMode = ParentMode; tooltip.ParentTag = ParentTag; } tooltip.SetParent(tooltip.GetParent()); //Reparent the tooltip //Tracking Mode if (OverrideTrackingMode) { tooltip.TrackingMode = TrackingMode; tooltip.FollowTag = FollowTag; } //Position Mode if (OverridePositionMode) tooltip.PositionMode = PositionMode; //Position Offset if (OverridePositionOffset) tooltip.PositionOffset = PositionOffset; //Maximum Width if (OverrideMaximumWidth) tooltip.MaximumWidth = MaximumWidth; tooltip.InstantHide(false); //Hide instantly to prevent the tooltip from showing up in the wrong position tooltip.SetTexts(Texts.RemoveNulls().ToArray()); //Set the texts tooltip.SetSprites(Sprites.RemoveNulls().ToArray()); //Set the sprites tooltip.SetEvents(Events.RemoveNulls().ToArray()); //Set the events tooltip.Show(); //Show the tooltip OnShowCallback?.Execute(); //Invoke the OnShowCallback } /// Hide the tooltip public virtual void HideTooltip() { if (tooltip == null) return; //tooltip might have been destroyed by another script tooltip.OnHiddenCallback.Event.AddListener(() => { if (tooltip == null) return; //If the tooltip is null, it means it was destroyed by another script, so we don't need to destroy it again Destroy(tooltip.gameObject); //Destroy the tooltip tooltip = null; //Set the tooltip to null to prevent it from being destroyed on the next frame OnHideCallback?.Execute(); //Invoke the OnHideCallback }); tooltip.Hide(); //Hide the tooltip } } }