From d07e14add74e017b52ab2371efeea1aa4ea10ced Mon Sep 17 00:00:00 2001 From: chai Date: Sat, 8 May 2021 23:15:13 +0800 Subject: +init --- .../UnityEngine.UI/UI/Core/UIControls/Dropdown.cs | 649 +++++++++++++++++++++ 1 file changed, 649 insertions(+) create mode 100644 Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Dropdown.cs (limited to 'Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Dropdown.cs') diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Dropdown.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Dropdown.cs new file mode 100644 index 0000000..86bed37 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Dropdown.cs @@ -0,0 +1,649 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine.Events; +using UnityEngine.EventSystems; +using UnityEngine.UI.CoroutineTween; + +namespace UnityEngine.UI +{ + [AddComponentMenu("UI/Dropdown", 35)] + [RequireComponent(typeof(RectTransform))] + public class Dropdown : Selectable, IPointerClickHandler, ISubmitHandler, ICancelHandler + { + protected internal class DropdownItem : MonoBehaviour, IPointerEnterHandler, ICancelHandler + { + [SerializeField] + private Text m_Text; + [SerializeField] + private Image m_Image; + [SerializeField] + private RectTransform m_RectTransform; + [SerializeField] + private Toggle m_Toggle; + + public 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) + { + Dropdown dropdown = GetComponentInParent(); + if (dropdown) + dropdown.Hide(); + } + } + + [Serializable] + public class OptionData + { + [SerializeField] + private string m_Text; + [SerializeField] + private Sprite m_Image; + + public string text { get { return m_Text; } set { m_Text = value; } } + 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; + } + + public OptionData(string text, Sprite image) + { + this.text = text; + this.image = image; + } + } + + [Serializable] + public class OptionDataList + { + [SerializeField] + private List m_Options; + public List options { get { return m_Options; } set { m_Options = value; } } + + + public OptionDataList() + { + options = new List(); + } + } + + [Serializable] + public class DropdownEvent : UnityEvent {} + + // Template used to create the dropdown. + [SerializeField] + private RectTransform m_Template; + 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 Text m_CaptionText; + public Text captionText { get { return m_CaptionText; } set { m_CaptionText = value; RefreshShownValue(); } } + + [SerializeField] + private Image m_CaptionImage; + public Image captionImage { get { return m_CaptionImage; } set { m_CaptionImage = value; RefreshShownValue(); } } + + [Space] + + [SerializeField] + private Text m_ItemText; + public Text itemText { get { return m_ItemText; } set { m_ItemText = value; RefreshShownValue(); } } + + [SerializeField] + private Image m_ItemImage; + 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(); + public List 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(); + public DropdownEvent onValueChanged { get { return m_OnValueChanged; } set { m_OnValueChanged = value; } } + + private GameObject m_Dropdown; + private GameObject m_Blocker; + private List m_Items = new List(); + private TweenRunner m_AlphaTweenRunner; + private bool validTemplate = false; + + private static OptionData s_NoOptionData = new OptionData(); + + // Current value. + public int value + { + get + { + return m_Value; + } + set + { + if (Application.isPlaying && (value == m_Value || options.Count == 0)) + return; + + m_Value = Mathf.Clamp(value, 0, options.Count - 1); + RefreshShownValue(); + + // Notify all listeners + UISystemProfilerApi.AddMarker("Dropdown.value", this); + m_OnValueChanged.Invoke(m_Value); + } + } + + protected Dropdown() + {} + + protected override void Awake() + { + #if UNITY_EDITOR + if (!Application.isPlaying) + return; + #endif + + m_AlphaTweenRunner = new TweenRunner(); + m_AlphaTweenRunner.Init(this); + + if (m_CaptionImage) + m_CaptionImage.enabled = (m_CaptionImage.sprite != null); + + if (m_Template) + m_Template.gameObject.SetActive(false); + } + + #if UNITY_EDITOR + protected override void OnValidate() + { + base.OnValidate(); + + if (!IsActive()) + return; + + RefreshShownValue(); + } + + #endif + + 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); + } + } + + public void AddOptions(List options) + { + this.options.AddRange(options); + RefreshShownValue(); + } + + public void AddOptions(List options) + { + for (int i = 0; i < options.Count; i++) + this.options.Add(new OptionData(options[i])); + RefreshShownValue(); + } + + public void AddOptions(List options) + { + for (int i = 0; i < options.Count; i++) + this.options.Add(new OptionData(options[i])); + RefreshShownValue(); + } + + public void ClearOptions() + { + options.Clear(); + 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(); + + 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(); + item.text = m_ItemText; + item.image = m_ItemImage; + item.toggle = itemToggle; + item.rectTransform = (RectTransform)itemToggle.transform; + + Canvas popupCanvas = GetOrAddComponent(templateGo); + popupCanvas.overrideSorting = true; + popupCanvas.sortingOrder = 30000; + + GetOrAddComponent(templateGo); + GetOrAddComponent(templateGo); + templateGo.SetActive(false); + + validTemplate = true; + } + + private static T GetOrAddComponent(GameObject go) where T : Component + { + T comp = go.GetComponent(); + if (!comp) + comp = go.AddComponent(); + return comp; + } + + public virtual void OnPointerClick(PointerEventData eventData) + { + Show(); + } + + public virtual void OnSubmit(BaseEventData eventData) + { + Show(); + } + + public virtual void OnCancel(BaseEventData eventData) + { + Hide(); + } + + // 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 contains dropdowns. + public void Show() + { + if (!IsActive() || !IsInteractable() || m_Dropdown != null) + return; + + if (!validTemplate) + { + SetupTemplate(); + if (!validTemplate) + return; + } + + // Get root Canvas. + var list = ListPool.Get(); + gameObject.GetComponentsInParent(false, list); + if (list.Count == 0) + return; + Canvas rootCanvas = list[0]; + ListPool.Release(list); + + m_Template.gameObject.SetActive(true); + + // 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(); + + 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] || 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); + } + + 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(); + 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(); + blockerCanvas.overrideSorting = true; + Canvas dropdownCanvas = m_Dropdown.GetComponent(); + blockerCanvas.sortingLayerID = dropdownCanvas.sortingLayerID; + blockerCanvas.sortingOrder = dropdownCanvas.sortingOrder - 1; + + // Add raycaster since it's needed to block. + blocker.AddComponent(); + + // Add image since it's needed to block, but make it clear. + Image blockerImage = blocker.AddComponent(); + 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