using UnityEngine; using UnityEngine.UI; using UnityEngine.Events; using UnityEngine.EventSystems; using System.Collections.Generic; using Lean.Transition; using Lean.Common; namespace Lean.Gui { /// This component provides an alternative to Unity's UI button, allowing you to easily add custom transitions, as well as add an OnDown event. [HelpURL(LeanGui.HelpUrlPrefix + "LeanButton")] [AddComponentMenu(LeanGui.ComponentMenuPrefix + "Button")] public class LeanButton : LeanSelectable, IPointerDownHandler, IBeginDragHandler, IDragHandler, IEndDragHandler, ISubmitHandler { [System.Flags] public enum ButtonTypes { LeftMouse = 1 << 0, RightMouse = 1 << 1, MiddleMouse = 1 << 2, Touch = 1 << 5 } /// Which buttons should this component react to? public ButtonTypes RequiredButtons { set { requiredButtons = value; } get { return requiredButtons; } } [SerializeField] private ButtonTypes requiredButtons = (ButtonTypes)~0; /// If you enable this then OnDown + DownTransition be invoked once for each finger that begins touching the button. /// If you disable this then OnDown + DownTransition will only be invoked for the first finger that begins touching the button. public bool MultiDown { set { multiDown = value; } get { return multiDown; } } [SerializeField] private bool multiDown; /// If your finger presses down on the button and drags more than this many pixels, then selection will be canceled. /// -1 = Unlimited drag distance. /// 0 = Until the finger exits the button graphic. public float DragThreshold { set { dragThreshold = value; } get { return dragThreshold; } } [SerializeField] private float dragThreshold = 10.0f; /// This allows you to perform a transition when there are no longer any fingers touching the button. /// You can create a new transition GameObject by right clicking the transition name, and selecting Create. /// For example, the Graphic.color Transition (LeanGraphicColor) component can be used to change the color back to normal. public LeanPlayer NormalTransitions { get { if (normalTransitions == null) normalTransitions = new LeanPlayer(); return normalTransitions; } } [SerializeField] private LeanPlayer normalTransitions; /// This allows you to perform a transition when a finger begins touching the button. /// You can create a new transition GameObject by right clicking the transition name, and selecting Create. /// For example, the Graphic.color Transition (LeanGraphicColor) component can be used to change the color. /// NOTE: Any transitions you perform here must be reverted in the Normal Transitions setting using a matching transition component. public LeanPlayer DownTransitions { get { if (downTransitions == null) downTransitions = new LeanPlayer(); return downTransitions; } } [SerializeField] private LeanPlayer downTransitions; /// This allows you to perform a transition when you click or tap on this button. /// You can create a new transition GameObject by right clicking the transition name, and selecting Create. /// For example, the Play Sound Transition (LeanPlaySound) component can be used to play a click sound. public LeanPlayer ClickTransitions { get { if (clickTransitions == null) clickTransitions = new LeanPlayer(); return clickTransitions; } } [SerializeField] private LeanPlayer clickTransitions; /// This allows you to perform an action when a finger begins touching the button. public UnityEvent OnDown { get { if (onDown == null) onDown = new UnityEvent(); return onDown; } } [SerializeField] private UnityEvent onDown; /// This allows you to perform an action when you click or tap on this button. public UnityEvent OnClick { get { if (onClick == null) onClick = new UnityEvent(); return onClick; } } [SerializeField] private UnityEvent onClick; [System.NonSerialized] private Vector2 totalDelta; /// Track the currently down pointers so we can toggle the transition. private List downPointers = new List(); [System.NonSerialized] private ScrollRect parentScrollRect; [System.NonSerialized] private LeanDrag parentDrag; public override void OnPointerDown(PointerEventData eventData) { base.OnPointerDown(eventData); if (IsInteractable() == true) { if (RequiredButtonPressed(eventData) == false) { return; } totalDelta = Vector2.zero; downPointers.Add(eventData.pointerId); if (multiDown == true || downPointers.Count == 1) { if (downTransitions != null) { downTransitions.Begin(); } if (onDown != null) { onDown.Invoke(); } } } } public override void OnPointerUp(PointerEventData eventData) { base.OnPointerUp(eventData); if (downPointers.Remove(eventData.pointerId) == true) { TryNormal(); DoClick(); } } public void OnBeginDrag(PointerEventData eventData) { parentScrollRect = GetComponentInParent(); parentDrag = GetComponentInParent(); if (parentScrollRect != null) { parentScrollRect.OnBeginDrag(eventData); } if (parentDrag != null) { parentDrag.OnBeginDrag(eventData); } } public void OnDrag(PointerEventData eventData) { if (downPointers.Contains(eventData.pointerId) == true) { totalDelta += eventData.delta; if (dragThreshold > 0.0f && totalDelta.magnitude > dragThreshold) { downPointers.Remove(eventData.pointerId); TryNormal(); } } if (parentScrollRect != null) { parentScrollRect.OnDrag(eventData); } if (parentDrag != null) { parentDrag.OnDrag(eventData); } } public void OnEndDrag(PointerEventData eventData) { if (parentScrollRect != null) { parentScrollRect.OnEndDrag(eventData); } if (parentDrag != null) { parentDrag.OnEndDrag(eventData); } } public void OnSubmit(BaseEventData eventData) { if (enabled == true) { DoClick(); } } public override void OnPointerExit(PointerEventData eventData) { base.OnPointerExit(eventData); if (dragThreshold == 0.0f) { downPointers.Remove(eventData.pointerId); TryNormal(); } } private void TryNormal() { if (downPointers.Count == 0) { if (normalTransitions != null) { normalTransitions.Begin(); } } } private void DoClick() { if (clickTransitions != null) { clickTransitions.Begin(); } if (onClick != null) { onClick.Invoke(); } } private bool RequiredButtonPressed(PointerEventData eventData) { if (LeanInput.GetMouseExists() == true) { if ((requiredButtons & ButtonTypes.LeftMouse) != 0 && eventData.button == PointerEventData.InputButton.Left) { return true; } if ((requiredButtons & ButtonTypes.RightMouse) != 0 && eventData.button == PointerEventData.InputButton.Right) { return true; } if ((requiredButtons & ButtonTypes.MiddleMouse) != 0 && eventData.button == PointerEventData.InputButton.Middle) { return true; } } if (LeanInput.GetTouchCount() > 0) { if ((requiredButtons & ButtonTypes.Touch) != 0 && eventData.button == PointerEventData.InputButton.Left) { return true; } } return false; } } } #if UNITY_EDITOR namespace Lean.Gui.Editor { using TARGET = LeanButton; [UnityEditor.CanEditMultipleObjects] [UnityEditor.CustomEditor(typeof(TARGET))] public class LeanButton_Editor : LeanSelectable_Editor { protected override void DrawSelectableSettings() { base.DrawSelectableSettings(); Draw("dragThreshold", "If your finger presses down on the button and drags more than this many pixels, then selection will be canceled.\n\n-1 = Unlimited drag distance.\n\n0 = Until the finger exits the button graphic."); Draw("requiredButtons", "Which buttons should this component react to?"); Draw("multiDown", "If you press multiple fingers on this button at the same time, should OnDown and DownTransition be invoked multiple times?"); } protected override void DrawSelectableTransitions(bool showUnusedEvents) { TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts); if (showUnusedEvents == true || Any(tgts, t => t.NormalTransitions.IsUsed == true)) { Draw("normalTransitions"); } if (showUnusedEvents == true || Any(tgts, t => t.DownTransitions.IsUsed == true)) { Draw("downTransitions"); } if (showUnusedEvents == true || Any(tgts, t => t.ClickTransitions.IsUsed == true)) { Draw("clickTransitions"); } base.DrawSelectableTransitions(showUnusedEvents); } protected override void DrawSelectableEvents(bool showUnusedEvents) { TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts); if (showUnusedEvents == true || Any(tgts, t => t.OnDown.GetPersistentEventCount() > 0)) { Draw("onDown"); } if (showUnusedEvents == true || Any(tgts, t => t.OnClick.GetPersistentEventCount() > 0)) { Draw("onClick"); } base.DrawSelectableEvents(showUnusedEvents); } } } #endif