using UnityEngine; using UnityEngine.UI; using DuloGames.UI.Tweens; using System; namespace DuloGames.UI { [DisallowMultipleComponent, AddComponentMenu("UI/Tab", 58)] public class UITab : Toggle { public enum TextTransition { None, ColorTint } [SerializeField] private GameObject m_TargetContent; [SerializeField] private Image m_ImageTarget; [SerializeField] private Selectable.Transition m_ImageTransition = Selectable.Transition.None; [SerializeField] private ColorBlockExtended m_ImageColors = ColorBlockExtended.defaultColorBlock; [SerializeField] private SpriteStateExtended m_ImageSpriteState; [SerializeField] private AnimationTriggersExtended m_ImageAnimationTriggers = new AnimationTriggersExtended(); [SerializeField] private Text m_TextTarget; [SerializeField] private TextTransition m_TextTransition = TextTransition.None; [SerializeField] private ColorBlockExtended m_TextColors = ColorBlockExtended.defaultColorBlock; /// /// Gets or sets the target content game object. /// public GameObject targetContent { get { return this.m_TargetContent; } set { this.m_TargetContent = value; } } /// /// Gets or sets the image target. /// public Image imageTarget { get { return this.m_ImageTarget; } set { this.m_ImageTarget = value; } } /// /// Gets or sets the image transition. /// public Transition imageTransition { get { return this.m_ImageTransition; } set { this.m_ImageTransition = value; } } /// /// Gets or sets the image color block. /// public ColorBlockExtended imageColors { get { return this.m_ImageColors; } set { this.m_ImageColors = value; } } /// /// Gets or sets the image sprite state. /// public SpriteStateExtended imageSpriteState { get { return this.m_ImageSpriteState; } set { this.m_ImageSpriteState = value; } } /// /// Gets or sets the image animation triggers. /// public AnimationTriggersExtended imageAnimationTriggers { get { return this.m_ImageAnimationTriggers; } set { this.m_ImageAnimationTriggers = value; } } /// /// Gets or sets the text target. /// public Text textTarget { get { return this.m_TextTarget; } set { this.m_TextTarget = value; } } /// /// Gets or sets the text transition. /// public TextTransition textTransition { get { return this.m_TextTransition; } set { this.m_TextTransition = value; } } /// /// Gets or sets the text colors block. /// public ColorBlockExtended textColors { get { return this.m_TextColors; } set { this.m_TextColors = value; } } private Selectable.SelectionState m_CurrentState = Selectable.SelectionState.Normal; // Tween controls [NonSerialized] private readonly TweenRunner m_ColorTweenRunner; // Called by Unity prior to deserialization, // should not be called by users protected UITab() { if (this.m_ColorTweenRunner == null) this.m_ColorTweenRunner = new TweenRunner(); this.m_ColorTweenRunner.Init(this); } protected override void Awake() { base.Awake(); // Make sure we have toggle group if (this.group == null) { // Try to find the group in the parents ToggleGroup grp = UIUtility.FindInParents(this.gameObject); if (grp != null) { this.group = grp; } else { // Add new group on the parent this.group = this.transform.parent.gameObject.AddComponent(); } } } protected override void OnEnable() { base.OnEnable(); // Hook an event listener this.onValueChanged.AddListener(OnToggleStateChanged); // Apply initial state this.InternalEvaluateAndTransitionState(true); } protected override void OnDisable() { base.OnDisable(); // Unhook the event listener this.onValueChanged.RemoveListener(OnToggleStateChanged); } #if UNITY_EDITOR protected override void OnValidate() { base.OnValidate(); this.m_ImageColors.fadeDuration = Mathf.Max(this.m_ImageColors.fadeDuration, 0f); this.m_TextColors.fadeDuration = Mathf.Max(this.m_TextColors.fadeDuration, 0f); } /// /// Raises the property change event from the editor. /// public void OnProperyChange_Editor() { if (!this.isActiveAndEnabled) return; this.DoSpriteSwap(this.m_ImageTarget, null); this.InternalEvaluateAndTransitionState(true); } #endif /// /// Raises the toggle state changed event. /// /// If set to true state. protected void OnToggleStateChanged(bool state) { if (!this.IsActive() || !Application.isPlaying) return; this.InternalEvaluateAndTransitionState(false); } /// /// Evaluates and toggles the content visibility. /// public void EvaluateAndToggleContent() { if (this.m_TargetContent != null) m_TargetContent.SetActive(this.isOn); } /// /// Internaly evaluates and transitions to the current state. /// /// If set to true instant. private void InternalEvaluateAndTransitionState(bool instant) { if (!this.isActiveAndEnabled) return; // Toggle the content this.EvaluateAndToggleContent(); #if UNITY_EDITOR // Transition the active graphic // Hackfix because unity is not toggling it in edit mode if (instant && !Application.isPlaying && this.graphic != null) { this.graphic.canvasRenderer.SetAlpha((!this.isOn) ? 0f : 1f); } #endif // Transition the active graphic children if (this.graphic != null && this.graphic.transform.childCount > 0) { float targetAlpha = (!this.isOn) ? 0f : 1f; // Loop through the children foreach (Transform child in this.graphic.transform) { // Try getting a graphic component Graphic g = child.GetComponent(); if (g != null) { if (!g.canvasRenderer.GetAlpha().Equals(targetAlpha)) { if (instant) g.canvasRenderer.SetAlpha(targetAlpha); else g.CrossFadeAlpha(targetAlpha, 0.1f, true); } } } } // Do a state transition this.DoStateTransition(this.m_CurrentState, instant); } /// /// Does the state transitioning. /// /// State. /// If set to true instant. protected override void DoStateTransition(Selectable.SelectionState state, bool instant) { if (!this.isActiveAndEnabled) return; // Save the state as current state this.m_CurrentState = state; Color newImageColor = this.m_ImageColors.normalColor; Color newTextColor = this.m_TextColors.normalColor; Sprite newSprite = null; string imageTrigger = this.m_ImageAnimationTriggers.normalTrigger; // Prepare state values switch (state) { case Selectable.SelectionState.Normal: newImageColor = (!this.isOn) ? this.m_ImageColors.normalColor : this.m_ImageColors.activeColor; newTextColor = (!this.isOn) ? this.m_TextColors.normalColor : this.m_TextColors.activeColor; newSprite = (!this.isOn) ? null : this.m_ImageSpriteState.activeSprite; imageTrigger = (!this.isOn) ? this.m_ImageAnimationTriggers.normalTrigger : this.m_ImageAnimationTriggers.activeTrigger; break; case Selectable.SelectionState.Highlighted: newImageColor = (!this.isOn) ? this.m_ImageColors.highlightedColor : this.m_ImageColors.activeHighlightedColor; newTextColor = (!this.isOn) ? this.m_TextColors.highlightedColor : this.m_TextColors.activeHighlightedColor; newSprite = (!this.isOn) ? this.m_ImageSpriteState.highlightedSprite : this.m_ImageSpriteState.activeHighlightedSprite; imageTrigger = (!this.isOn) ? this.m_ImageAnimationTriggers.highlightedTrigger : this.m_ImageAnimationTriggers.activeHighlightedTrigger; break; case Selectable.SelectionState.Pressed: newImageColor = (!this.isOn) ? this.m_ImageColors.pressedColor : this.m_ImageColors.activePressedColor; newTextColor = (!this.isOn) ? this.m_TextColors.pressedColor : this.m_TextColors.activePressedColor; newSprite = (!this.isOn) ? this.m_ImageSpriteState.pressedSprite : this.m_ImageSpriteState.activePressedSprite; imageTrigger = (!this.isOn) ? this.m_ImageAnimationTriggers.pressedTrigger : this.m_ImageAnimationTriggers.activePressedTrigger; break; case Selectable.SelectionState.Disabled: newImageColor = this.m_ImageColors.disabledColor; newTextColor = this.m_TextColors.disabledColor; newSprite = this.m_ImageSpriteState.disabledSprite; imageTrigger = this.m_ImageAnimationTriggers.disabledTrigger; break; } // Check if the tab is active in the scene if (this.gameObject.activeInHierarchy) { // Do the image transition switch (this.m_ImageTransition) { case Selectable.Transition.ColorTint: this.StartColorTween((this.m_ImageTarget as Graphic), newImageColor * this.m_ImageColors.colorMultiplier, (instant ? 0f : this.m_ImageColors.fadeDuration)); break; case Selectable.Transition.SpriteSwap: this.DoSpriteSwap(this.m_ImageTarget, newSprite); break; case Selectable.Transition.Animation: this.TriggerAnimation(this.m_ImageTarget.gameObject, imageTrigger); break; } // Do the text transition switch (this.m_TextTransition) { case TextTransition.ColorTint: this.StartColorTweenText(newTextColor * this.m_TextColors.colorMultiplier, (instant ? 0f : this.m_TextColors.fadeDuration)); break; } } } /// /// Starts a color tween. /// /// Target. /// Target color. /// Duration. private void StartColorTween(Graphic target, Color targetColor, float duration) { if (target == null) return; if (!Application.isPlaying || duration == 0f) { target.canvasRenderer.SetColor(targetColor); } else { target.CrossFadeColor(targetColor, duration, true, true); } } /// /// Starts a color tween. /// /// Target color. /// Duration. private void StartColorTweenText(Color targetColor, float duration) { if (this.m_TextTarget == null) return; if (!Application.isPlaying || duration == 0f) { this.m_TextTarget.color = targetColor; } else { var colorTween = new ColorTween { duration = duration, startColor = this.m_TextTarget.color, targetColor = targetColor }; colorTween.AddOnChangedCallback(SetTextColor); colorTween.ignoreTimeScale = true; this.m_ColorTweenRunner.StartTween(colorTween); } } /// /// Sets the color of the text. /// /// Color. private void SetTextColor(Color color) { if (this.m_TextTarget == null) return; this.m_TextTarget.color = color; } /// /// Does a sprite swap. /// /// Target. /// New sprite. private void DoSpriteSwap(Image target, Sprite newSprite) { if (target == null) return; if (!target.overrideSprite.Equals(newSprite)) target.overrideSprite = newSprite; } /// /// Triggers the animation. /// /// Target. /// Triggername. private void TriggerAnimation(GameObject target, string triggername) { if (target == null) return; Animator animator = target.GetComponent(); if (animator == null || !animator.enabled || !animator.isActiveAndEnabled || animator.runtimeAnimatorController == null || !animator.hasBoundPlayables || string.IsNullOrEmpty(triggername)) return; animator.ResetTrigger(this.m_ImageAnimationTriggers.normalTrigger); animator.ResetTrigger(this.m_ImageAnimationTriggers.pressedTrigger); animator.ResetTrigger(this.m_ImageAnimationTriggers.highlightedTrigger); animator.ResetTrigger(this.m_ImageAnimationTriggers.activeTrigger); animator.ResetTrigger(this.m_ImageAnimationTriggers.activeHighlightedTrigger); animator.ResetTrigger(this.m_ImageAnimationTriggers.activePressedTrigger); animator.ResetTrigger(this.m_ImageAnimationTriggers.disabledTrigger); animator.SetTrigger(triggername); } /// /// Activate the tab. /// public void Activate() { if (!this.isOn) this.isOn = true; } } }