using UnityEngine; using UnityEngine.UI; using UnityEngine.Events; using UnityEngine.EventSystems; using System.Collections.Generic; using System.Linq; using System; namespace UIWidgets { /// /// Base class for custom combobox. /// public class ComboboxCustom : MonoBehaviour, ISubmitHandler where TListViewCustom : ListViewCustom where TComponent : ListViewItem { /// /// Custom Combobox event. /// [System.Serializable] public class ComboboxCustomEvent : UnityEvent { } [SerializeField] TListViewCustom listView; /// /// Gets or sets the ListView. /// /// ListView component. public TListViewCustom ListView { get { return listView; } set { SetListView(value); } } [SerializeField] Button toggleButton; /// /// Gets or sets the toggle button. /// /// The toggle button. public Button ToggleButton { get { return toggleButton; } set { SetToggleButton(value); } } [SerializeField] TComponent current; /// /// Gets or sets the current component. /// /// The current. public TComponent Current { get { return current; } set { current = value; } } /// /// OnSelect event. /// public ComboboxCustomEvent OnSelect = new ComboboxCustomEvent(); UnityAction onSelectCallback; Transform listCanvas; Transform listParent; /// /// The components list. /// protected List components = new List(); /// /// The components cache list. /// protected List componentsCache = new List(); void Awake() { ListView.Sort = false; Start(); } [System.NonSerialized] bool isStartedComboboxCustom; /// /// Start this instance. /// public virtual void Start() { if (isStartedComboboxCustom) { return ; } isStartedComboboxCustom = true; onSelectCallback = 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.FindTopmostCanvas(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); } } /// /// Sets the toggle button. /// /// Value. protected virtual void SetToggleButton(Button value) { if (toggleButton!=null) { toggleButton.onClick.RemoveListener(ToggleList); } toggleButton = value; if (toggleButton!=null) { toggleButton.onClick.AddListener(ToggleList); } } /// /// Sets the list view. /// /// Value. 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(); } } /// /// Set the specified item. /// /// Item. /// If set to true allow duplicate. /// Index of item. public virtual int Set(TItem item, bool allowDuplicate=true) { return listView.Set(item, allowDuplicate); } /// /// Clear listview and selected item. /// public virtual void Clear() { listView.Clear(); UpdateView(); } /// /// Toggles the list visibility. /// public virtual void ToggleList() { if (listView==null) { return ; } if (listView.gameObject.activeSelf) { HideList(); } else { ShowList(); } } /// /// The modal key. /// protected int? ModalKey; /// /// Shows the list. /// public virtual void ShowList() { if (listView==null) { return ; } listView.gameObject.SetActive(true); ModalKey = ModalHelper.Open(this, null, new Color(0, 0, 0, 0f), HideList); if (listCanvas!=null) { listParent = listView.transform.parent; listView.transform.SetParent(listCanvas); } if (listView.Layout!=null) { listView.Layout.UpdateLayout(); } if (listView.SelectComponent()) { SetChildDeselectListener(EventSystem.current.currentSelectedGameObject); } else { EventSystem.current.SetSelectedGameObject(listView.gameObject); } } /// /// Hides the list. /// public virtual void HideList() { if (ModalKey!=null) { ModalHelper.Close((int)ModalKey); ModalKey = null; } if (listView==null) { return ; } if (listCanvas!=null) { listView.transform.SetParent(listParent); } listView.gameObject.SetActive(false); } /// /// The children deselect. /// protected List childrenDeselect = new List(); /// /// Hide list when focus lost. /// /// Event data. protected void onFocusHideList(BaseEventData eventData) { if (eventData.selectedObject==gameObject) { return ; } var ev_item = eventData as ListViewItemEventData; if (ev_item!=null) { if (ev_item.NewSelectedObject!=null) { SetChildDeselectListener(ev_item.NewSelectedObject); } return ; } var ev = eventData as PointerEventData; if (ev==null) { HideList(); return ; } var go = ev.pointerPressRaycast.gameObject;//ev.pointerEnter if (go==null) { HideList(); return ; } if (go.Equals(toggleButton.gameObject)) { return ; } if (go.transform.IsChildOf(listView.transform)) { SetChildDeselectListener(go); return ; } HideList(); } /// /// Sets the child deselect listener. /// /// Child. protected void SetChildDeselectListener(GameObject child) { var deselectListener = GetDeselectListener(child); if (!childrenDeselect.Contains(deselectListener)) { deselectListener.onDeselect.AddListener(onFocusHideList); childrenDeselect.Add(deselectListener); } } /// /// Gets the deselect listener. /// /// The deselect listener. /// Go. protected SelectListener GetDeselectListener(GameObject go) { var result = go.GetComponent(); return (result!=null) ? result : go.AddComponent(); } /// /// Adds the deselect callbacks. /// protected void AddDeselectCallbacks() { if (listView.ScrollRect==null) { return ; } if (listView.ScrollRect.verticalScrollbar==null) { return ; } var scrollbar = listView.ScrollRect.verticalScrollbar.gameObject; var deselectListener = GetDeselectListener(scrollbar); deselectListener.onDeselect.AddListener(onFocusHideList); childrenDeselect.Add(deselectListener); } /// /// Removes the deselect callbacks. /// protected void RemoveDeselectCallbacks() { childrenDeselect.ForEach(RemoveDeselectCallback); childrenDeselect.Clear(); } /// /// Removes the deselect callback. /// /// Listener. protected void RemoveDeselectCallback(SelectListener listener) { if (listener!=null) { listener.onDeselect.RemoveListener(onFocusHideList); } } void UpdateView(int index) { UpdateView(); } /// /// The current indicies. /// protected List currentIndicies; /// /// Updates the view. /// protected virtual void UpdateView() { currentIndicies = ListView.SelectedIndicies; UpdateComponentsCount(); components.ForEach(SetData); HideList(); if ((EventSystem.current!=null) && (!EventSystem.current.alreadySelecting)) { EventSystem.current.SetSelectedGameObject(gameObject); } } /// /// Sets the data. /// /// Component. /// The index. protected virtual void SetData(TComponent component, int i) { component.Index = currentIndicies[i]; SetData(component, ListView.DataSource[currentIndicies[i]]); } /// /// Sets component data with specified item. /// /// Component. /// Item. protected virtual void SetData(TComponent component, TItem item) { } /// /// Hide list view. /// void OnListViewCancel() { HideList(); } /// /// Adds the component. /// /// Index. protected virtual void AddComponent(int index) { TComponent component; if (componentsCache.Count > 0) { component = componentsCache[componentsCache.Count - 1]; componentsCache.RemoveAt(componentsCache.Count - 1); } else { component = Instantiate(current) as TComponent; component.transform.SetParent(current.transform.parent, false); Utilites.FixInstantiated(current, component); } component.Index = -1; component.transform.SetAsLastSibling(); component.gameObject.SetActive(true); components.Add(component); } /// /// Deactivates the component. /// /// Component. protected virtual void DeactivateComponent(TComponent component) { if (component!=null) { component.MovedToCache(); component.Index = -1; component.gameObject.SetActive(false); } } /// /// Updates the components count. /// protected void UpdateComponentsCount() { components.RemoveAll(IsNullComponent); if (components.Count==currentIndicies.Count) { return ; } if (components.Count < currentIndicies.Count) { componentsCache.RemoveAll(IsNullComponent); Enumerable.Range(0, currentIndicies.Count - components.Count).ForEach(AddComponent); } else { var to_cache = components.GetRange(currentIndicies.Count, components.Count - currentIndicies.Count).OrderByDescending(GetComponentIndex); to_cache.ForEach(DeactivateComponent); componentsCache.AddRange(to_cache); components.RemoveRange(currentIndicies.Count, components.Count - currentIndicies.Count); } } /// /// Determines whether the specified component is null. /// /// true if the specified component is null; otherwise, false. /// Component. protected bool IsNullComponent(TComponent component) { return component==null; } /// /// Gets the index of the component. /// /// The component index. /// Item. protected int GetComponentIndex(TComponent item) { return item.Index; } /// /// Raises the submit event. /// /// Event data. public virtual void OnSubmit(BaseEventData eventData) { ShowList(); } /// /// Updates the current component. /// [Obsolete("Use SetData() instead.")] protected virtual void UpdateCurrent() { HideList(); } /// /// This function is called when the MonoBehaviour will be destroyed. /// protected virtual void OnDestroy() { ListView = null; ToggleButton = null; } } }