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.
1661 lines
38 KiB
1661 lines
38 KiB
2 years ago
|
using UnityEngine;
|
||
|
using UnityEngine.UI;
|
||
|
using UnityEngine.Events;
|
||
|
using UnityEngine.EventSystems;
|
||
|
using UnityEngine.Serialization;
|
||
|
using System.Collections.Generic;
|
||
|
using System;
|
||
|
using System.Linq;
|
||
|
|
||
|
namespace UIWidgets
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// ListView sources.
|
||
|
/// List - Use strings as source for list.
|
||
|
/// File - Get strings from file, one line per string.
|
||
|
/// </summary>
|
||
|
public enum ListViewSources {
|
||
|
List = 0,
|
||
|
File = 1,
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// List view event.
|
||
|
/// </summary>
|
||
|
[Serializable]
|
||
|
public class ListViewEvent : UnityEvent<int,string> {
|
||
|
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// List view.
|
||
|
/// http://ilih.ru/images/unity-assets/UIWidgets/ListView.png
|
||
|
/// </summary>
|
||
|
[AddComponentMenu("UI/UIWidgets/ListView")]
|
||
|
public class ListView : ListViewBase {
|
||
|
[SerializeField]
|
||
|
[Obsolete("Use DataSource instead.")]
|
||
|
List<string> strings = new List<string>();
|
||
|
|
||
|
//[SerializeField]
|
||
|
//[HideInInspector]
|
||
|
protected ObservableList<string> dataSource;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets or sets the data source.
|
||
|
/// </summary>
|
||
|
/// <value>The data source.</value>
|
||
|
public virtual ObservableList<string> DataSource {
|
||
|
get {
|
||
|
if (dataSource==null)
|
||
|
{
|
||
|
#pragma warning disable 0618
|
||
|
dataSource = new ObservableList<string>(strings);
|
||
|
dataSource.OnChange += UpdateItems;
|
||
|
strings = null;
|
||
|
#pragma warning restore 0618
|
||
|
}
|
||
|
return dataSource;
|
||
|
}
|
||
|
set {
|
||
|
SetNewItems(value);
|
||
|
SetScrollValue(0f);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the strings.
|
||
|
/// </summary>
|
||
|
/// <value>The strings.</value>
|
||
|
[Obsolete("Use DataSource instead.")]
|
||
|
public List<string> Strings {
|
||
|
get {
|
||
|
return new List<string>(DataSource);
|
||
|
}
|
||
|
set {
|
||
|
SetNewItems(new ObservableList<string>(value));
|
||
|
SetScrollValue(0f);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the strings.
|
||
|
/// </summary>
|
||
|
/// <value>The strings.</value>
|
||
|
[Obsolete("Use DataSource instead.")]
|
||
|
public new List<string> Items {
|
||
|
get {
|
||
|
return new List<string>(DataSource);
|
||
|
}
|
||
|
set {
|
||
|
SetNewItems(new ObservableList<string>(value));
|
||
|
SetScrollValue(0f);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[SerializeField]
|
||
|
TextAsset file;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets or sets the file with strings for ListView. One string per line.
|
||
|
/// </summary>
|
||
|
/// <value>The file.</value>
|
||
|
public TextAsset File {
|
||
|
get {
|
||
|
return file;
|
||
|
}
|
||
|
set {
|
||
|
file = value;
|
||
|
if (file!=null)
|
||
|
{
|
||
|
GetItemsFromFile(file);
|
||
|
SetScrollValue(0f);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// The comments in file start with specified strings.
|
||
|
/// </summary>
|
||
|
[SerializeField]
|
||
|
public List<string> CommentsStartWith = new List<string>(){"#", "//"};
|
||
|
|
||
|
/// <summary>
|
||
|
/// The source.
|
||
|
/// </summary>
|
||
|
[SerializeField]
|
||
|
public ListViewSources Source = ListViewSources.List;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Allow only unique strings.
|
||
|
/// </summary>
|
||
|
[SerializeField]
|
||
|
public bool Unique = true;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Allow empty strings.
|
||
|
/// </summary>
|
||
|
[SerializeField]
|
||
|
public bool AllowEmptyItems;
|
||
|
|
||
|
[SerializeField]
|
||
|
Color backgroundColor = Color.white;
|
||
|
|
||
|
[SerializeField]
|
||
|
Color textColor = Color.black;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Default background color.
|
||
|
/// </summary>
|
||
|
public Color BackgroundColor {
|
||
|
get {
|
||
|
return backgroundColor;
|
||
|
}
|
||
|
set {
|
||
|
backgroundColor = value;
|
||
|
UpdateColors();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Default text color.
|
||
|
/// </summary>
|
||
|
public Color TextColor {
|
||
|
get {
|
||
|
return textColor;
|
||
|
}
|
||
|
set {
|
||
|
textColor = value;
|
||
|
UpdateColors();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Color of background on pointer over.
|
||
|
/// </summary>
|
||
|
[SerializeField]
|
||
|
public Color HighlightedBackgroundColor = new Color(203, 230, 244, 255);
|
||
|
|
||
|
/// <summary>
|
||
|
/// Color of text on pointer text.
|
||
|
/// </summary>
|
||
|
[SerializeField]
|
||
|
public Color HighlightedTextColor = Color.black;
|
||
|
|
||
|
[SerializeField]
|
||
|
Color selectedBackgroundColor = new Color(53, 83, 227, 255);
|
||
|
|
||
|
[SerializeField]
|
||
|
Color selectedTextColor = Color.black;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Background color of selected item.
|
||
|
/// </summary>
|
||
|
public Color SelectedBackgroundColor {
|
||
|
get {
|
||
|
return selectedBackgroundColor;
|
||
|
}
|
||
|
set {
|
||
|
selectedBackgroundColor = value;
|
||
|
UpdateColors();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Text color of selected item.
|
||
|
/// </summary>
|
||
|
public Color SelectedTextColor {
|
||
|
get {
|
||
|
return selectedTextColor;
|
||
|
}
|
||
|
set {
|
||
|
selectedTextColor = value;
|
||
|
UpdateColors();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// The default item.
|
||
|
/// </summary>
|
||
|
[SerializeField]
|
||
|
public ImageAdvanced DefaultItem;
|
||
|
|
||
|
/// <summary>
|
||
|
/// The components.
|
||
|
/// </summary>
|
||
|
protected List<ListViewStringComponent> components = new List<ListViewStringComponent>();
|
||
|
|
||
|
/// <summary>
|
||
|
/// The callbacks enter.
|
||
|
/// </summary>
|
||
|
protected List<UnityAction<PointerEventData>> callbacksEnter = new List<UnityAction<PointerEventData>>();
|
||
|
|
||
|
/// <summary>
|
||
|
/// The callbacks exit.
|
||
|
/// </summary>
|
||
|
protected List<UnityAction<PointerEventData>> callbacksExit = new List<UnityAction<PointerEventData>>();
|
||
|
|
||
|
/// <summary>
|
||
|
/// The sort.
|
||
|
/// </summary>
|
||
|
[SerializeField]
|
||
|
[FormerlySerializedAs("Sort")]
|
||
|
protected bool sort = true;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Sort items.
|
||
|
/// Advice to use DataSource.Comparison instead Sort and SortFunc.
|
||
|
/// </summary>
|
||
|
public bool Sort {
|
||
|
get {
|
||
|
return sort;
|
||
|
}
|
||
|
set {
|
||
|
sort = value;
|
||
|
if (Sort && isStartedListView && sortFunc!=null)
|
||
|
{
|
||
|
UpdateItems();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// The sort function.
|
||
|
/// </summary>
|
||
|
protected Func<IEnumerable<string>,IEnumerable<string>> sortFunc = items => items.OrderBy(x => x);
|
||
|
|
||
|
/// <summary>
|
||
|
/// Sort function.
|
||
|
/// Advice to use DataSource.Comparison instead Sort and SortFunc.
|
||
|
/// </summary>
|
||
|
public Func<IEnumerable<string>, IEnumerable<string>> SortFunc {
|
||
|
get {
|
||
|
return sortFunc;
|
||
|
}
|
||
|
set {
|
||
|
sortFunc = value;
|
||
|
if (Sort && isStartedListView && sortFunc!=null)
|
||
|
{
|
||
|
UpdateItems();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// OnSelect event.
|
||
|
/// </summary>
|
||
|
public ListViewEvent OnSelectString = new ListViewEvent();
|
||
|
|
||
|
/// <summary>
|
||
|
/// OnDeselect event.
|
||
|
/// </summary>
|
||
|
public ListViewEvent OnDeselectString = new ListViewEvent();
|
||
|
|
||
|
[SerializeField]
|
||
|
ScrollRect scrollRect;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets or sets the ScrollRect.
|
||
|
/// </summary>
|
||
|
/// <value>The ScrollRect.</value>
|
||
|
public ScrollRect ScrollRect {
|
||
|
get {
|
||
|
return scrollRect;
|
||
|
}
|
||
|
set {
|
||
|
if (scrollRect!=null)
|
||
|
{
|
||
|
var r = scrollRect.GetComponent<ResizeListener>();
|
||
|
if (r!=null)
|
||
|
{
|
||
|
r.OnResize.RemoveListener(SetNeedResize);
|
||
|
}
|
||
|
|
||
|
scrollRect.onValueChanged.RemoveListener(OnScrollUpdate);
|
||
|
}
|
||
|
scrollRect = value;
|
||
|
if (scrollRect!=null)
|
||
|
{
|
||
|
var resizeListener = scrollRect.GetComponent<ResizeListener>();
|
||
|
if (resizeListener==null)
|
||
|
{
|
||
|
resizeListener = scrollRect.gameObject.AddComponent<ResizeListener>();
|
||
|
}
|
||
|
resizeListener.OnResize.AddListener(SetNeedResize);
|
||
|
|
||
|
scrollRect.onValueChanged.AddListener(OnScrollUpdate);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// The height of the DefaultItem.
|
||
|
/// </summary>
|
||
|
protected float itemHeight;
|
||
|
|
||
|
/// <summary>
|
||
|
/// The width of the DefaultItem.
|
||
|
/// </summary>
|
||
|
protected float itemWidth;
|
||
|
|
||
|
/// <summary>
|
||
|
/// The height of the ScrollRect.
|
||
|
/// </summary>
|
||
|
protected float scrollHeight;
|
||
|
|
||
|
/// <summary>
|
||
|
/// The width of the ScrollRect.
|
||
|
/// </summary>
|
||
|
protected float scrollWidth;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Count of visible items.
|
||
|
/// </summary>
|
||
|
protected int maxVisibleItems;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Count of visible items.
|
||
|
/// </summary>
|
||
|
protected int visibleItems;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Count of hidden items by top filler.
|
||
|
/// </summary>
|
||
|
protected int topHiddenItems;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Count of hidden items by bottom filler.
|
||
|
/// </summary>
|
||
|
protected int bottomHiddenItems;
|
||
|
|
||
|
/// <summary>
|
||
|
/// The direction.
|
||
|
/// </summary>
|
||
|
[SerializeField]
|
||
|
protected ListViewDirection direction = ListViewDirection.Vertical;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets or sets the direction.
|
||
|
/// </summary>
|
||
|
/// <value>The direction.</value>
|
||
|
public ListViewDirection Direction {
|
||
|
get {
|
||
|
return direction;
|
||
|
}
|
||
|
set {
|
||
|
direction = value;
|
||
|
|
||
|
(Container as RectTransform).anchoredPosition = Vector2.zero;
|
||
|
if (scrollRect)
|
||
|
{
|
||
|
scrollRect.horizontal = IsHorizontal();
|
||
|
scrollRect.vertical = !IsHorizontal();
|
||
|
}
|
||
|
if (CanOptimize() && (layout is EasyLayout.EasyLayout))
|
||
|
{
|
||
|
LayoutBridge.IsHorizontal = IsHorizontal();
|
||
|
|
||
|
CalculateMaxVisibleItems();
|
||
|
}
|
||
|
UpdateView();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Awake this instance.
|
||
|
/// </summary>
|
||
|
protected virtual void Awake()
|
||
|
{
|
||
|
Start();
|
||
|
}
|
||
|
|
||
|
[System.NonSerialized]
|
||
|
bool isStartedListView = false;
|
||
|
|
||
|
LayoutGroup layout;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the layout.
|
||
|
/// </summary>
|
||
|
/// <value>The layout.</value>
|
||
|
public EasyLayout.EasyLayout Layout {
|
||
|
get {
|
||
|
return layout as EasyLayout.EasyLayout;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// LayoutBridge.
|
||
|
/// </summary>
|
||
|
protected ILayoutBridge LayoutBridge;
|
||
|
|
||
|
List<string> SelectedItemsCache;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Start this instance.
|
||
|
/// </summary>
|
||
|
public override void Start()
|
||
|
{
|
||
|
if (isStartedListView)
|
||
|
{
|
||
|
return ;
|
||
|
}
|
||
|
isStartedListView = true;
|
||
|
|
||
|
base.Start();
|
||
|
base.Items = new List<ListViewItem>();
|
||
|
|
||
|
SelectedItemsCache = SelectedIndicies.Convert<int,string>(GetDataItem);
|
||
|
|
||
|
DestroyGameObjects = false;
|
||
|
|
||
|
if (DefaultItem==null)
|
||
|
{
|
||
|
throw new NullReferenceException("DefaultItem is null. Set component of type ImageAdvanced to DefaultItem.");
|
||
|
}
|
||
|
DefaultItem.gameObject.SetActive(true);
|
||
|
if (DefaultItem.GetComponentInChildren<Text>()==null)
|
||
|
{
|
||
|
throw new MissingComponentException("DefaultItem don't have child with 'Text' component. Add child with 'Text' component to DefaultItem.");
|
||
|
}
|
||
|
|
||
|
if (CanOptimize())
|
||
|
{
|
||
|
ScrollRect = scrollRect;
|
||
|
|
||
|
var scrollRectTransform = scrollRect.transform as RectTransform;
|
||
|
scrollHeight = scrollRectTransform.rect.height;
|
||
|
scrollWidth = scrollRectTransform.rect.width;
|
||
|
|
||
|
layout = Container.GetComponent<LayoutGroup>();
|
||
|
if (layout is EasyLayout.EasyLayout)
|
||
|
{
|
||
|
LayoutBridge = new EasyLayoutBridge(layout as EasyLayout.EasyLayout, DefaultItem.transform as RectTransform);
|
||
|
LayoutBridge.IsHorizontal = IsHorizontal();
|
||
|
}
|
||
|
else if (layout is HorizontalOrVerticalLayoutGroup)
|
||
|
{
|
||
|
LayoutBridge = new StandardLayoutBridge(layout as HorizontalOrVerticalLayoutGroup, DefaultItem.transform as RectTransform);
|
||
|
}
|
||
|
|
||
|
CalculateItemSize();
|
||
|
CalculateMaxVisibleItems();
|
||
|
}
|
||
|
|
||
|
DefaultItem.gameObject.SetActive(false);
|
||
|
|
||
|
UpdateItems();
|
||
|
|
||
|
OnSelect.AddListener(OnSelectCallback);
|
||
|
OnDeselect.AddListener(OnDeselectCallback);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Calculates the size of the item.
|
||
|
/// </summary>
|
||
|
protected virtual void CalculateItemSize()
|
||
|
{
|
||
|
if (LayoutBridge==null)
|
||
|
{
|
||
|
return ;
|
||
|
}
|
||
|
var size = LayoutBridge.GetItemSize();
|
||
|
itemHeight = size.y;
|
||
|
itemWidth = size.x;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the item.
|
||
|
/// </summary>
|
||
|
/// <returns>The item.</returns>
|
||
|
/// <param name="index">Index.</param>
|
||
|
protected string GetDataItem(int index)
|
||
|
{
|
||
|
return DataSource[index];
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Determines whether this instance is horizontal.
|
||
|
/// </summary>
|
||
|
/// <returns><c>true</c> if this instance is horizontal; otherwise, <c>false</c>.</returns>
|
||
|
public override bool IsHorizontal()
|
||
|
{
|
||
|
return direction==ListViewDirection.Horizontal;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the default height of the item.
|
||
|
/// </summary>
|
||
|
/// <returns>The default item height.</returns>
|
||
|
public override float GetDefaultItemHeight()
|
||
|
{
|
||
|
return itemHeight;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the default width of the item.
|
||
|
/// </summary>
|
||
|
/// <returns>The default item width.</returns>
|
||
|
public override float GetDefaultItemWidth()
|
||
|
{
|
||
|
return itemWidth;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the spacing between items. Not implemented for ListViewBase.
|
||
|
/// </summary>
|
||
|
/// <returns>The item spacing.</returns>
|
||
|
public override float GetItemSpacing()
|
||
|
{
|
||
|
return LayoutBridge.GetSpacing();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Calculates the max count of visible items.
|
||
|
/// </summary>
|
||
|
protected virtual void CalculateMaxVisibleItems()
|
||
|
{
|
||
|
if (IsHorizontal())
|
||
|
{
|
||
|
maxVisibleItems = Mathf.CeilToInt(scrollWidth / itemWidth) + 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
maxVisibleItems = Mathf.CeilToInt(scrollHeight / itemHeight) + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Handle instance resize.
|
||
|
/// </summary>
|
||
|
public virtual void Resize()
|
||
|
{
|
||
|
needResize = false;
|
||
|
|
||
|
var scrollRectTransform = scrollRect.transform as RectTransform;
|
||
|
scrollHeight = scrollRectTransform.rect.height;
|
||
|
scrollWidth = scrollRectTransform.rect.width;
|
||
|
|
||
|
CalculateMaxVisibleItems();
|
||
|
UpdateView();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Determines whether this instance can be optimized.
|
||
|
/// </summary>
|
||
|
/// <returns><c>true</c> if this instance can be optimized; otherwise, <c>false</c>.</returns>
|
||
|
protected bool CanOptimize()
|
||
|
{
|
||
|
return scrollRect!=null && (layout!=null || Container.GetComponent<EasyLayout.EasyLayout>()!=null);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Raises the select callback event.
|
||
|
/// </summary>
|
||
|
/// <param name="index">Index.</param>
|
||
|
/// <param name="item">Item.</param>
|
||
|
void OnSelectCallback(int index, ListViewItem item)
|
||
|
{
|
||
|
if (SelectedItemsCache!=null)
|
||
|
{
|
||
|
SelectedItemsCache.Add(DataSource[index]);
|
||
|
}
|
||
|
|
||
|
OnSelectString.Invoke(index, DataSource[index]);
|
||
|
|
||
|
if (item!=null)
|
||
|
{
|
||
|
SelectColoring(item as ListViewStringComponent);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Raises the deselect callback event.
|
||
|
/// </summary>
|
||
|
/// <param name="index">Index.</param>
|
||
|
/// <param name="item">Item.</param>
|
||
|
void OnDeselectCallback(int index, ListViewItem item)
|
||
|
{
|
||
|
if (SelectedItemsCache!=null)
|
||
|
{
|
||
|
SelectedItemsCache.Remove(DataSource[index]);
|
||
|
}
|
||
|
|
||
|
OnDeselectString.Invoke(index, DataSource[index]);
|
||
|
|
||
|
if (item!=null)
|
||
|
{
|
||
|
DefaultColoring(item as ListViewStringComponent);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Updates the items.
|
||
|
/// </summary>
|
||
|
public override void UpdateItems()
|
||
|
{
|
||
|
if (Source==ListViewSources.List)
|
||
|
{
|
||
|
SetNewItems(DataSource);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Source = ListViewSources.List;
|
||
|
|
||
|
GetItemsFromFile(File);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Clear strings list.
|
||
|
/// </summary>
|
||
|
public override void Clear()
|
||
|
{
|
||
|
DataSource.Clear();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the items from file.
|
||
|
/// </summary>
|
||
|
public void GetItemsFromFile()
|
||
|
{
|
||
|
GetItemsFromFile(File);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Trim end of string.
|
||
|
/// </summary>
|
||
|
/// <returns>The trimmed string.</returns>
|
||
|
/// <param name="str">String.</param>
|
||
|
string StringTrimEnd(string str)
|
||
|
{
|
||
|
return str.TrimEnd();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Determines whether specified string not empty.
|
||
|
/// </summary>
|
||
|
/// <returns><c>true</c> if specified string not empty; otherwise, <c>false</c>.</returns>
|
||
|
/// <param name="str">String.</param>
|
||
|
bool IsStringNotEmpty(string str)
|
||
|
{
|
||
|
return str!=string.Empty;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Determines whether specified string not comment.
|
||
|
/// </summary>
|
||
|
/// <returns><c>true</c>, if string not comment, <c>false</c> otherwise.</returns>
|
||
|
/// <param name="str">String.</param>
|
||
|
bool NotComment(string str)
|
||
|
{
|
||
|
return !CommentsStartWith.Any(comment => str.StartsWith(comment));
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the items from file.
|
||
|
/// </summary>
|
||
|
/// <param name="sourceFile">Source file.</param>
|
||
|
public void GetItemsFromFile(TextAsset sourceFile)
|
||
|
{
|
||
|
if (file==null)
|
||
|
{
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
var new_items = sourceFile.text.Split(new string[] {"\r\n", "\r", "\n"}, StringSplitOptions.None).Select<string,string>(StringTrimEnd);
|
||
|
if (Unique)
|
||
|
{
|
||
|
new_items = new_items.Distinct();
|
||
|
}
|
||
|
|
||
|
if (!AllowEmptyItems)
|
||
|
{
|
||
|
new_items = new_items.Where(IsStringNotEmpty);
|
||
|
}
|
||
|
|
||
|
if (CommentsStartWith.Count > 0)
|
||
|
{
|
||
|
new_items = new_items.Where(NotComment);
|
||
|
}
|
||
|
SetNewItems(new_items.ToObservableList());
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Finds the indicies of specified item.
|
||
|
/// </summary>
|
||
|
/// <returns>The indicies.</returns>
|
||
|
/// <param name="item">Item.</param>
|
||
|
public virtual List<int> FindIndicies(string item)
|
||
|
{
|
||
|
return Enumerable.Range(0, DataSource.Count)
|
||
|
.Where(i => DataSource[i]==item)
|
||
|
.ToList();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Finds the index of specified item.
|
||
|
/// </summary>
|
||
|
/// <returns>The index.</returns>
|
||
|
/// <param name="item">Item.</param>
|
||
|
public virtual int FindIndex(string item)
|
||
|
{
|
||
|
return DataSource.IndexOf(item);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Add the specified item.
|
||
|
/// </summary>
|
||
|
/// <param name="item">Item.</param>
|
||
|
/// <returns>Index of added item.</returns>
|
||
|
public virtual int Add(string item)
|
||
|
{
|
||
|
var old_indicies = (Sort && SortFunc!=null) ? FindIndicies(item) : null;
|
||
|
|
||
|
DataSource.Add(item);
|
||
|
|
||
|
if (Sort && SortFunc!=null)
|
||
|
{
|
||
|
var new_indicies = FindIndicies(item);
|
||
|
|
||
|
var diff = new_indicies.Except(old_indicies).ToArray();
|
||
|
if (diff.Length > 0)
|
||
|
{
|
||
|
return diff[0];
|
||
|
}
|
||
|
if (new_indicies.Count > 0)
|
||
|
{
|
||
|
return new_indicies[0];
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return DataSource.Count - 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Remove the specified item.
|
||
|
/// </summary>
|
||
|
/// <param name="item">Item.</param>
|
||
|
/// <returns>Index of removed item.</returns>
|
||
|
public virtual int Remove(string item)
|
||
|
{
|
||
|
var index = FindIndex(item);
|
||
|
if (index==-1)
|
||
|
{
|
||
|
return index;
|
||
|
}
|
||
|
|
||
|
DataSource.Remove(item);
|
||
|
|
||
|
return index;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Removes the callback.
|
||
|
/// </summary>
|
||
|
/// <param name="item">Item.</param>
|
||
|
/// <param name="index">Index.</param>
|
||
|
/// <param name="component">Component.</param>
|
||
|
void RemoveCallback(ListViewStringComponent component, int index)
|
||
|
{
|
||
|
if (component==null)
|
||
|
{
|
||
|
return ;
|
||
|
}
|
||
|
if (index < callbacksEnter.Count)
|
||
|
{
|
||
|
component.onPointerEnter.RemoveListener(callbacksEnter[index]);
|
||
|
}
|
||
|
if (index < callbacksExit.Count)
|
||
|
{
|
||
|
component.onPointerExit.RemoveListener(callbacksExit[index]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Removes the callbacks.
|
||
|
/// </summary>
|
||
|
void RemoveCallbacks()
|
||
|
{
|
||
|
components.ForEach(RemoveCallback);
|
||
|
callbacksEnter.Clear();
|
||
|
callbacksExit.Clear();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Adds the callbacks.
|
||
|
/// </summary>
|
||
|
void AddCallbacks()
|
||
|
{
|
||
|
components.ForEach(AddCallback);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Adds the callback.
|
||
|
/// </summary>
|
||
|
/// <param name="component">Component.</param>
|
||
|
/// <param name="index">Index.</param>
|
||
|
void AddCallback(ListViewStringComponent component, int index)
|
||
|
{
|
||
|
callbacksEnter.Add(ev => OnPointerEnterCallback(component));
|
||
|
callbacksExit.Add(ev => OnPointerExitCallback(component));
|
||
|
|
||
|
component.onPointerEnter.AddListener(callbacksEnter[index]);
|
||
|
component.onPointerExit.AddListener(callbacksExit[index]);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Determines if item exists with the specified index.
|
||
|
/// </summary>
|
||
|
/// <returns><c>true</c> if item exists with the specified index; otherwise, <c>false</c>.</returns>
|
||
|
/// <param name="index">Index.</param>
|
||
|
public override bool IsValid(int index)
|
||
|
{
|
||
|
return (index >= 0) && (index < DataSource.Count);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Raises the pointer enter callback event.
|
||
|
/// </summary>
|
||
|
/// <param name="component">Component.</param>
|
||
|
void OnPointerEnterCallback(ListViewStringComponent component)
|
||
|
{
|
||
|
if (!IsValid(component.Index))
|
||
|
{
|
||
|
var message = string.Format("Index must be between 0 and Items.Count ({0})", DataSource.Count);
|
||
|
throw new IndexOutOfRangeException(message);
|
||
|
}
|
||
|
|
||
|
if (IsSelected(component.Index))
|
||
|
{
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
HighlightColoring(component);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Raises the pointer exit callback event.
|
||
|
/// </summary>
|
||
|
/// <param name="component">Component.</param>
|
||
|
void OnPointerExitCallback(ListViewStringComponent component)
|
||
|
{
|
||
|
if (!IsValid(component.Index))
|
||
|
{
|
||
|
var message = string.Format("Index must be between 0 and Items.Count ({0})", DataSource.Count);
|
||
|
throw new IndexOutOfRangeException(message);
|
||
|
}
|
||
|
|
||
|
if (IsSelected(component.Index))
|
||
|
{
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
DefaultColoring(component);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Sets the scroll value.
|
||
|
/// </summary>
|
||
|
/// <param name="value">Value.</param>
|
||
|
protected void SetScrollValue(float value)
|
||
|
{
|
||
|
var current_position = scrollRect.content.anchoredPosition;
|
||
|
var new_position = new Vector2(current_position.x, value);
|
||
|
if (new_position != current_position)
|
||
|
{
|
||
|
scrollRect.content.anchoredPosition = new_position;
|
||
|
ScrollUpdate();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the scroll value.
|
||
|
/// </summary>
|
||
|
/// <returns>The scroll value.</returns>
|
||
|
protected float GetScrollValue()
|
||
|
{
|
||
|
var pos = scrollRect.content.anchoredPosition;
|
||
|
return Mathf.Max(0f, (IsHorizontal()) ? -pos.x : pos.y);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the size of the item.
|
||
|
/// </summary>
|
||
|
/// <returns>The item size.</returns>
|
||
|
protected float GetItemSize()
|
||
|
{
|
||
|
return (IsHorizontal())
|
||
|
? itemWidth + LayoutBridge.GetSpacing()
|
||
|
: itemHeight + LayoutBridge.GetSpacing();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the size of the scroll.
|
||
|
/// </summary>
|
||
|
/// <returns>The scroll size.</returns>
|
||
|
protected float GetScrollSize()
|
||
|
{
|
||
|
return (IsHorizontal()) ? scrollWidth : scrollHeight;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Scrolls to item with specifid index.
|
||
|
/// </summary>
|
||
|
/// <param name="index">Index.</param>
|
||
|
public override void ScrollTo(int index)
|
||
|
{
|
||
|
if (!CanOptimize())
|
||
|
{
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
var first_visible = GetFirstVisibleIndex(true);
|
||
|
var last_visible = GetLastVisibleIndex(true);
|
||
|
|
||
|
if (first_visible > index)
|
||
|
{
|
||
|
SetScrollValue(GetItemPosition(index));
|
||
|
}
|
||
|
else if (last_visible < index)
|
||
|
{
|
||
|
SetScrollValue(GetItemPositionBottom(index));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the item bottom position by index.
|
||
|
/// </summary>
|
||
|
/// <returns>The item bottom position.</returns>
|
||
|
/// <param name="index">Index.</param>
|
||
|
public virtual float GetItemPositionBottom(int index)
|
||
|
{
|
||
|
return GetItemPosition(index) + GetItemSize() - LayoutBridge.GetSpacing() + LayoutBridge.GetMargin() - GetScrollSize();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the last index of the visible.
|
||
|
/// </summary>
|
||
|
/// <returns>The last visible index.</returns>
|
||
|
/// <param name="strict">If set to <c>true</c> strict.</param>
|
||
|
protected virtual int GetLastVisibleIndex(bool strict=false)
|
||
|
{
|
||
|
var window = GetScrollValue() + GetScrollSize();
|
||
|
var last_visible_index = (strict)
|
||
|
? Mathf.FloorToInt(window / GetItemSize())
|
||
|
: Mathf.CeilToInt(window / GetItemSize());
|
||
|
|
||
|
return last_visible_index - 1;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the first index of the visible.
|
||
|
/// </summary>
|
||
|
/// <returns>The first visible index.</returns>
|
||
|
/// <param name="strict">If set to <c>true</c> strict.</param>
|
||
|
protected virtual int GetFirstVisibleIndex(bool strict=false)
|
||
|
{
|
||
|
var first_visible_index = (strict)
|
||
|
? Mathf.CeilToInt(GetScrollValue() / GetItemSize())
|
||
|
: Mathf.FloorToInt(GetScrollValue() / GetItemSize());
|
||
|
if (strict)
|
||
|
{
|
||
|
return first_visible_index;
|
||
|
}
|
||
|
|
||
|
return Mathf.Min(first_visible_index, Mathf.Max(0, DataSource.Count - visibleItems));
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Move first component from top to bottom.
|
||
|
/// </summary>
|
||
|
/// <returns>Component.</returns>
|
||
|
ListViewStringComponent ComponentTopToBottom()
|
||
|
{
|
||
|
var bottom = components.Count - 1;
|
||
|
|
||
|
var bottomComponent = components[bottom];
|
||
|
components.RemoveAt(bottom);
|
||
|
components.Insert(0, bottomComponent);
|
||
|
bottomComponent.transform.SetAsFirstSibling();
|
||
|
|
||
|
return bottomComponent;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Move last component from bottom to top.
|
||
|
/// </summary>
|
||
|
/// <returns>Component.</returns>
|
||
|
ListViewStringComponent ComponentBottomToTop()
|
||
|
{
|
||
|
var topComponent = components[0];
|
||
|
components.RemoveAt(0);
|
||
|
components.Add(topComponent);
|
||
|
topComponent.transform.SetAsLastSibling();
|
||
|
|
||
|
return topComponent;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Raises the scroll update event.
|
||
|
/// </summary>
|
||
|
/// <param name="position">Position.</param>
|
||
|
void OnScrollUpdate(Vector2 position)
|
||
|
{
|
||
|
ScrollUpdate();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Raises the scroll event.
|
||
|
/// </summary>
|
||
|
/// <param name="value">Value.</param>
|
||
|
void OnScroll(float value)
|
||
|
{
|
||
|
ScrollUpdate();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Update ListView according scroll position.
|
||
|
/// </summary>
|
||
|
void ScrollUpdate()
|
||
|
{
|
||
|
var oldTopHiddenItems = topHiddenItems;
|
||
|
|
||
|
topHiddenItems = GetFirstVisibleIndex();
|
||
|
bottomHiddenItems = Mathf.Max(0, DataSource.Count - visibleItems - topHiddenItems);
|
||
|
|
||
|
if (oldTopHiddenItems==topHiddenItems)
|
||
|
{
|
||
|
//do nothing
|
||
|
}
|
||
|
// optimization on +-1 item scroll
|
||
|
else if (oldTopHiddenItems==(topHiddenItems + 1))
|
||
|
{
|
||
|
var bottomComponent = ComponentTopToBottom();
|
||
|
|
||
|
bottomComponent.Index = topHiddenItems;
|
||
|
bottomComponent.SetData(DataSource[topHiddenItems]);
|
||
|
Coloring(bottomComponent);
|
||
|
}
|
||
|
else if (oldTopHiddenItems==(topHiddenItems - 1))
|
||
|
{
|
||
|
var topComponent = ComponentBottomToTop();
|
||
|
|
||
|
var new_index = topHiddenItems + visibleItems - 1;
|
||
|
topComponent.Index = new_index;
|
||
|
topComponent.SetData(DataSource[new_index]);
|
||
|
Coloring(topComponent);
|
||
|
}
|
||
|
// all other cases
|
||
|
else
|
||
|
{
|
||
|
//!
|
||
|
var new_indicies = Enumerable.Range(topHiddenItems, visibleItems).ToArray();
|
||
|
components.ForEach((x, i) => {
|
||
|
x.Index = new_indicies[i];
|
||
|
x.SetData(DataSource[new_indicies[i]]);
|
||
|
Coloring(x);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
if (LayoutBridge!=null)
|
||
|
{
|
||
|
LayoutBridge.SetFiller(CalculateTopFillerSize(), CalculateBottomFillerSize());
|
||
|
LayoutBridge.UpdateLayout();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Determines whether is sort enabled.
|
||
|
/// </summary>
|
||
|
/// <returns><c>true</c> if is sort enabled; otherwise, <c>false</c>.</returns>
|
||
|
public bool IsSortEnabled()
|
||
|
{
|
||
|
if (DataSource.Comparison!=null)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return Sort && SortFunc!=null;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the index of the nearest item.
|
||
|
/// </summary>
|
||
|
/// <returns>The nearest index.</returns>
|
||
|
/// <param name="eventData">Event data.</param>
|
||
|
public virtual int GetNearestIndex(PointerEventData eventData)
|
||
|
{
|
||
|
Vector2 point;
|
||
|
var rectTransform = Container as RectTransform;
|
||
|
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, eventData.position, eventData.pressEventCamera, out point))
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
var rect = rectTransform.rect;
|
||
|
if (!rect.Contains(point))
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return GetNearestIndex(point);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the index of the nearest item.
|
||
|
/// </summary>
|
||
|
/// <returns>The nearest item index.</returns>
|
||
|
/// <param name="point">Point.</param>
|
||
|
public virtual int GetNearestIndex(Vector2 point)
|
||
|
{
|
||
|
if (IsSortEnabled())
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
var pos = IsHorizontal() ? point.x : -point.y;
|
||
|
var index = Mathf.RoundToInt(pos / GetItemSize());
|
||
|
|
||
|
return Mathf.Min(index, DataSource.Count - 1);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// The components cache.
|
||
|
/// </summary>
|
||
|
List<ListViewStringComponent> componentsCache = new List<ListViewStringComponent>();
|
||
|
|
||
|
/// <summary>
|
||
|
/// Determines whether specified component is null.
|
||
|
/// </summary>
|
||
|
/// <returns><c>true</c> if this specified component is null; otherwise, <c>false</c>.</returns>
|
||
|
/// <param name="component">Component.</param>
|
||
|
bool IsNullComponent(ListViewStringComponent component)
|
||
|
{
|
||
|
return component==null;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the new components according max count of visible items.
|
||
|
/// </summary>
|
||
|
/// <returns>The new components.</returns>
|
||
|
List<ListViewStringComponent> GetNewComponents()
|
||
|
{
|
||
|
componentsCache.RemoveAll(IsNullComponent);
|
||
|
var new_components = new List<ListViewStringComponent>();
|
||
|
DataSource.ForEach ((x, i) => {
|
||
|
if (i >= visibleItems)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (components.Count > 0)
|
||
|
{
|
||
|
new_components.Add(components[0]);
|
||
|
components.RemoveAt(0);
|
||
|
}
|
||
|
else if (componentsCache.Count > 0)
|
||
|
{
|
||
|
componentsCache[0].gameObject.SetActive(true);
|
||
|
new_components.Add(componentsCache[0]);
|
||
|
componentsCache.RemoveAt(0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
#if UNITY_4_6 || UNITY_4_7
|
||
|
var background = Instantiate(DefaultItem) as ImageAdvanced;
|
||
|
#else
|
||
|
var background = Instantiate(DefaultItem);
|
||
|
#endif
|
||
|
|
||
|
background.gameObject.SetActive(true);
|
||
|
|
||
|
var component = background.GetComponent<ListViewStringComponent>();
|
||
|
if (component==null)
|
||
|
{
|
||
|
component = background.gameObject.AddComponent<ListViewStringComponent>();
|
||
|
component.Text = background.GetComponentInChildren<Text>();
|
||
|
}
|
||
|
|
||
|
Utilites.FixInstantiated(DefaultItem, background);
|
||
|
|
||
|
new_components.Add(component);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
components.ForEach(x => {
|
||
|
x.MovedToCache();
|
||
|
x.Index = -1;
|
||
|
x.gameObject.SetActive(false);
|
||
|
});
|
||
|
componentsCache.AddRange(components);
|
||
|
components.Clear();
|
||
|
|
||
|
return new_components;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Updates the view.
|
||
|
/// </summary>
|
||
|
void UpdateView()
|
||
|
{
|
||
|
RemoveCallbacks();
|
||
|
|
||
|
if ((CanOptimize()) && (DataSource.Count > 0))
|
||
|
{
|
||
|
visibleItems = (maxVisibleItems < DataSource.Count) ? maxVisibleItems : DataSource.Count;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
visibleItems = DataSource.Count;
|
||
|
}
|
||
|
|
||
|
components = GetNewComponents();
|
||
|
|
||
|
base.Items = components.Convert(x => x as ListViewItem);
|
||
|
|
||
|
components.ForEach(SetComponentData);
|
||
|
|
||
|
AddCallbacks();
|
||
|
|
||
|
topHiddenItems = 0;
|
||
|
bottomHiddenItems = DataSource.Count() - visibleItems;
|
||
|
|
||
|
if (LayoutBridge!=null)
|
||
|
{
|
||
|
LayoutBridge.SetFiller(CalculateTopFillerSize(), CalculateBottomFillerSize());
|
||
|
LayoutBridge.UpdateLayout();
|
||
|
}
|
||
|
|
||
|
if (scrollRect!=null)
|
||
|
{
|
||
|
var r = scrollRect.transform as RectTransform;
|
||
|
r.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, r.rect.width);
|
||
|
}
|
||
|
|
||
|
if ((CanOptimize()) && (DataSource.Count > 0))
|
||
|
{
|
||
|
ScrollUpdate();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Sets the component data.
|
||
|
/// </summary>
|
||
|
/// <param name="component">Component.</param>
|
||
|
/// <param name="index">Index.</param>
|
||
|
void SetComponentData(ListViewStringComponent component, int index)
|
||
|
{
|
||
|
component.Index = index;
|
||
|
component.SetData(DataSource[index]);
|
||
|
Coloring(component);
|
||
|
}
|
||
|
|
||
|
bool IndexNotFound(int index)
|
||
|
{
|
||
|
return index==-1;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Updates the items.
|
||
|
/// </summary>
|
||
|
/// <param name="newItems">New items.</param>
|
||
|
protected virtual void SetNewItems(ObservableList<string> newItems)
|
||
|
{
|
||
|
DataSource.OnChange -= UpdateItems;
|
||
|
|
||
|
if (Sort && SortFunc!=null)
|
||
|
{
|
||
|
newItems.BeginUpdate();
|
||
|
|
||
|
var sorted = SortFunc(newItems).ToArray();
|
||
|
|
||
|
newItems.Clear();
|
||
|
newItems.AddRange(sorted);
|
||
|
|
||
|
newItems.EndUpdate();
|
||
|
}
|
||
|
|
||
|
SilentDeselect(SelectedIndicies);
|
||
|
var new_selected_indicies = SelectedItemsCache.Convert<string,int>(newItems.IndexOf);
|
||
|
new_selected_indicies.RemoveAll(IndexNotFound);
|
||
|
|
||
|
dataSource = newItems;
|
||
|
|
||
|
SilentSelect(new_selected_indicies);
|
||
|
SelectedItemsCache = SelectedIndicies.Convert<int,string>(GetDataItem);
|
||
|
|
||
|
UpdateView();
|
||
|
|
||
|
DataSource.OnChange += UpdateItems;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Calculates the size of the bottom filler.
|
||
|
/// </summary>
|
||
|
/// <returns>The bottom filler size.</returns>
|
||
|
protected virtual float CalculateBottomFillerSize()
|
||
|
{
|
||
|
return (bottomHiddenItems==0) ? 0f : bottomHiddenItems * GetItemSize() - LayoutBridge.GetSpacing();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Calculates the size of the top filler.
|
||
|
/// </summary>
|
||
|
/// <returns>The top filler size.</returns>
|
||
|
protected virtual float CalculateTopFillerSize()
|
||
|
{
|
||
|
return (topHiddenItems==0) ? 0f : topHiddenItems * GetItemSize() - LayoutBridge.GetSpacing();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Calculated selected indicies of new items .
|
||
|
/// </summary>
|
||
|
/// <returns>The selected indicies.</returns>
|
||
|
/// <param name="newItems">New items.</param>
|
||
|
List<int> NewSelectedIndicies(ObservableList<string> newItems)
|
||
|
{
|
||
|
var selected_indicies = new List<int>();
|
||
|
if (newItems.Count==0)
|
||
|
{
|
||
|
return selected_indicies;
|
||
|
}
|
||
|
|
||
|
//duplicated items should not be selected more than at start
|
||
|
var new_items_copy = new List<string>(newItems);
|
||
|
|
||
|
var selected_items = SelectedIndicies.Convert<int,string>(GetDataItem);
|
||
|
|
||
|
selected_items = selected_items.Where(x => {
|
||
|
var is_valid_item = newItems.Contains(x);
|
||
|
if (is_valid_item)
|
||
|
{
|
||
|
new_items_copy.Remove(x);
|
||
|
}
|
||
|
return is_valid_item;
|
||
|
}).ToList();
|
||
|
|
||
|
newItems.ForEach((item, index) => {
|
||
|
if (selected_items.Contains(item))
|
||
|
{
|
||
|
selected_items.Remove(item);
|
||
|
selected_indicies.Add(index);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
return selected_indicies;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Coloring the specified component.
|
||
|
/// </summary>
|
||
|
/// <param name="component">Component.</param>
|
||
|
protected override void Coloring(ListViewItem component)
|
||
|
{
|
||
|
if (component==null)
|
||
|
{
|
||
|
return ;
|
||
|
}
|
||
|
if (SelectedIndicies.Contains(component.Index))
|
||
|
{
|
||
|
SelectColoring(component);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DefaultColoring(component);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Updates the colors of components.
|
||
|
/// </summary>
|
||
|
void UpdateColors()
|
||
|
{
|
||
|
components.ForEach(Coloring);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the component.
|
||
|
/// </summary>
|
||
|
/// <returns>The component.</returns>
|
||
|
/// <param name="index">Index.</param>
|
||
|
ListViewStringComponent GetComponent(int index)
|
||
|
{
|
||
|
return components.Find(x => x.Index==index);
|
||
|
}
|
||
|
|
||
|
/// <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 int Set(string item, bool allowDuplicate=true)
|
||
|
{
|
||
|
int index;
|
||
|
if (!allowDuplicate)
|
||
|
{
|
||
|
index = DataSource.IndexOf(item);
|
||
|
if (index==-1)
|
||
|
{
|
||
|
index = Add(item);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
index = Add(item);
|
||
|
}
|
||
|
Select(index);
|
||
|
|
||
|
return index;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Called when item selected.
|
||
|
/// Use it for change visible style of selected item.
|
||
|
/// </summary>
|
||
|
/// <param name="index">Index.</param>
|
||
|
protected override void SelectItem(int index)
|
||
|
{
|
||
|
SelectColoring(GetComponent(index));
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Called when item deselected.
|
||
|
/// Use it for change visible style of deselected item.
|
||
|
/// </summary>
|
||
|
/// <param name="index">Index.</param>
|
||
|
protected override void DeselectItem(int index)
|
||
|
{
|
||
|
DefaultColoring(GetComponent(index));
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Set highlights colors of specified component.
|
||
|
/// </summary>
|
||
|
/// <param name="component">Component.</param>
|
||
|
protected override void HighlightColoring(ListViewItem component)
|
||
|
{
|
||
|
if (IsSelected(component.Index))
|
||
|
{
|
||
|
return ;
|
||
|
}
|
||
|
HighlightColoring(component as ListViewStringComponent);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Set highlights colors of specified component.
|
||
|
/// </summary>
|
||
|
/// <param name="component">Component.</param>
|
||
|
protected virtual void HighlightColoring(ListViewStringComponent component)
|
||
|
{
|
||
|
if (component==null)
|
||
|
{
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
component.Background.color = HighlightedBackgroundColor;
|
||
|
component.Text.color = HighlightedTextColor;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Set select colors of specified component.
|
||
|
/// </summary>
|
||
|
/// <param name="component">Component.</param>
|
||
|
protected virtual void SelectColoring(ListViewItem component)
|
||
|
{
|
||
|
if (component==null)
|
||
|
{
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
SelectColoring(component as ListViewStringComponent);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Set select colors of specified component.
|
||
|
/// </summary>
|
||
|
/// <param name="component">Component.</param>
|
||
|
protected virtual void SelectColoring(ListViewStringComponent component)
|
||
|
{
|
||
|
if (component==null)
|
||
|
{
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
component.Background.color = selectedBackgroundColor;
|
||
|
component.Text.color = selectedTextColor;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Set default colors of specified component.
|
||
|
/// </summary>
|
||
|
/// <param name="component">Component.</param>
|
||
|
protected virtual void DefaultColoring(ListViewItem component)
|
||
|
{
|
||
|
if (component==null)
|
||
|
{
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
DefaultColoring(component as ListViewStringComponent);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Set default colors of specified component.
|
||
|
/// </summary>
|
||
|
/// <param name="component">Component.</param>
|
||
|
protected virtual void DefaultColoring(ListViewStringComponent component)
|
||
|
{
|
||
|
if (component==null)
|
||
|
{
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
component.Background.color = backgroundColor;
|
||
|
component.Text.color = textColor;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Determines whether item visible.
|
||
|
/// </summary>
|
||
|
/// <returns><c>true</c> if item visible; otherwise, <c>false</c>.</returns>
|
||
|
/// <param name="index">Index.</param>
|
||
|
public bool IsItemVisible(int index)
|
||
|
{
|
||
|
return topHiddenItems<=index && index<=(topHiddenItems + visibleItems - 1);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the visible indicies.
|
||
|
/// </summary>
|
||
|
/// <returns>The visible indicies.</returns>
|
||
|
public List<int> GetVisibleIndicies()
|
||
|
{
|
||
|
return Enumerable.Range(topHiddenItems, visibleItems).ToList();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// This function is called when the MonoBehaviour will be destroyed.
|
||
|
/// </summary>
|
||
|
protected override void OnDestroy()
|
||
|
{
|
||
|
OnSelect.RemoveListener(OnSelectCallback);
|
||
|
OnDeselect.RemoveListener(OnDeselectCallback);
|
||
|
|
||
|
ScrollRect = null;
|
||
|
|
||
|
RemoveCallbacks();
|
||
|
|
||
|
base.OnDestroy();
|
||
|
}
|
||
|
|
||
|
bool needResize;
|
||
|
|
||
|
void Update()
|
||
|
{
|
||
|
if (needResize)
|
||
|
{
|
||
|
Resize();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SetNeedResize()
|
||
|
{
|
||
|
if (!CanOptimize())
|
||
|
{
|
||
|
return ;
|
||
|
}
|
||
|
needResize = true;
|
||
|
}
|
||
|
|
||
|
#region ListViewPaginator support
|
||
|
/// <summary>
|
||
|
/// Gets the ScrollRect.
|
||
|
/// </summary>
|
||
|
/// <returns>The ScrollRect.</returns>
|
||
|
public override ScrollRect GetScrollRect()
|
||
|
{
|
||
|
return ScrollRect;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the items count.
|
||
|
/// </summary>
|
||
|
/// <returns>The items count.</returns>
|
||
|
public override int GetItemsCount()
|
||
|
{
|
||
|
return DataSource.Count;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the items per block count.
|
||
|
/// </summary>
|
||
|
/// <returns>The items per block.</returns>
|
||
|
public override int GetItemsPerBlock()
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the item position by index.
|
||
|
/// </summary>
|
||
|
/// <returns>The item position.</returns>
|
||
|
/// <param name="index">Index.</param>
|
||
|
public override float GetItemPosition(int index)
|
||
|
{
|
||
|
return index * GetItemSize() - GetItemSpacing();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the index of the nearest item.
|
||
|
/// </summary>
|
||
|
/// <returns>The nearest item index.</returns>
|
||
|
public override int GetNearestItemIndex()
|
||
|
{
|
||
|
return Mathf.Clamp(Mathf.RoundToInt(GetScrollValue() / GetItemSize()), 0, DataSource.Count - 1);
|
||
|
}
|
||
|
#endregion
|
||
|
}
|
||
|
}
|