500 lines
10 KiB
C#
500 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.Events;
|
|
|
|
namespace UIWidgets
|
|
{
|
|
public abstract class ListViewBase : MonoBehaviour, ISelectHandler, IDeselectHandler, ISubmitHandler, ICancelHandler, IEventSystemHandler
|
|
{
|
|
[SerializeField]
|
|
[HideInInspector]
|
|
private List<ListViewItem> items = new List<ListViewItem>();
|
|
|
|
private List<UnityAction> callbacks = new List<UnityAction>();
|
|
|
|
[SerializeField]
|
|
[HideInInspector]
|
|
public bool DestroyGameObjects = true;
|
|
|
|
[SerializeField]
|
|
public bool Multiple;
|
|
|
|
[SerializeField]
|
|
private int selectedIndex = -1;
|
|
|
|
[SerializeField]
|
|
private List<int> selectedIndicies = new List<int>();
|
|
|
|
public ListViewBaseEvent OnSelect = new ListViewBaseEvent();
|
|
|
|
public ListViewBaseEvent OnDeselect = new ListViewBaseEvent();
|
|
|
|
public UnityEvent onSubmit = new UnityEvent();
|
|
|
|
public UnityEvent onCancel = new UnityEvent();
|
|
|
|
public UnityEvent onItemSelect = new UnityEvent();
|
|
|
|
public UnityEvent onItemCancel = new UnityEvent();
|
|
|
|
[SerializeField]
|
|
public Transform Container;
|
|
|
|
public ListViewFocusEvent OnFocusIn = new ListViewFocusEvent();
|
|
|
|
public ListViewFocusEvent OnFocusOut = new ListViewFocusEvent();
|
|
|
|
[NonSerialized]
|
|
protected bool SetItemIndicies = true;
|
|
|
|
private GameObject Unused;
|
|
|
|
[NonSerialized]
|
|
private bool isStartedListViewBase;
|
|
|
|
public List<ListViewItem> Items
|
|
{
|
|
get
|
|
{
|
|
return new List<ListViewItem>(items);
|
|
}
|
|
set
|
|
{
|
|
UpdateItems(value);
|
|
}
|
|
}
|
|
|
|
public int SelectedIndex
|
|
{
|
|
get
|
|
{
|
|
return selectedIndex;
|
|
}
|
|
set
|
|
{
|
|
if (value == -1)
|
|
{
|
|
if (selectedIndex != -1)
|
|
{
|
|
Deselect(selectedIndex);
|
|
}
|
|
selectedIndex = value;
|
|
}
|
|
else
|
|
{
|
|
Select(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
public List<int> SelectedIndicies
|
|
{
|
|
get
|
|
{
|
|
return new List<int>(selectedIndicies);
|
|
}
|
|
set
|
|
{
|
|
IEnumerable<int> enumerable = selectedIndicies.Except(value);
|
|
IEnumerable<int> enumerable2 = value.Except(selectedIndicies);
|
|
enumerable.ForEach(Deselect);
|
|
enumerable2.ForEach(Select);
|
|
}
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
Start();
|
|
}
|
|
|
|
public virtual void Start()
|
|
{
|
|
if (!isStartedListViewBase)
|
|
{
|
|
isStartedListViewBase = true;
|
|
Unused = new GameObject("unused base");
|
|
Unused.SetActive(false);
|
|
Unused.transform.SetParent(base.transform, false);
|
|
if (selectedIndex != -1 && selectedIndicies.Count == 0)
|
|
{
|
|
selectedIndicies.Add(selectedIndex);
|
|
}
|
|
selectedIndicies.RemoveAll(NotIsValid);
|
|
if (selectedIndicies.Count == 0)
|
|
{
|
|
selectedIndex = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected bool NotIsValid(int index)
|
|
{
|
|
return !IsValid(index);
|
|
}
|
|
|
|
public virtual void UpdateItems()
|
|
{
|
|
UpdateItems(items);
|
|
}
|
|
|
|
private void RemoveCallback(ListViewItem item, int index)
|
|
{
|
|
if (!(item == null))
|
|
{
|
|
if (index < callbacks.Count)
|
|
{
|
|
item.onClick.RemoveListener(callbacks[index]);
|
|
}
|
|
item.onSubmit.RemoveListener(Toggle);
|
|
item.onCancel.RemoveListener(OnItemCancel);
|
|
item.onSelect.RemoveListener(HighlightColoring);
|
|
item.onDeselect.RemoveListener(Coloring);
|
|
item.onMove.RemoveListener(OnItemMove);
|
|
}
|
|
}
|
|
|
|
private void OnItemCancel(ListViewItem item)
|
|
{
|
|
if (!EventSystem.current.alreadySelecting)
|
|
{
|
|
EventSystem.current.SetSelectedGameObject(base.gameObject);
|
|
onItemCancel.Invoke();
|
|
}
|
|
}
|
|
|
|
private void RemoveCallbacks()
|
|
{
|
|
if (callbacks.Count > 0)
|
|
{
|
|
items.ForEach(RemoveCallback);
|
|
}
|
|
callbacks.Clear();
|
|
}
|
|
|
|
private void AddCallbacks()
|
|
{
|
|
items.ForEach(AddCallback);
|
|
}
|
|
|
|
private void AddCallback(ListViewItem item, int index)
|
|
{
|
|
callbacks.Insert(index, delegate
|
|
{
|
|
Toggle(item);
|
|
});
|
|
item.onClick.AddListener(callbacks[index]);
|
|
item.onSubmit.AddListener(OnItemSubmit);
|
|
item.onCancel.AddListener(OnItemCancel);
|
|
item.onSelect.AddListener(HighlightColoring);
|
|
item.onDeselect.AddListener(Coloring);
|
|
item.onMove.AddListener(OnItemMove);
|
|
}
|
|
|
|
private void OnItemSelect(ListViewItem item)
|
|
{
|
|
onItemSelect.Invoke();
|
|
}
|
|
|
|
private void OnItemSubmit(ListViewItem item)
|
|
{
|
|
Toggle(item);
|
|
if (!IsSelected(item.Index))
|
|
{
|
|
HighlightColoring(item);
|
|
}
|
|
}
|
|
|
|
protected virtual void OnItemMove(AxisEventData eventData, ListViewItem item)
|
|
{
|
|
switch (eventData.moveDir)
|
|
{
|
|
case MoveDirection.Left:
|
|
break;
|
|
case MoveDirection.Right:
|
|
break;
|
|
case MoveDirection.Up:
|
|
if (item.Index > 0)
|
|
{
|
|
SelectComponentByIndex(item.Index - 1);
|
|
}
|
|
break;
|
|
case MoveDirection.Down:
|
|
if (IsValid(item.Index + 1))
|
|
{
|
|
SelectComponentByIndex(item.Index + 1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
protected virtual void ScrollTo(int index)
|
|
{
|
|
}
|
|
|
|
public virtual int Add(ListViewItem item)
|
|
{
|
|
item.transform.SetParent(Container, false);
|
|
AddCallback(item, items.Count);
|
|
items.Add(item);
|
|
item.Index = callbacks.Count - 1;
|
|
return callbacks.Count - 1;
|
|
}
|
|
|
|
public virtual void Clear()
|
|
{
|
|
items.Clear();
|
|
UpdateItems();
|
|
}
|
|
|
|
protected virtual int Remove(ListViewItem item)
|
|
{
|
|
RemoveCallbacks();
|
|
int index = item.Index;
|
|
selectedIndicies = (from x in selectedIndicies
|
|
where x != index
|
|
select (x <= index) ? x : x--).ToList();
|
|
if (selectedIndex == index)
|
|
{
|
|
Deselect(index);
|
|
selectedIndex = ((selectedIndicies.Count <= 0) ? (-1) : selectedIndicies.Last());
|
|
}
|
|
else if (selectedIndex > index)
|
|
{
|
|
selectedIndex--;
|
|
}
|
|
items.Remove(item);
|
|
Free(item);
|
|
AddCallbacks();
|
|
return index;
|
|
}
|
|
|
|
private void Free(Component item)
|
|
{
|
|
if (item == null)
|
|
{
|
|
return;
|
|
}
|
|
if (DestroyGameObjects)
|
|
{
|
|
if (!(item.gameObject == null))
|
|
{
|
|
UnityEngine.Object.Destroy(item.gameObject);
|
|
}
|
|
}
|
|
else if (!(item.transform == null) && !(Unused == null) && !(Unused.transform == null))
|
|
{
|
|
item.transform.SetParent(Unused.transform, false);
|
|
}
|
|
}
|
|
|
|
private void UpdateItems(List<ListViewItem> newItems)
|
|
{
|
|
RemoveCallbacks();
|
|
items.Where((ListViewItem item) => item != null && !newItems.Contains(item)).ForEach(Free);
|
|
newItems.ForEach(UpdateItem);
|
|
items = newItems;
|
|
AddCallbacks();
|
|
}
|
|
|
|
private void UpdateItem(ListViewItem item, int index)
|
|
{
|
|
if (!(item == null))
|
|
{
|
|
if (SetItemIndicies)
|
|
{
|
|
item.Index = index;
|
|
}
|
|
item.transform.SetParent(Container, false);
|
|
}
|
|
}
|
|
|
|
public virtual bool IsValid(int index)
|
|
{
|
|
return index >= 0 && index < items.Count;
|
|
}
|
|
|
|
protected ListViewItem GetItem(int index)
|
|
{
|
|
return items.Find((ListViewItem x) => x.Index == index);
|
|
}
|
|
|
|
public virtual void Select(int index)
|
|
{
|
|
if (index == -1)
|
|
{
|
|
return;
|
|
}
|
|
if (!IsValid(index))
|
|
{
|
|
string message = string.Format("Index must be between 0 and Items.Count ({0}). Gameobject {1}.", items.Count - 1, base.name);
|
|
throw new IndexOutOfRangeException(message);
|
|
}
|
|
if (IsSelected(index) && Multiple)
|
|
{
|
|
return;
|
|
}
|
|
if (!Multiple)
|
|
{
|
|
if (selectedIndex != -1 && selectedIndex != index)
|
|
{
|
|
Deselect(selectedIndex);
|
|
}
|
|
selectedIndicies.Clear();
|
|
}
|
|
selectedIndicies.Add(index);
|
|
selectedIndex = index;
|
|
SelectItem(index);
|
|
OnSelect.Invoke(index, GetItem(index));
|
|
}
|
|
|
|
protected void SilentDeselect(List<int> indicies)
|
|
{
|
|
selectedIndicies = selectedIndicies.Except(indicies).ToList();
|
|
selectedIndex = ((selectedIndicies.Count <= 0) ? (-1) : selectedIndicies[selectedIndicies.Count - 1]);
|
|
}
|
|
|
|
protected void SilentSelect(List<int> indicies)
|
|
{
|
|
if (indicies == null)
|
|
{
|
|
indicies = new List<int>();
|
|
}
|
|
selectedIndicies.AddRange(indicies.Except(selectedIndicies));
|
|
selectedIndex = ((selectedIndicies.Count <= 0) ? (-1) : selectedIndicies[selectedIndicies.Count - 1]);
|
|
}
|
|
|
|
public void Deselect(int index)
|
|
{
|
|
if (index != -1 && IsSelected(index))
|
|
{
|
|
selectedIndicies.Remove(index);
|
|
selectedIndex = ((selectedIndicies.Count <= 0) ? (-1) : selectedIndicies.Last());
|
|
if (IsValid(index))
|
|
{
|
|
DeselectItem(index);
|
|
OnDeselect.Invoke(index, GetItem(index));
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool IsSelected(int index)
|
|
{
|
|
return selectedIndicies.Contains(index);
|
|
}
|
|
|
|
public void Toggle(int index)
|
|
{
|
|
if (IsSelected(index) && Multiple)
|
|
{
|
|
Deselect(index);
|
|
}
|
|
else
|
|
{
|
|
Select(index);
|
|
}
|
|
}
|
|
|
|
private void Toggle(ListViewItem item)
|
|
{
|
|
bool flag = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
|
|
bool flag2 = selectedIndicies.Count > 0;
|
|
if (Multiple && flag && flag2 && selectedIndicies[0] != item.Index)
|
|
{
|
|
selectedIndicies.GetRange(1, selectedIndicies.Count - 1).ForEach(Deselect);
|
|
int num = Mathf.Min(selectedIndicies[0], item.Index);
|
|
int num2 = Mathf.Max(selectedIndicies[0], item.Index);
|
|
Enumerable.Range(num, num2 - num + 1).ForEach(Select);
|
|
}
|
|
else
|
|
{
|
|
Toggle(item.Index);
|
|
}
|
|
}
|
|
|
|
protected int GetComponentIndex(ListViewItem item)
|
|
{
|
|
return item.Index;
|
|
}
|
|
|
|
protected void SetComponentAsLastSibling(ListViewItem item)
|
|
{
|
|
item.transform.SetAsLastSibling();
|
|
}
|
|
|
|
protected virtual void SelectItem(int index)
|
|
{
|
|
}
|
|
|
|
protected virtual void DeselectItem(int index)
|
|
{
|
|
}
|
|
|
|
protected virtual void Coloring(ListViewItem component)
|
|
{
|
|
}
|
|
|
|
protected virtual void HighlightColoring(ListViewItem component)
|
|
{
|
|
}
|
|
|
|
protected virtual void OnDestroy()
|
|
{
|
|
RemoveCallbacks();
|
|
items.ForEach(Free);
|
|
}
|
|
|
|
public virtual bool SelectComponent()
|
|
{
|
|
if (items.Count == 0)
|
|
{
|
|
return false;
|
|
}
|
|
int index = ((SelectedIndex != -1) ? SelectedIndex : 0);
|
|
SelectComponentByIndex(index);
|
|
return true;
|
|
}
|
|
|
|
protected void SelectComponentByIndex(int index)
|
|
{
|
|
ScrollTo(index);
|
|
ListViewItemEventData listViewItemEventData = new ListViewItemEventData(EventSystem.current);
|
|
listViewItemEventData.NewSelectedObject = GetItem(index).gameObject;
|
|
ListViewItemEventData listViewItemEventData2 = listViewItemEventData;
|
|
ExecuteEvents.Execute(listViewItemEventData2.NewSelectedObject, listViewItemEventData2, ExecuteEvents.selectHandler);
|
|
}
|
|
|
|
void ISelectHandler.OnSelect(BaseEventData eventData)
|
|
{
|
|
if (!EventSystem.current.alreadySelecting)
|
|
{
|
|
EventSystem.current.SetSelectedGameObject(base.gameObject);
|
|
}
|
|
OnFocusIn.Invoke(eventData);
|
|
}
|
|
|
|
void IDeselectHandler.OnDeselect(BaseEventData eventData)
|
|
{
|
|
OnFocusOut.Invoke(eventData);
|
|
}
|
|
|
|
void ISubmitHandler.OnSubmit(BaseEventData eventData)
|
|
{
|
|
SelectComponent();
|
|
onSubmit.Invoke();
|
|
}
|
|
|
|
void ICancelHandler.OnCancel(BaseEventData eventData)
|
|
{
|
|
onCancel.Invoke();
|
|
}
|
|
|
|
public virtual void ForEachComponent(Action<ListViewItem> func)
|
|
{
|
|
items.ForEach(func);
|
|
}
|
|
}
|
|
}
|