天津23维预案
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

595 lines
13 KiB

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