using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; namespace UIWidgets { public class ComboboxCustom : MonoBehaviour, ISubmitHandler, IEventSystemHandler where TListViewCustom : ListViewCustom where TComponent : ListViewItem { [Serializable] public class ComboboxCustomEvent : UnityEvent { } [SerializeField] private TListViewCustom listView; [SerializeField] private Button toggleButton; [SerializeField] private TComponent current; public ComboboxCustomEvent OnSelect = new ComboboxCustomEvent(); private UnityAction onSelectCallback; private Transform listCanvas; private Transform listParent; protected List components = new List(); protected List componentsCache = new List(); [NonSerialized] private bool isStartedComboboxCustom; protected int? ModalKey; protected List childrenDeselect = new List(); protected List currentIndicies; public TListViewCustom ListView { get { return listView; } set { SetListView(value); } } public Button ToggleButton { get { return toggleButton; } set { SetToggleButton(value); } } public TComponent Current { get { return current; } set { current = value; } } private void Awake() { Start(); } public virtual void Start() { if (isStartedComboboxCustom) { return; } isStartedComboboxCustom = true; onSelectCallback = delegate(int index) { OnSelect.Invoke(index, listView.DataSource[index]); }; SetToggleButton(toggleButton); SetListView(listView); if (listView != null) { current.gameObject.SetActive(false); listView.OnSelectObject.RemoveListener(UpdateView); listView.OnDeselectObject.RemoveListener(UpdateView); listCanvas = Utilites.FindCanvas(listParent); listView.gameObject.SetActive(true); listView.Start(); if (listView.SelectedIndex == -1 && listView.DataSource.Count > 0 && !listView.Multiple) { listView.SelectedIndex = 0; } if (listView.SelectedIndex != -1) { UpdateView(); } listView.gameObject.SetActive(false); listView.OnSelectObject.AddListener(UpdateView); listView.OnDeselectObject.AddListener(UpdateView); } } protected virtual void SetToggleButton(Button value) { if (toggleButton != null) { toggleButton.onClick.RemoveListener(ToggleList); } toggleButton = value; if (toggleButton != null) { toggleButton.onClick.AddListener(ToggleList); } } protected virtual void SetListView(TListViewCustom value) { if (listView != null) { listParent = null; listView.OnSelectObject.RemoveListener(UpdateView); listView.OnDeselectObject.RemoveListener(UpdateView); listView.OnSelectObject.RemoveListener(onSelectCallback); listView.OnFocusOut.RemoveListener(onFocusHideList); listView.onCancel.RemoveListener(OnListViewCancel); listView.onItemCancel.RemoveListener(OnListViewCancel); RemoveDeselectCallbacks(); } listView = value; if (listView != null) { listParent = listView.transform.parent; listView.OnSelectObject.AddListener(UpdateView); listView.OnDeselectObject.AddListener(UpdateView); listView.OnSelectObject.AddListener(onSelectCallback); listView.OnFocusOut.AddListener(onFocusHideList); listView.onCancel.AddListener(OnListViewCancel); listView.onItemCancel.AddListener(OnListViewCancel); AddDeselectCallbacks(); } } public virtual int Set(TItem item, bool allowDuplicate = true) { return listView.Set(item, allowDuplicate); } public virtual void Clear() { listView.Clear(); UpdateView(); } public virtual void ToggleList() { if (!(listView == null)) { if (listView.gameObject.activeSelf) { HideList(); } else { ShowList(); } } } public virtual void ShowList() { if (!(listView == null)) { ModalKey = ModalHelper.Open(this, null, new Color(0f, 0f, 0f, 0f), HideList); if (listCanvas != null) { listParent = listView.transform.parent; listView.transform.SetParent(listCanvas); } listView.gameObject.SetActive(true); if (listView.Layout != null) { listView.Layout.UpdateLayout(); } if (listView.SelectComponent()) { SetChildDeselectListener(EventSystem.current.currentSelectedGameObject); } else { EventSystem.current.SetSelectedGameObject(listView.gameObject); } } } public virtual void HideList() { int? modalKey = ModalKey; if (modalKey.HasValue) { int? modalKey2 = ModalKey; ModalHelper.Close(modalKey2.Value); ModalKey = null; } if (!(listView == null)) { listView.gameObject.SetActive(false); if (listCanvas != null) { listView.transform.SetParent(listParent); } } } protected void onFocusHideList(BaseEventData eventData) { if (eventData.selectedObject == base.gameObject) { return; } ListViewItemEventData listViewItemEventData = eventData as ListViewItemEventData; if (listViewItemEventData != null) { if (listViewItemEventData.NewSelectedObject != null) { SetChildDeselectListener(listViewItemEventData.NewSelectedObject); } return; } PointerEventData pointerEventData = eventData as PointerEventData; if (pointerEventData == null) { HideList(); return; } GameObject gameObject = pointerEventData.pointerPressRaycast.gameObject; if (gameObject == null) { HideList(); } else if (!gameObject.Equals(toggleButton.gameObject)) { if (gameObject.transform.IsChildOf(listView.transform)) { SetChildDeselectListener(gameObject); } else { HideList(); } } } protected void SetChildDeselectListener(GameObject child) { SelectListener deselectListener = GetDeselectListener(child); if (!childrenDeselect.Contains(deselectListener)) { deselectListener.onDeselect.AddListener(onFocusHideList); childrenDeselect.Add(deselectListener); } } protected SelectListener GetDeselectListener(GameObject go) { return go.GetComponent() ?? go.AddComponent(); } protected void AddDeselectCallbacks() { if (!(listView.ScrollRect == null) && !(listView.ScrollRect.verticalScrollbar == null)) { GameObject go = listView.ScrollRect.verticalScrollbar.gameObject; SelectListener deselectListener = GetDeselectListener(go); deselectListener.onDeselect.AddListener(onFocusHideList); childrenDeselect.Add(deselectListener); } } protected void RemoveDeselectCallbacks() { childrenDeselect.ForEach(RemoveDeselectCallback); childrenDeselect.Clear(); } protected void RemoveDeselectCallback(SelectListener listener) { if (listener != null) { listener.onDeselect.RemoveListener(onFocusHideList); } } private void UpdateView(int index) { UpdateView(); } protected virtual void UpdateView() { TListViewCustom val = ListView; currentIndicies = val.SelectedIndicies; UpdateComponentsCount(); components.ForEach(SetData); HideList(); if (EventSystem.current != null && !EventSystem.current.alreadySelecting) { EventSystem.current.SetSelectedGameObject(base.gameObject); } } protected virtual void SetData(TComponent component, int i) { component.Index = currentIndicies[i]; TListViewCustom val = ListView; SetData(component, val.DataSource[currentIndicies[i]]); } protected virtual void SetData(TComponent component, TItem item) { } private void OnListViewCancel() { HideList(); } protected virtual void AddComponent(int index) { TComponent val; if (componentsCache.Count > 0) { val = componentsCache[componentsCache.Count - 1]; componentsCache.RemoveAt(componentsCache.Count - 1); } else { val = UnityEngine.Object.Instantiate(current); val.transform.SetParent(current.transform.parent); Utilites.FixInstantiated(current, val); } val.Index = -1; val.transform.SetAsLastSibling(); val.gameObject.SetActive(true); components.Add(val); } protected virtual void DeactivateComponent(TComponent component) { if (component != null) { component.MovedToCache(); component.Index = -1; component.gameObject.SetActive(false); } } protected void UpdateComponentsCount() { components.RemoveAll(IsNullComponent); if (components.Count != currentIndicies.Count) { if (components.Count < currentIndicies.Count) { componentsCache.RemoveAll(IsNullComponent); Enumerable.Range(0, currentIndicies.Count - components.Count).ForEach(AddComponent); return; } IOrderedEnumerable orderedEnumerable = components.GetRange(currentIndicies.Count, components.Count - currentIndicies.Count).OrderByDescending(GetComponentIndex); orderedEnumerable.ForEach(DeactivateComponent); componentsCache.AddRange(orderedEnumerable); components.RemoveRange(currentIndicies.Count, components.Count - currentIndicies.Count); } } protected bool IsNullComponent(TComponent component) { return component == null; } protected int GetComponentIndex(TComponent item) { return item.Index; } public virtual void OnSubmit(BaseEventData eventData) { ShowList(); } [Obsolete("Use SetData() instead.")] protected virtual void UpdateCurrent() { HideList(); } protected virtual void OnDestroy() { ListView = (TListViewCustom)null; ToggleButton = null; } } }