summaryrefslogtreecommitdiff
path: root/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Dropdown.cs
diff options
context:
space:
mode:
authorAndrew Lee <alee14498@protonmail.com>2020-04-19 17:19:32 -0400
committerAndrew Lee <alee14498@protonmail.com>2020-04-19 17:19:32 -0400
commitc55fba8ab2a1c9d3df65eda4a5a1e957f4aa1f78 (patch)
treeee4d51c7c1d633e11f46453ef1edd3c77c4ef9f7 /Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Dropdown.cs
downloadProject-Sandbox-c55fba8ab2a1c9d3df65eda4a5a1e957f4aa1f78.tar.gz
Project-Sandbox-c55fba8ab2a1c9d3df65eda4a5a1e957f4aa1f78.tar.bz2
Project-Sandbox-c55fba8ab2a1c9d3df65eda4a5a1e957f4aa1f78.zip
Inital commit
Diffstat (limited to 'Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Dropdown.cs')
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Dropdown.cs1059
1 files changed, 1059 insertions, 0 deletions
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Dropdown.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Dropdown.cs
new file mode 100644
index 0000000..6424a5a
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Dropdown.cs
@@ -0,0 +1,1059 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+using UnityEngine.Events;
+using UnityEngine.EventSystems;
+using UnityEngine.UI.CoroutineTween;
+
+namespace TMPro
+{
+ [AddComponentMenu("UI/Dropdown - TextMeshPro", 35)]
+ [RequireComponent(typeof(RectTransform))]
+ /// <summary>
+ /// A standard dropdown that presents a list of options when clicked, of which one can be chosen.
+ /// </summary>
+ /// <remarks>
+ /// The dropdown component is a Selectable. When an option is chosen, the label and/or image of the control changes to show the chosen option.
+ ///
+ /// When a dropdown event occurs a callback is sent to any registered listeners of onValueChanged.
+ /// </remarks>
+ public class TMP_Dropdown : Selectable, IPointerClickHandler, ISubmitHandler, ICancelHandler
+ {
+ protected internal class DropdownItem : MonoBehaviour, IPointerEnterHandler, ICancelHandler
+ {
+ [SerializeField]
+ private TMP_Text m_Text;
+ [SerializeField]
+ private Image m_Image;
+ [SerializeField]
+ private RectTransform m_RectTransform;
+ [SerializeField]
+ private Toggle m_Toggle;
+
+ public TMP_Text text { get { return m_Text; } set { m_Text = value; } }
+ public Image image { get { return m_Image; } set { m_Image = value; } }
+ public RectTransform rectTransform { get { return m_RectTransform; } set { m_RectTransform = value; } }
+ public Toggle toggle { get { return m_Toggle; } set { m_Toggle = value; } }
+
+ public virtual void OnPointerEnter(PointerEventData eventData)
+ {
+ EventSystem.current.SetSelectedGameObject(gameObject);
+ }
+
+ public virtual void OnCancel(BaseEventData eventData)
+ {
+ TMP_Dropdown dropdown = GetComponentInParent<TMP_Dropdown>();
+ if (dropdown)
+ dropdown.Hide();
+ }
+ }
+
+ [Serializable]
+ /// <summary>
+ /// Class to store the text and/or image of a single option in the dropdown list.
+ /// </summary>
+ public class OptionData
+ {
+ [SerializeField]
+ private string m_Text;
+ [SerializeField]
+ private Sprite m_Image;
+
+ /// <summary>
+ /// The text associated with the option.
+ /// </summary>
+ public string text { get { return m_Text; } set { m_Text = value; } }
+
+ /// <summary>
+ /// The image associated with the option.
+ /// </summary>
+ public Sprite image { get { return m_Image; } set { m_Image = value; } }
+
+ public OptionData() { }
+
+ public OptionData(string text)
+ {
+ this.text = text;
+ }
+
+ public OptionData(Sprite image)
+ {
+ this.image = image;
+ }
+
+ /// <summary>
+ /// Create an object representing a single option for the dropdown list.
+ /// </summary>
+ /// <param name="text">Optional text for the option.</param>
+ /// <param name="image">Optional image for the option.</param>
+ public OptionData(string text, Sprite image)
+ {
+ this.text = text;
+ this.image = image;
+ }
+ }
+
+ [Serializable]
+ /// <summary>
+ /// Class used internally to store the list of options for the dropdown list.
+ /// </summary>
+ /// <remarks>
+ /// The usage of this class is not exposed in the runtime API. It's only relevant for the PropertyDrawer drawing the list of options.
+ /// </remarks>
+ public class OptionDataList
+ {
+ [SerializeField]
+ private List<OptionData> m_Options;
+
+ /// <summary>
+ /// The list of options for the dropdown list.
+ /// </summary>
+ public List<OptionData> options { get { return m_Options; } set { m_Options = value; } }
+
+
+ public OptionDataList()
+ {
+ options = new List<OptionData>();
+ }
+ }
+
+ [Serializable]
+ /// <summary>
+ /// UnityEvent callback for when a dropdown current option is changed.
+ /// </summary>
+ public class DropdownEvent : UnityEvent<int> { }
+
+ // Template used to create the dropdown.
+ [SerializeField]
+ private RectTransform m_Template;
+
+ /// <summary>
+ /// The Rect Transform of the template for the dropdown list.
+ /// </summary>
+ public RectTransform template { get { return m_Template; } set { m_Template = value; RefreshShownValue(); } }
+
+ // Text to be used as a caption for the current value. It's not required, but it's kept here for convenience.
+ [SerializeField]
+ private TMP_Text m_CaptionText;
+
+ /// <summary>
+ /// The Text component to hold the text of the currently selected option.
+ /// </summary>
+ public TMP_Text captionText { get { return m_CaptionText; } set { m_CaptionText = value; RefreshShownValue(); } }
+
+ [SerializeField]
+ private Image m_CaptionImage;
+
+ /// <summary>
+ /// The Image component to hold the image of the currently selected option.
+ /// </summary>
+ public Image captionImage { get { return m_CaptionImage; } set { m_CaptionImage = value; RefreshShownValue(); } }
+
+ [Space]
+
+ [SerializeField]
+ private TMP_Text m_ItemText;
+
+ /// <summary>
+ /// The Text component to hold the text of the item.
+ /// </summary>
+ public TMP_Text itemText { get { return m_ItemText; } set { m_ItemText = value; RefreshShownValue(); } }
+
+ [SerializeField]
+ private Image m_ItemImage;
+
+ /// <summary>
+ /// The Image component to hold the image of the item
+ /// </summary>
+ public Image itemImage { get { return m_ItemImage; } set { m_ItemImage = value; RefreshShownValue(); } }
+
+ [Space]
+
+ [SerializeField]
+ private int m_Value;
+
+ [Space]
+
+ // Items that will be visible when the dropdown is shown.
+ // We box this into its own class so we can use a Property Drawer for it.
+ [SerializeField]
+ private OptionDataList m_Options = new OptionDataList();
+
+ /// <summary>
+ /// The list of possible options. A text string and an image can be specified for each option.
+ /// </summary>
+ /// <remarks>
+ /// This is the list of options within the Dropdown. Each option contains Text and/or image data that you can specify using UI.Dropdown.OptionData before adding to the Dropdown list.
+ /// This also unlocks the ability to edit the Dropdown, including the insertion, removal, and finding of options, as well as other useful tools
+ /// </remarks>
+ /// /// <example>
+ /// <code>
+ /// //Create a new Dropdown GameObject by going to the Hierarchy and clicking Create>UI>Dropdown - TextMeshPro. Attach this script to the Dropdown GameObject.
+ ///
+ /// using UnityEngine;
+ /// using UnityEngine.UI;
+ /// using System.Collections.Generic;
+ /// using TMPro;
+ ///
+ /// public class Example : MonoBehaviour
+ /// {
+ /// //Use these for adding options to the Dropdown List
+ /// TMP_Dropdown.OptionData m_NewData, m_NewData2;
+ /// //The list of messages for the Dropdown
+ /// List<TMP_Dropdown.OptionData> m_Messages = new List<TMP_Dropdown.OptionData>();
+ ///
+ ///
+ /// //This is the Dropdown
+ /// TMP_Dropdown m_Dropdown;
+ /// string m_MyString;
+ /// int m_Index;
+ ///
+ /// void Start()
+ /// {
+ /// //Fetch the Dropdown GameObject the script is attached to
+ /// m_Dropdown = GetComponent<TMP_Dropdown>();
+ /// //Clear the old options of the Dropdown menu
+ /// m_Dropdown.ClearOptions();
+ ///
+ /// //Create a new option for the Dropdown menu which reads "Option 1" and add to messages List
+ /// m_NewData = new TMP_Dropdown.OptionData();
+ /// m_NewData.text = "Option 1";
+ /// m_Messages.Add(m_NewData);
+ ///
+ /// //Create a new option for the Dropdown menu which reads "Option 2" and add to messages List
+ /// m_NewData2 = new TMP_Dropdown.OptionData();
+ /// m_NewData2.text = "Option 2";
+ /// m_Messages.Add(m_NewData2);
+ ///
+ /// //Take each entry in the message List
+ /// foreach (TMP_Dropdown.OptionData message in m_Messages)
+ /// {
+ /// //Add each entry to the Dropdown
+ /// m_Dropdown.options.Add(message);
+ /// //Make the index equal to the total number of entries
+ /// m_Index = m_Messages.Count - 1;
+ /// }
+ /// }
+ ///
+ /// //This OnGUI function is used here for a quick demonstration. See the [[wiki:UISystem|UI Section]] for more information about setting up your own UI.
+ /// void OnGUI()
+ /// {
+ /// //TextField for user to type new entry to add to Dropdown
+ /// m_MyString = GUI.TextField(new Rect(0, 40, 100, 40), m_MyString);
+ ///
+ /// //Press the "Add" Button to add a new entry to the Dropdown
+ /// if (GUI.Button(new Rect(0, 0, 100, 40), "Add"))
+ /// {
+ /// //Make the index the last number of entries
+ /// m_Index = m_Messages.Count;
+ /// //Create a temporary option
+ /// TMP_Dropdown.OptionData temp = new TMP_Dropdown.OptionData();
+ /// //Make the option the data from the TextField
+ /// temp.text = m_MyString;
+ ///
+ /// //Update the messages list with the TextField data
+ /// m_Messages.Add(temp);
+ ///
+ /// //Add the Textfield data to the Dropdown
+ /// m_Dropdown.options.Insert(m_Index, temp);
+ /// }
+ ///
+ /// //Press the "Remove" button to delete the selected option
+ /// if (GUI.Button(new Rect(110, 0, 100, 40), "Remove"))
+ /// {
+ /// //Remove the current selected item from the Dropdown from the messages List
+ /// m_Messages.RemoveAt(m_Dropdown.value);
+ /// //Remove the current selection from the Dropdown
+ /// m_Dropdown.options.RemoveAt(m_Dropdown.value);
+ /// }
+ /// }
+ /// }
+ /// </code>
+ /// </example>
+ public List<OptionData> options
+ {
+ get { return m_Options.options; }
+ set { m_Options.options = value; RefreshShownValue(); }
+ }
+
+ [Space]
+
+ // Notification triggered when the dropdown changes.
+ [SerializeField]
+ private DropdownEvent m_OnValueChanged = new DropdownEvent();
+
+ /// <summary>
+ /// A UnityEvent that is invoked when a user has clicked one of the options in the dropdown list.
+ /// </summary>
+ /// <remarks>
+ /// Use this to detect when a user selects one or more options in the Dropdown. Add a listener to perform an action when this UnityEvent detects a selection by the user. See https://unity3d.com/learn/tutorials/topics/scripting/delegates for more information on delegates.
+ /// </remarks>
+ /// <example>
+ /// <code>
+ /// //Create a new Dropdown GameObject by going to the Hierarchy and clicking Create>UI>Dropdown - TextMeshPro. Attach this script to the Dropdown GameObject.
+ /// //Set your own Text in the Inspector window
+ ///
+ /// using UnityEngine;
+ /// using UnityEngine.UI;
+ /// using TMPro;
+ ///
+ /// public class Example : MonoBehaviour
+ /// {
+ /// TMP_Dropdown m_Dropdown;
+ /// public Text m_Text;
+ ///
+ /// void Start()
+ /// {
+ /// //Fetch the Dropdown GameObject
+ /// m_Dropdown = GetComponent<TMP_Dropdown>();
+ /// //Add listener for when the value of the Dropdown changes, to take action
+ /// m_Dropdown.onValueChanged.AddListener(delegate {
+ /// DropdownValueChanged(m_Dropdown);
+ /// });
+ ///
+ /// //Initialize the Text to say the first value of the Dropdown
+ /// m_Text.text = "First Value : " + m_Dropdown.value;
+ /// }
+ ///
+ /// //Output the new value of the Dropdown into Text
+ /// void DropdownValueChanged(TMP_Dropdown change)
+ /// {
+ /// m_Text.text = "New Value : " + change.value;
+ /// }
+ /// }
+ /// </code>
+ /// </example>
+ public DropdownEvent onValueChanged { get { return m_OnValueChanged; } set { m_OnValueChanged = value; } }
+
+ private GameObject m_Dropdown;
+ private GameObject m_Blocker;
+ private List<DropdownItem> m_Items = new List<DropdownItem>();
+ private TweenRunner<FloatTween> m_AlphaTweenRunner;
+ private bool validTemplate = false;
+
+ private static OptionData s_NoOptionData = new OptionData();
+
+ /// <summary>
+ /// The Value is the index number of the current selection in the Dropdown. 0 is the first option in the Dropdown, 1 is the second, and so on.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// //Create a new Dropdown GameObject by going to the Hierarchy and clicking Create>UI>Dropdown - TextMeshPro. Attach this script to the Dropdown GameObject.
+ /// //Set your own Text in the Inspector window
+ ///
+ /// using UnityEngine;
+ /// using UnityEngine.UI;
+ /// using TMPro;
+ ///
+ /// public class Example : MonoBehaviour
+ /// {
+ /// //Attach this script to a Dropdown GameObject
+ /// TMP_Dropdown m_Dropdown;
+ /// //This is the string that stores the current selection m_Text of the Dropdown
+ /// string m_Message;
+ /// //This Text outputs the current selection to the screen
+ /// public Text m_Text;
+ /// //This is the index value of the Dropdown
+ /// int m_DropdownValue;
+ ///
+ /// void Start()
+ /// {
+ /// //Fetch the DropDown component from the GameObject
+ /// m_Dropdown = GetComponent<TMP_Dropdown>();
+ /// //Output the first Dropdown index value
+ /// Debug.Log("Starting Dropdown Value : " + m_Dropdown.value);
+ /// }
+ ///
+ /// void Update()
+ /// {
+ /// //Keep the current index of the Dropdown in a variable
+ /// m_DropdownValue = m_Dropdown.value;
+ /// //Change the message to say the name of the current Dropdown selection using the value
+ /// m_Message = m_Dropdown.options[m_DropdownValue].text;
+ /// //Change the on screen Text to reflect the current Dropdown selection
+ /// m_Text.text = m_Message;
+ /// }
+ /// }
+ /// </code>
+ /// </example>
+ public int value
+ {
+ get
+ {
+ return m_Value;
+ }
+ set
+ {
+ SetValue(value);
+ }
+ }
+
+ /// <summary>
+ /// Set index number of the current selection in the Dropdown without invoking onValueChanged callback.
+ /// </summary>
+ /// <param name="input">The new index for the current selection.</param>
+ public void SetValueWithoutNotify(int input)
+ {
+ SetValue(input, false);
+ }
+
+ void SetValue(int value, bool sendCallback = true)
+ {
+ if (Application.isPlaying && (value == m_Value || options.Count == 0))
+ return;
+
+ m_Value = Mathf.Clamp(value, 0, options.Count - 1);
+ RefreshShownValue();
+
+ if (sendCallback)
+ {
+ // Notify all listeners
+ UISystemProfilerApi.AddMarker("Dropdown.value", this);
+ m_OnValueChanged.Invoke(m_Value);
+ }
+ }
+
+ public bool IsExpanded { get { return m_Dropdown != null; } }
+
+ protected TMP_Dropdown() { }
+
+ protected override void Awake()
+ {
+ #if UNITY_EDITOR
+ if (!Application.isPlaying)
+ return;
+ #endif
+
+ m_AlphaTweenRunner = new TweenRunner<FloatTween>();
+ m_AlphaTweenRunner.Init(this);
+
+ if (m_CaptionImage)
+ m_CaptionImage.enabled = (m_CaptionImage.sprite != null);
+
+ if (m_Template)
+ m_Template.gameObject.SetActive(false);
+ }
+
+ protected override void Start()
+ {
+ base.Start();
+
+ RefreshShownValue();
+ }
+
+ #if UNITY_EDITOR
+ protected override void OnValidate()
+ {
+ base.OnValidate();
+
+ if (!IsActive())
+ return;
+
+ RefreshShownValue();
+ }
+ #endif
+
+ protected override void OnDisable()
+ {
+ //Destroy dropdown and blocker in case user deactivates the dropdown when they click an option (case 935649)
+ ImmediateDestroyDropdownList();
+
+ if (m_Blocker != null)
+ DestroyBlocker(m_Blocker);
+ m_Blocker = null;
+ }
+
+ /// <summary>
+ /// Refreshes the text and image (if available) of the currently selected option.
+ /// </summary>
+ /// <remarks>
+ /// If you have modified the list of options, you should call this method afterwards to ensure that the visual state of the dropdown corresponds to the updated options.
+ /// </remarks>
+ public void RefreshShownValue()
+ {
+ OptionData data = s_NoOptionData;
+
+ if (options.Count > 0)
+ data = options[Mathf.Clamp(m_Value, 0, options.Count - 1)];
+
+ if (m_CaptionText)
+ {
+ if (data != null && data.text != null)
+ m_CaptionText.text = data.text;
+ else
+ m_CaptionText.text = "";
+ }
+
+ if (m_CaptionImage)
+ {
+ if (data != null)
+ m_CaptionImage.sprite = data.image;
+ else
+ m_CaptionImage.sprite = null;
+ m_CaptionImage.enabled = (m_CaptionImage.sprite != null);
+ }
+ }
+
+ /// <summary>
+ /// Add multiple options to the options of the Dropdown based on a list of OptionData objects.
+ /// </summary>
+ /// <param name="options">The list of OptionData to add.</param>
+ /// /// <remarks>
+ /// See AddOptions(List<string> options) for code example of usages.
+ /// </remarks>
+ public void AddOptions(List<OptionData> options)
+ {
+ this.options.AddRange(options);
+ RefreshShownValue();
+ }
+
+ /// <summary>
+ /// Add multiple text-only options to the options of the Dropdown based on a list of strings.
+ /// </summary>
+ /// <remarks>
+ /// Add a List of string messages to the Dropdown. The Dropdown shows each member of the list as a separate option.
+ /// </remarks>
+ /// <param name="options">The list of text strings to add.</param>
+ /// <example>
+ /// <code>
+ /// //Create a new Dropdown GameObject by going to the Hierarchy and clicking Create>UI>Dropdown - TextMeshPro. Attach this script to the Dropdown GameObject.
+ ///
+ /// using System.Collections.Generic;
+ /// using UnityEngine;
+ /// using UnityEngine.UI;
+ /// using TMPro;
+ ///
+ /// public class Example : MonoBehaviour
+ /// {
+ /// //Create a List of new Dropdown options
+ /// List<string> m_DropOptions = new List<string> { "Option 1", "Option 2"};
+ /// //This is the Dropdown
+ /// TMP_Dropdown m_Dropdown;
+ ///
+ /// void Start()
+ /// {
+ /// //Fetch the Dropdown GameObject the script is attached to
+ /// m_Dropdown = GetComponent<TMP_Dropdown>();
+ /// //Clear the old options of the Dropdown menu
+ /// m_Dropdown.ClearOptions();
+ /// //Add the options created in the List above
+ /// m_Dropdown.AddOptions(m_DropOptions);
+ /// }
+ /// }
+ /// </code>
+ /// </example>
+ public void AddOptions(List<string> options)
+ {
+ for (int i = 0; i < options.Count; i++)
+ this.options.Add(new OptionData(options[i]));
+
+ RefreshShownValue();
+ }
+
+ /// <summary>
+ /// Add multiple image-only options to the options of the Dropdown based on a list of Sprites.
+ /// </summary>
+ /// <param name="options">The list of Sprites to add.</param>
+ /// <remarks>
+ /// See AddOptions(List<string> options) for code example of usages.
+ /// </remarks>
+ public void AddOptions(List<Sprite> options)
+ {
+ for (int i = 0; i < options.Count; i++)
+ this.options.Add(new OptionData(options[i]));
+
+ RefreshShownValue();
+ }
+
+ /// <summary>
+ /// Clear the list of options in the Dropdown.
+ /// </summary>
+ public void ClearOptions()
+ {
+ options.Clear();
+ m_Value = 0;
+ RefreshShownValue();
+ }
+
+ private void SetupTemplate()
+ {
+ validTemplate = false;
+
+ if (!m_Template)
+ {
+ Debug.LogError("The dropdown template is not assigned. The template needs to be assigned and must have a child GameObject with a Toggle component serving as the item.", this);
+ return;
+ }
+
+ GameObject templateGo = m_Template.gameObject;
+ templateGo.SetActive(true);
+ Toggle itemToggle = m_Template.GetComponentInChildren<Toggle>();
+
+ validTemplate = true;
+ if (!itemToggle || itemToggle.transform == template)
+ {
+ validTemplate = false;
+ Debug.LogError("The dropdown template is not valid. The template must have a child GameObject with a Toggle component serving as the item.", template);
+ }
+ else if (!(itemToggle.transform.parent is RectTransform))
+ {
+ validTemplate = false;
+ Debug.LogError("The dropdown template is not valid. The child GameObject with a Toggle component (the item) must have a RectTransform on its parent.", template);
+ }
+ else if (itemText != null && !itemText.transform.IsChildOf(itemToggle.transform))
+ {
+ validTemplate = false;
+ Debug.LogError("The dropdown template is not valid. The Item Text must be on the item GameObject or children of it.", template);
+ }
+ else if (itemImage != null && !itemImage.transform.IsChildOf(itemToggle.transform))
+ {
+ validTemplate = false;
+ Debug.LogError("The dropdown template is not valid. The Item Image must be on the item GameObject or children of it.", template);
+ }
+
+ if (!validTemplate)
+ {
+ templateGo.SetActive(false);
+ return;
+ }
+
+ DropdownItem item = itemToggle.gameObject.AddComponent<DropdownItem>();
+ item.text = m_ItemText;
+ item.image = m_ItemImage;
+ item.toggle = itemToggle;
+ item.rectTransform = (RectTransform)itemToggle.transform;
+
+ Canvas popupCanvas = GetOrAddComponent<Canvas>(templateGo);
+ popupCanvas.overrideSorting = true;
+ popupCanvas.sortingOrder = 30000;
+
+ GetOrAddComponent<GraphicRaycaster>(templateGo);
+ GetOrAddComponent<CanvasGroup>(templateGo);
+ templateGo.SetActive(false);
+
+ validTemplate = true;
+ }
+
+ private static T GetOrAddComponent<T>(GameObject go) where T : Component
+ {
+ T comp = go.GetComponent<T>();
+ if (!comp)
+ comp = go.AddComponent<T>();
+ return comp;
+ }
+
+ /// <summary>
+ /// Handling for when the dropdown is initially 'clicked'. Typically shows the dropdown
+ /// </summary>
+ /// <param name="eventData">The associated event data.</param>
+ public virtual void OnPointerClick(PointerEventData eventData)
+ {
+ Show();
+ }
+
+ /// <summary>
+ /// Handling for when the dropdown is selected and a submit event is processed. Typically shows the dropdown
+ /// </summary>
+ /// <param name="eventData">The associated event data.</param>
+ public virtual void OnSubmit(BaseEventData eventData)
+ {
+ Show();
+ }
+
+ /// <summary>
+ /// This will hide the dropdown list.
+ /// </summary>
+ /// <remarks>
+ /// Called by a BaseInputModule when a Cancel event occurs.
+ /// </remarks>
+ /// <param name="eventData">The associated event data.</param>
+ public virtual void OnCancel(BaseEventData eventData)
+ {
+ Hide();
+ }
+
+ /// <summary>
+ /// Show the dropdown.
+ ///
+ /// Plan for dropdown scrolling to ensure dropdown is contained within screen.
+ ///
+ /// We assume the Canvas is the screen that the dropdown must be kept inside.
+ /// This is always valid for screen space canvas modes.
+ /// For world space canvases we don't know how it's used, but it could be e.g. for an in-game monitor.
+ /// We consider it a fair constraint that the canvas must be big enough to contain dropdowns.
+ /// </summary>
+ public void Show()
+ {
+ if (!IsActive() || !IsInteractable() || m_Dropdown != null)
+ return;
+
+ // Get root Canvas.
+ var list = TMP_ListPool<Canvas>.Get();
+ gameObject.GetComponentsInParent(false, list);
+ if (list.Count == 0)
+ return;
+
+ Canvas rootCanvas = list[list.Count - 1];
+ for (int i = 0; i < list.Count; i++)
+ {
+ if (list[i].isRootCanvas)
+ {
+ rootCanvas = list[i];
+ break;
+ }
+ }
+
+ TMP_ListPool<Canvas>.Release(list);
+
+ if (!validTemplate)
+ {
+ SetupTemplate();
+ if (!validTemplate)
+ return;
+ }
+
+ m_Template.gameObject.SetActive(true);
+
+ // popupCanvas used to assume the root canvas had the default sorting Layer, next line fixes (case 958281 - [UI] Dropdown list does not copy the parent canvas layer when the panel is opened)
+ m_Template.GetComponent<Canvas>().sortingLayerID = rootCanvas.sortingLayerID;
+
+ // Instantiate the drop-down template
+ m_Dropdown = CreateDropdownList(m_Template.gameObject);
+ m_Dropdown.name = "Dropdown List";
+ m_Dropdown.SetActive(true);
+
+ // Make drop-down RectTransform have same values as original.
+ RectTransform dropdownRectTransform = m_Dropdown.transform as RectTransform;
+ dropdownRectTransform.SetParent(m_Template.transform.parent, false);
+
+ // Instantiate the drop-down list items
+
+ // Find the dropdown item and disable it.
+ DropdownItem itemTemplate = m_Dropdown.GetComponentInChildren<DropdownItem>();
+
+ GameObject content = itemTemplate.rectTransform.parent.gameObject;
+ RectTransform contentRectTransform = content.transform as RectTransform;
+ itemTemplate.rectTransform.gameObject.SetActive(true);
+
+ // Get the rects of the dropdown and item
+ Rect dropdownContentRect = contentRectTransform.rect;
+ Rect itemTemplateRect = itemTemplate.rectTransform.rect;
+
+ // Calculate the visual offset between the item's edges and the background's edges
+ Vector2 offsetMin = itemTemplateRect.min - dropdownContentRect.min + (Vector2)itemTemplate.rectTransform.localPosition;
+ Vector2 offsetMax = itemTemplateRect.max - dropdownContentRect.max + (Vector2)itemTemplate.rectTransform.localPosition;
+ Vector2 itemSize = itemTemplateRect.size;
+
+ m_Items.Clear();
+
+ Toggle prev = null;
+ for (int i = 0; i < options.Count; ++i)
+ {
+ OptionData data = options[i];
+ DropdownItem item = AddItem(data, value == i, itemTemplate, m_Items);
+ if (item == null)
+ continue;
+
+ // Automatically set up a toggle state change listener
+ item.toggle.isOn = value == i;
+ item.toggle.onValueChanged.AddListener(x => OnSelectItem(item.toggle));
+
+ // Select current option
+ if (item.toggle.isOn)
+ item.toggle.Select();
+
+ // Automatically set up explicit navigation
+ if (prev != null)
+ {
+ Navigation prevNav = prev.navigation;
+ Navigation toggleNav = item.toggle.navigation;
+ prevNav.mode = Navigation.Mode.Explicit;
+ toggleNav.mode = Navigation.Mode.Explicit;
+
+ prevNav.selectOnDown = item.toggle;
+ prevNav.selectOnRight = item.toggle;
+ toggleNav.selectOnLeft = prev;
+ toggleNav.selectOnUp = prev;
+
+ prev.navigation = prevNav;
+ item.toggle.navigation = toggleNav;
+ }
+ prev = item.toggle;
+ }
+
+ // Reposition all items now that all of them have been added
+ Vector2 sizeDelta = contentRectTransform.sizeDelta;
+ sizeDelta.y = itemSize.y * m_Items.Count + offsetMin.y - offsetMax.y;
+ contentRectTransform.sizeDelta = sizeDelta;
+
+ float extraSpace = dropdownRectTransform.rect.height - contentRectTransform.rect.height;
+ if (extraSpace > 0)
+ dropdownRectTransform.sizeDelta = new Vector2(dropdownRectTransform.sizeDelta.x, dropdownRectTransform.sizeDelta.y - extraSpace);
+
+ // Invert anchoring and position if dropdown is partially or fully outside of canvas rect.
+ // Typically this will have the effect of placing the dropdown above the button instead of below,
+ // but it works as inversion regardless of initial setup.
+ Vector3[] corners = new Vector3[4];
+ dropdownRectTransform.GetWorldCorners(corners);
+
+ RectTransform rootCanvasRectTransform = rootCanvas.transform as RectTransform;
+ Rect rootCanvasRect = rootCanvasRectTransform.rect;
+ for (int axis = 0; axis < 2; axis++)
+ {
+ bool outside = false;
+ for (int i = 0; i < 4; i++)
+ {
+ Vector3 corner = rootCanvasRectTransform.InverseTransformPoint(corners[i]);
+ if ((corner[axis] < rootCanvasRect.min[axis] && !Mathf.Approximately(corner[axis], rootCanvasRect.min[axis])) ||
+ (corner[axis] > rootCanvasRect.max[axis] && !Mathf.Approximately(corner[axis], rootCanvasRect.max[axis])))
+ {
+ outside = true;
+ break;
+ }
+ }
+ if (outside)
+ RectTransformUtility.FlipLayoutOnAxis(dropdownRectTransform, axis, false, false);
+ }
+
+ for (int i = 0; i < m_Items.Count; i++)
+ {
+ RectTransform itemRect = m_Items[i].rectTransform;
+ itemRect.anchorMin = new Vector2(itemRect.anchorMin.x, 0);
+ itemRect.anchorMax = new Vector2(itemRect.anchorMax.x, 0);
+ itemRect.anchoredPosition = new Vector2(itemRect.anchoredPosition.x, offsetMin.y + itemSize.y * (m_Items.Count - 1 - i) + itemSize.y * itemRect.pivot.y);
+ itemRect.sizeDelta = new Vector2(itemRect.sizeDelta.x, itemSize.y);
+ }
+
+ // Fade in the popup
+ AlphaFadeList(0.15f, 0f, 1f);
+
+ // Make drop-down template and item template inactive
+ m_Template.gameObject.SetActive(false);
+ itemTemplate.gameObject.SetActive(false);
+
+ m_Blocker = CreateBlocker(rootCanvas);
+ }
+
+ /// <summary>
+ /// Create a blocker that blocks clicks to other controls while the dropdown list is open.
+ /// </summary>
+ /// <remarks>
+ /// Override this method to implement a different way to obtain a blocker GameObject.
+ /// </remarks>
+ /// <param name="rootCanvas">The root canvas the dropdown is under.</param>
+ /// <returns>The created blocker object</returns>
+ protected virtual GameObject CreateBlocker(Canvas rootCanvas)
+ {
+ // Create blocker GameObject.
+ GameObject blocker = new GameObject("Blocker");
+
+ // Setup blocker RectTransform to cover entire root canvas area.
+ RectTransform blockerRect = blocker.AddComponent<RectTransform>();
+ blockerRect.SetParent(rootCanvas.transform, false);
+ blockerRect.anchorMin = Vector3.zero;
+ blockerRect.anchorMax = Vector3.one;
+ blockerRect.sizeDelta = Vector2.zero;
+
+ // Make blocker be in separate canvas in same layer as dropdown and in layer just below it.
+ Canvas blockerCanvas = blocker.AddComponent<Canvas>();
+ blockerCanvas.overrideSorting = true;
+ Canvas dropdownCanvas = m_Dropdown.GetComponent<Canvas>();
+ blockerCanvas.sortingLayerID = dropdownCanvas.sortingLayerID;
+ blockerCanvas.sortingOrder = dropdownCanvas.sortingOrder - 1;
+
+ // Add raycaster since it's needed to block.
+ blocker.AddComponent<GraphicRaycaster>();
+
+ // Add image since it's needed to block, but make it clear.
+ Image blockerImage = blocker.AddComponent<Image>();
+ blockerImage.color = Color.clear;
+
+ // Add button since it's needed to block, and to close the dropdown when blocking area is clicked.
+ Button blockerButton = blocker.AddComponent<Button>();
+ blockerButton.onClick.AddListener(Hide);
+
+ return blocker;
+ }
+
+ /// <summary>
+ /// Convenience method to explicitly destroy the previously generated blocker object
+ /// </summary>
+ /// <remarks>
+ /// Override this method to implement a different way to dispose of a blocker GameObject that blocks clicks to other controls while the dropdown list is open.
+ /// </remarks>
+ /// <param name="blocker">The blocker object to destroy.</param>
+ protected virtual void DestroyBlocker(GameObject blocker)
+ {
+ Destroy(blocker);
+ }
+
+ /// <summary>
+ /// Create the dropdown list to be shown when the dropdown is clicked. The dropdown list should correspond to the provided template GameObject, equivalent to instantiating a copy of it.
+ /// </summary>
+ /// <remarks>
+ /// Override this method to implement a different way to obtain a dropdown list GameObject.
+ /// </remarks>
+ /// <param name="template">The template to create the dropdown list from.</param>
+ /// <returns>The created drop down list gameobject.</returns>
+ protected virtual GameObject CreateDropdownList(GameObject template)
+ {
+ return (GameObject)Instantiate(template);
+ }
+
+ /// <summary>
+ /// Convenience method to explicitly destroy the previously generated dropdown list
+ /// </summary>
+ /// <remarks>
+ /// Override this method to implement a different way to dispose of a dropdown list GameObject.
+ /// </remarks>
+ /// <param name="dropdownList">The dropdown list GameObject to destroy</param>
+ protected virtual void DestroyDropdownList(GameObject dropdownList)
+ {
+ Destroy(dropdownList);
+ }
+
+ /// <summary>
+ /// Create a dropdown item based upon the item template.
+ /// </summary>
+ /// <remarks>
+ /// Override this method to implement a different way to obtain an option item.
+ /// The option item should correspond to the provided template DropdownItem and its GameObject, equivalent to instantiating a copy of it.
+ /// </remarks>
+ /// <param name="itemTemplate">e template to create the option item from.</param>
+ /// <returns>The created dropdown item component</returns>
+ protected virtual DropdownItem CreateItem(DropdownItem itemTemplate)
+ {
+ return (DropdownItem)Instantiate(itemTemplate);
+ }
+
+ /// <summary>
+ /// Convenience method to explicitly destroy the previously generated Items.
+ /// </summary>
+ /// <remarks>
+ /// Override this method to implement a different way to dispose of an option item.
+ /// Likely no action needed since destroying the dropdown list destroys all contained items as well.
+ /// </remarks>
+ /// <param name="item">The Item to destroy.</param>
+ protected virtual void DestroyItem(DropdownItem item) { }
+
+ // Add a new drop-down list item with the specified values.
+ private DropdownItem AddItem(OptionData data, bool selected, DropdownItem itemTemplate, List<DropdownItem> items)
+ {
+ // Add a new item to the dropdown.
+ DropdownItem item = CreateItem(itemTemplate);
+ item.rectTransform.SetParent(itemTemplate.rectTransform.parent, false);
+
+ item.gameObject.SetActive(true);
+ item.gameObject.name = "Item " + items.Count + (data.text != null ? ": " + data.text : "");
+
+ if (item.toggle != null)
+ {
+ item.toggle.isOn = false;
+ }
+
+ // Set the item's data
+ if (item.text)
+ item.text.text = data.text;
+ if (item.image)
+ {
+ item.image.sprite = data.image;
+ item.image.enabled = (item.image.sprite != null);
+ }
+
+ items.Add(item);
+ return item;
+ }
+
+ private void AlphaFadeList(float duration, float alpha)
+ {
+ CanvasGroup group = m_Dropdown.GetComponent<CanvasGroup>();
+ AlphaFadeList(duration, group.alpha, alpha);
+ }
+
+ private void AlphaFadeList(float duration, float start, float end)
+ {
+ if (end.Equals(start))
+ return;
+
+ FloatTween tween = new FloatTween { duration = duration, startValue = start, targetValue = end };
+ tween.AddOnChangedCallback(SetAlpha);
+ tween.ignoreTimeScale = true;
+ m_AlphaTweenRunner.StartTween(tween);
+ }
+
+ private void SetAlpha(float alpha)
+ {
+ if (!m_Dropdown)
+ return;
+ CanvasGroup group = m_Dropdown.GetComponent<CanvasGroup>();
+ group.alpha = alpha;
+ }
+
+ /// <summary>
+ /// Hide the dropdown list. I.e. close it.
+ /// </summary>
+ public void Hide()
+ {
+ if (m_Dropdown != null)
+ {
+ AlphaFadeList(0.15f, 0f);
+
+ // User could have disabled the dropdown during the OnValueChanged call.
+ if (IsActive())
+ StartCoroutine(DelayedDestroyDropdownList(0.15f));
+ }
+ if (m_Blocker != null)
+ DestroyBlocker(m_Blocker);
+ m_Blocker = null;
+ Select();
+ }
+
+ private IEnumerator DelayedDestroyDropdownList(float delay)
+ {
+ yield return new WaitForSecondsRealtime(delay);
+ ImmediateDestroyDropdownList();
+ }
+
+ private void ImmediateDestroyDropdownList()
+ {
+ for (int i = 0; i < m_Items.Count; i++)
+ {
+ if (m_Items[i] != null)
+ DestroyItem(m_Items[i]);
+ }
+ m_Items.Clear();
+
+ if (m_Dropdown != null)
+ DestroyDropdownList(m_Dropdown);
+ m_Dropdown = null;
+ }
+
+ // Change the value and hide the dropdown.
+ private void OnSelectItem(Toggle toggle)
+ {
+ if (!toggle.isOn)
+ toggle.isOn = true;
+
+ int selectedIndex = -1;
+ Transform tr = toggle.transform;
+ Transform parent = tr.parent;
+ for (int i = 0; i < parent.childCount; i++)
+ {
+ if (parent.GetChild(i) == tr)
+ {
+ // Subtract one to account for template child.
+ selectedIndex = i - 1;
+ break;
+ }
+ }
+
+ if (selectedIndex < 0)
+ return;
+
+ value = selectedIndex;
+ Hide();
+ }
+ }
+} \ No newline at end of file