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
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 |
|
} |
|
} |