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
{
///
/// ListView sources.
/// List - Use strings as source for list.
/// File - Get strings from file, one line per string.
///
public enum ListViewSources {
List = 0,
File = 1,
}
///
/// List view event.
///
[Serializable]
public class ListViewEvent : UnityEvent {
}
///
/// List view.
/// http://ilih.ru/images/unity-assets/UIWidgets/ListView.png
///
[AddComponentMenu("UI/UIWidgets/ListView")]
public class ListView : ListViewBase {
[SerializeField]
[Obsolete("Use DataSource instead.")]
List strings = new List();
//[SerializeField]
//[HideInInspector]
protected ObservableList dataSource;
///
/// Gets or sets the data source.
///
/// The data source.
public virtual ObservableList DataSource {
get {
if (dataSource==null)
{
#pragma warning disable 0618
dataSource = new ObservableList(strings);
dataSource.OnChange += UpdateItems;
strings = null;
#pragma warning restore 0618
}
return dataSource;
}
set {
SetNewItems(value);
SetScrollValue(0f);
}
}
///
/// Gets the strings.
///
/// The strings.
[Obsolete("Use DataSource instead.")]
public List Strings {
get {
return new List(DataSource);
}
set {
SetNewItems(new ObservableList(value));
SetScrollValue(0f);
}
}
///
/// Gets the strings.
///
/// The strings.
[Obsolete("Use DataSource instead.")]
public new List Items {
get {
return new List(DataSource);
}
set {
SetNewItems(new ObservableList(value));
SetScrollValue(0f);
}
}
[SerializeField]
TextAsset file;
///
/// Gets or sets the file with strings for ListView. One string per line.
///
/// The file.
public TextAsset File {
get {
return file;
}
set {
file = value;
if (file!=null)
{
GetItemsFromFile(file);
SetScrollValue(0f);
}
}
}
///
/// The comments in file start with specified strings.
///
[SerializeField]
public List CommentsStartWith = new List(){"#", "//"};
///
/// The source.
///
[SerializeField]
public ListViewSources Source = ListViewSources.List;
///
/// Allow only unique strings.
///
[SerializeField]
public bool Unique = true;
///
/// Allow empty strings.
///
[SerializeField]
public bool AllowEmptyItems;
[SerializeField]
Color backgroundColor = Color.white;
[SerializeField]
Color textColor = Color.black;
///
/// Default background color.
///
public Color BackgroundColor {
get {
return backgroundColor;
}
set {
backgroundColor = value;
UpdateColors();
}
}
///
/// Default text color.
///
public Color TextColor {
get {
return textColor;
}
set {
textColor = value;
UpdateColors();
}
}
///
/// Color of background on pointer over.
///
[SerializeField]
public Color HighlightedBackgroundColor = new Color(203, 230, 244, 255);
///
/// Color of text on pointer text.
///
[SerializeField]
public Color HighlightedTextColor = Color.black;
[SerializeField]
Color selectedBackgroundColor = new Color(53, 83, 227, 255);
[SerializeField]
Color selectedTextColor = Color.black;
///
/// Background color of selected item.
///
public Color SelectedBackgroundColor {
get {
return selectedBackgroundColor;
}
set {
selectedBackgroundColor = value;
UpdateColors();
}
}
///
/// Text color of selected item.
///
public Color SelectedTextColor {
get {
return selectedTextColor;
}
set {
selectedTextColor = value;
UpdateColors();
}
}
///
/// The default item.
///
[SerializeField]
public ImageAdvanced DefaultItem;
///
/// The components.
///
protected List components = new List();
///
/// The callbacks enter.
///
protected List> callbacksEnter = new List>();
///
/// The callbacks exit.
///
protected List> callbacksExit = new List>();
///
/// The sort.
///
[SerializeField]
[FormerlySerializedAs("Sort")]
protected bool sort = true;
///
/// Sort items.
/// Advice to use DataSource.Comparison instead Sort and SortFunc.
///
public bool Sort {
get {
return sort;
}
set {
sort = value;
if (Sort && isStartedListView && sortFunc!=null)
{
UpdateItems();
}
}
}
///
/// The sort function.
///
protected Func,IEnumerable> sortFunc = items => items.OrderBy(x => x);
///
/// Sort function.
/// Advice to use DataSource.Comparison instead Sort and SortFunc.
///
public Func, IEnumerable> SortFunc {
get {
return sortFunc;
}
set {
sortFunc = value;
if (Sort && isStartedListView && sortFunc!=null)
{
UpdateItems();
}
}
}
///
/// OnSelect event.
///
public ListViewEvent OnSelectString = new ListViewEvent();
///
/// OnDeselect event.
///
public ListViewEvent OnDeselectString = new ListViewEvent();
[SerializeField]
ScrollRect scrollRect;
///
/// Gets or sets the ScrollRect.
///
/// The ScrollRect.
public ScrollRect ScrollRect {
get {
return scrollRect;
}
set {
if (scrollRect!=null)
{
var r = scrollRect.GetComponent();
if (r!=null)
{
r.OnResize.RemoveListener(SetNeedResize);
}
scrollRect.onValueChanged.RemoveListener(OnScrollUpdate);
}
scrollRect = value;
if (scrollRect!=null)
{
var resizeListener = scrollRect.GetComponent();
if (resizeListener==null)
{
resizeListener = scrollRect.gameObject.AddComponent();
}
resizeListener.OnResize.AddListener(SetNeedResize);
scrollRect.onValueChanged.AddListener(OnScrollUpdate);
}
}
}
///
/// The height of the DefaultItem.
///
protected float itemHeight;
///
/// The width of the DefaultItem.
///
protected float itemWidth;
///
/// The height of the ScrollRect.
///
protected float scrollHeight;
///
/// The width of the ScrollRect.
///
protected float scrollWidth;
///
/// Count of visible items.
///
protected int maxVisibleItems;
///
/// Count of visible items.
///
protected int visibleItems;
///
/// Count of hidden items by top filler.
///
protected int topHiddenItems;
///
/// Count of hidden items by bottom filler.
///
protected int bottomHiddenItems;
///
/// The direction.
///
[SerializeField]
protected ListViewDirection direction = ListViewDirection.Vertical;
///
/// Gets or sets the direction.
///
/// The direction.
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();
}
}
///
/// Awake this instance.
///
protected virtual void Awake()
{
Start();
}
[System.NonSerialized]
bool isStartedListView = false;
LayoutGroup layout;
///
/// Gets the layout.
///
/// The layout.
public EasyLayout.EasyLayout Layout {
get {
return layout as EasyLayout.EasyLayout;
}
}
///
/// LayoutBridge.
///
protected ILayoutBridge LayoutBridge;
List SelectedItemsCache;
///
/// Start this instance.
///
public override void Start()
{
if (isStartedListView)
{
return ;
}
isStartedListView = true;
base.Start();
base.Items = new List();
SelectedItemsCache = SelectedIndicies.Convert(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()==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();
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);
}
///
/// Calculates the size of the item.
///
protected virtual void CalculateItemSize()
{
if (LayoutBridge==null)
{
return ;
}
var size = LayoutBridge.GetItemSize();
itemHeight = size.y;
itemWidth = size.x;
}
///
/// Gets the item.
///
/// The item.
/// Index.
protected string GetDataItem(int index)
{
return DataSource[index];
}
///
/// Determines whether this instance is horizontal.
///
/// true if this instance is horizontal; otherwise, false.
public override bool IsHorizontal()
{
return direction==ListViewDirection.Horizontal;
}
///
/// Gets the default height of the item.
///
/// The default item height.
public override float GetDefaultItemHeight()
{
return itemHeight;
}
///
/// Gets the default width of the item.
///
/// The default item width.
public override float GetDefaultItemWidth()
{
return itemWidth;
}
///
/// Gets the spacing between items. Not implemented for ListViewBase.
///
/// The item spacing.
public override float GetItemSpacing()
{
return LayoutBridge.GetSpacing();
}
///
/// Calculates the max count of visible items.
///
protected virtual void CalculateMaxVisibleItems()
{
if (IsHorizontal())
{
maxVisibleItems = Mathf.CeilToInt(scrollWidth / itemWidth) + 1;
}
else
{
maxVisibleItems = Mathf.CeilToInt(scrollHeight / itemHeight) + 1;
}
}
///
/// Handle instance resize.
///
public virtual void Resize()
{
needResize = false;
var scrollRectTransform = scrollRect.transform as RectTransform;
scrollHeight = scrollRectTransform.rect.height;
scrollWidth = scrollRectTransform.rect.width;
CalculateMaxVisibleItems();
UpdateView();
}
///
/// Determines whether this instance can be optimized.
///
/// true if this instance can be optimized; otherwise, false.
protected bool CanOptimize()
{
return scrollRect!=null && (layout!=null || Container.GetComponent()!=null);
}
///
/// Raises the select callback event.
///
/// Index.
/// Item.
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);
}
}
///
/// Raises the deselect callback event.
///
/// Index.
/// Item.
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);
}
}
///
/// Updates the items.
///
public override void UpdateItems()
{
if (Source==ListViewSources.List)
{
SetNewItems(DataSource);
}
else
{
Source = ListViewSources.List;
GetItemsFromFile(File);
}
}
///
/// Clear strings list.
///
public override void Clear()
{
DataSource.Clear();
}
///
/// Gets the items from file.
///
public void GetItemsFromFile()
{
GetItemsFromFile(File);
}
///
/// Trim end of string.
///
/// The trimmed string.
/// String.
string StringTrimEnd(string str)
{
return str.TrimEnd();
}
///
/// Determines whether specified string not empty.
///
/// true if specified string not empty; otherwise, false.
/// String.
bool IsStringNotEmpty(string str)
{
return str!=string.Empty;
}
///
/// Determines whether specified string not comment.
///
/// true, if string not comment, false otherwise.
/// String.
bool NotComment(string str)
{
return !CommentsStartWith.Any(comment => str.StartsWith(comment));
}
///
/// Gets the items from file.
///
/// Source file.
public void GetItemsFromFile(TextAsset sourceFile)
{
if (file==null)
{
return ;
}
var new_items = sourceFile.text.Split(new string[] {"\r\n", "\r", "\n"}, StringSplitOptions.None).Select(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());
}
///
/// Finds the indicies of specified item.
///
/// The indicies.
/// Item.
public virtual List FindIndicies(string item)
{
return Enumerable.Range(0, DataSource.Count)
.Where(i => DataSource[i]==item)
.ToList();
}
///
/// Finds the index of specified item.
///
/// The index.
/// Item.
public virtual int FindIndex(string item)
{
return DataSource.IndexOf(item);
}
///
/// Add the specified item.
///
/// Item.
/// Index of added item.
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;
}
}
///
/// Remove the specified item.
///
/// Item.
/// Index of removed item.
public virtual int Remove(string item)
{
var index = FindIndex(item);
if (index==-1)
{
return index;
}
DataSource.Remove(item);
return index;
}
///
/// Removes the callback.
///
/// Item.
/// Index.
/// Component.
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]);
}
}
///
/// Removes the callbacks.
///
void RemoveCallbacks()
{
components.ForEach(RemoveCallback);
callbacksEnter.Clear();
callbacksExit.Clear();
}
///
/// Adds the callbacks.
///
void AddCallbacks()
{
components.ForEach(AddCallback);
}
///
/// Adds the callback.
///
/// Component.
/// Index.
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]);
}
///
/// Determines if item exists with the specified index.
///
/// true if item exists with the specified index; otherwise, false.
/// Index.
public override bool IsValid(int index)
{
return (index >= 0) && (index < DataSource.Count);
}
///
/// Raises the pointer enter callback event.
///
/// Component.
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);
}
///
/// Raises the pointer exit callback event.
///
/// Component.
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);
}
///
/// Sets the scroll value.
///
/// Value.
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();
}
}
///
/// Gets the scroll value.
///
/// The scroll value.
protected float GetScrollValue()
{
var pos = scrollRect.content.anchoredPosition;
return Mathf.Max(0f, (IsHorizontal()) ? -pos.x : pos.y);
}
///
/// Gets the size of the item.
///
/// The item size.
protected float GetItemSize()
{
return (IsHorizontal())
? itemWidth + LayoutBridge.GetSpacing()
: itemHeight + LayoutBridge.GetSpacing();
}
///
/// Gets the size of the scroll.
///
/// The scroll size.
protected float GetScrollSize()
{
return (IsHorizontal()) ? scrollWidth : scrollHeight;
}
///
/// Scrolls to item with specifid index.
///
/// Index.
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));
}
}
///
/// Gets the item bottom position by index.
///
/// The item bottom position.
/// Index.
public virtual float GetItemPositionBottom(int index)
{
return GetItemPosition(index) + GetItemSize() - LayoutBridge.GetSpacing() + LayoutBridge.GetMargin() - GetScrollSize();
}
///
/// Gets the last index of the visible.
///
/// The last visible index.
/// If set to true strict.
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;
}
///
/// Gets the first index of the visible.
///
/// The first visible index.
/// If set to true strict.
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));
}
///
/// Move first component from top to bottom.
///
/// Component.
ListViewStringComponent ComponentTopToBottom()
{
var bottom = components.Count - 1;
var bottomComponent = components[bottom];
components.RemoveAt(bottom);
components.Insert(0, bottomComponent);
bottomComponent.transform.SetAsFirstSibling();
return bottomComponent;
}
///
/// Move last component from bottom to top.
///
/// Component.
ListViewStringComponent ComponentBottomToTop()
{
var topComponent = components[0];
components.RemoveAt(0);
components.Add(topComponent);
topComponent.transform.SetAsLastSibling();
return topComponent;
}
///
/// Raises the scroll update event.
///
/// Position.
void OnScrollUpdate(Vector2 position)
{
ScrollUpdate();
}
///
/// Raises the scroll event.
///
/// Value.
void OnScroll(float value)
{
ScrollUpdate();
}
///
/// Update ListView according scroll position.
///
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();
}
}
///
/// Determines whether is sort enabled.
///
/// true if is sort enabled; otherwise, false.
public bool IsSortEnabled()
{
if (DataSource.Comparison!=null)
{
return true;
}
return Sort && SortFunc!=null;
}
///
/// Gets the index of the nearest item.
///
/// The nearest index.
/// Event data.
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);
}
///
/// Gets the index of the nearest item.
///
/// The nearest item index.
/// Point.
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);
}
///
/// The components cache.
///
List componentsCache = new List();
///
/// Determines whether specified component is null.
///
/// true if this specified component is null; otherwise, false.
/// Component.
bool IsNullComponent(ListViewStringComponent component)
{
return component==null;
}
///
/// Gets the new components according max count of visible items.
///
/// The new components.
List GetNewComponents()
{
componentsCache.RemoveAll(IsNullComponent);
var new_components = new List();
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();
if (component==null)
{
component = background.gameObject.AddComponent();
component.Text = background.GetComponentInChildren();
}
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;
}
///
/// Updates the view.
///
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();
}
}
///
/// Sets the component data.
///
/// Component.
/// Index.
void SetComponentData(ListViewStringComponent component, int index)
{
component.Index = index;
component.SetData(DataSource[index]);
Coloring(component);
}
bool IndexNotFound(int index)
{
return index==-1;
}
///
/// Updates the items.
///
/// New items.
protected virtual void SetNewItems(ObservableList 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(newItems.IndexOf);
new_selected_indicies.RemoveAll(IndexNotFound);
dataSource = newItems;
SilentSelect(new_selected_indicies);
SelectedItemsCache = SelectedIndicies.Convert(GetDataItem);
UpdateView();
DataSource.OnChange += UpdateItems;
}
///
/// Calculates the size of the bottom filler.
///
/// The bottom filler size.
protected virtual float CalculateBottomFillerSize()
{
return (bottomHiddenItems==0) ? 0f : bottomHiddenItems * GetItemSize() - LayoutBridge.GetSpacing();
}
///
/// Calculates the size of the top filler.
///
/// The top filler size.
protected virtual float CalculateTopFillerSize()
{
return (topHiddenItems==0) ? 0f : topHiddenItems * GetItemSize() - LayoutBridge.GetSpacing();
}
///
/// Calculated selected indicies of new items .
///
/// The selected indicies.
/// New items.
List NewSelectedIndicies(ObservableList newItems)
{
var selected_indicies = new List();
if (newItems.Count==0)
{
return selected_indicies;
}
//duplicated items should not be selected more than at start
var new_items_copy = new List(newItems);
var selected_items = SelectedIndicies.Convert(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;
}
///
/// Coloring the specified component.
///
/// Component.
protected override void Coloring(ListViewItem component)
{
if (component==null)
{
return ;
}
if (SelectedIndicies.Contains(component.Index))
{
SelectColoring(component);
}
else
{
DefaultColoring(component);
}
}
///
/// Updates the colors of components.
///
void UpdateColors()
{
components.ForEach(Coloring);
}
///
/// Gets the component.
///
/// The component.
/// Index.
ListViewStringComponent GetComponent(int index)
{
return components.Find(x => x.Index==index);
}
///
/// Set the specified item.
///
/// Item.
/// If set to true allow duplicate.
/// Index of item.
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;
}
///
/// Called when item selected.
/// Use it for change visible style of selected item.
///
/// Index.
protected override void SelectItem(int index)
{
SelectColoring(GetComponent(index));
}
///
/// Called when item deselected.
/// Use it for change visible style of deselected item.
///
/// Index.
protected override void DeselectItem(int index)
{
DefaultColoring(GetComponent(index));
}
///
/// Set highlights colors of specified component.
///
/// Component.
protected override void HighlightColoring(ListViewItem component)
{
if (IsSelected(component.Index))
{
return ;
}
HighlightColoring(component as ListViewStringComponent);
}
///
/// Set highlights colors of specified component.
///
/// Component.
protected virtual void HighlightColoring(ListViewStringComponent component)
{
if (component==null)
{
return ;
}
component.Background.color = HighlightedBackgroundColor;
component.Text.color = HighlightedTextColor;
}
///
/// Set select colors of specified component.
///
/// Component.
protected virtual void SelectColoring(ListViewItem component)
{
if (component==null)
{
return ;
}
SelectColoring(component as ListViewStringComponent);
}
///
/// Set select colors of specified component.
///
/// Component.
protected virtual void SelectColoring(ListViewStringComponent component)
{
if (component==null)
{
return ;
}
component.Background.color = selectedBackgroundColor;
component.Text.color = selectedTextColor;
}
///
/// Set default colors of specified component.
///
/// Component.
protected virtual void DefaultColoring(ListViewItem component)
{
if (component==null)
{
return ;
}
DefaultColoring(component as ListViewStringComponent);
}
///
/// Set default colors of specified component.
///
/// Component.
protected virtual void DefaultColoring(ListViewStringComponent component)
{
if (component==null)
{
return ;
}
component.Background.color = backgroundColor;
component.Text.color = textColor;
}
///
/// Determines whether item visible.
///
/// true if item visible; otherwise, false.
/// Index.
public bool IsItemVisible(int index)
{
return topHiddenItems<=index && index<=(topHiddenItems + visibleItems - 1);
}
///
/// Gets the visible indicies.
///
/// The visible indicies.
public List GetVisibleIndicies()
{
return Enumerable.Range(topHiddenItems, visibleItems).ToList();
}
///
/// This function is called when the MonoBehaviour will be destroyed.
///
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
///
/// Gets the ScrollRect.
///
/// The ScrollRect.
public override ScrollRect GetScrollRect()
{
return ScrollRect;
}
///
/// Gets the items count.
///
/// The items count.
public override int GetItemsCount()
{
return DataSource.Count;
}
///
/// Gets the items per block count.
///
/// The items per block.
public override int GetItemsPerBlock()
{
return 1;
}
///
/// Gets the item position by index.
///
/// The item position.
/// Index.
public override float GetItemPosition(int index)
{
return index * GetItemSize() - GetItemSpacing();
}
///
/// Gets the index of the nearest item.
///
/// The nearest item index.
public override int GetNearestItemIndex()
{
return Mathf.Clamp(Mathf.RoundToInt(GetScrollValue() / GetItemSize()), 0, DataSource.Count - 1);
}
#endregion
}
}