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