using System; using System.Collections.Generic; using System.Linq; using EasyLayout; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Serialization; using UnityEngine.UI; namespace UIWidgets { [AddComponentMenu("UI/ListView", 250)] public class ListView : ListViewBase { [SerializeField] [Obsolete("Use DataSource instead.")] private List strings = new List(); private ObservableList dataSource; [SerializeField] private TextAsset file; [SerializeField] public List CommentsStartWith = new List { "#", "//" }; [SerializeField] public ListViewSources Source; [SerializeField] public bool Unique = true; [SerializeField] public bool AllowEmptyItems; [SerializeField] private Color backgroundColor = Color.white; [SerializeField] private Color textColor = Color.black; [SerializeField] public Color HighlightedBackgroundColor = new Color(203f, 230f, 244f, 255f); [SerializeField] public Color HighlightedTextColor = Color.black; [SerializeField] private Color selectedBackgroundColor = new Color(53f, 83f, 227f, 255f); [SerializeField] private Color selectedTextColor = Color.black; [SerializeField] public ImageAdvanced DefaultItem; private List components = new List(); private List> callbacksEnter = new List>(); private List> callbacksExit = new List>(); [FormerlySerializedAs("Sort")] [SerializeField] private bool sort = true; private Func, IEnumerable> sortFunc = (IEnumerable items) => items.OrderBy((string x) => x); public ListViewEvent OnSelectString = new ListViewEvent(); public ListViewEvent OnDeselectString = new ListViewEvent(); [SerializeField] private ScrollRect scrollRect; private float itemHeight; private float itemWidth; private float scrollHeight; private float scrollWidth; private int maxVisibleItems; private int visibleItems; private int topHiddenItems; private int bottomHiddenItems; [SerializeField] private ListViewDirection direction = ListViewDirection.Vertical; [NonSerialized] private bool isStartedListView; private LayoutGroup layout; protected ILayoutBridge LayoutBridge; private List SelectedItemsCache; private List componentsCache = new List(); private bool needResize; public ObservableList DataSource { get { if (dataSource == null) { dataSource = new ObservableList(strings); dataSource.OnChange += UpdateItems; strings = null; } return dataSource; } set { SetNewItems(value); SetScrollValue(0f); } } [Obsolete("Use DataSource instead.")] public List Strings { get { return new List(DataSource); } set { SetNewItems(new ObservableList(value)); SetScrollValue(0f); } } [Obsolete("Use DataSource instead.")] public new List Items { get { return new List(DataSource); } set { SetNewItems(new ObservableList(value)); SetScrollValue(0f); } } public TextAsset File { get { return file; } set { file = value; if (file != null) { GetItemsFromFile(file); SetScrollValue(0f); } } } public Color BackgroundColor { get { return backgroundColor; } set { backgroundColor = value; UpdateColors(); } } public Color TextColor { get { return textColor; } set { textColor = value; UpdateColors(); } } public Color SelectedBackgroundColor { get { return selectedBackgroundColor; } set { selectedBackgroundColor = value; UpdateColors(); } } public Color SelectedTextColor { get { return selectedTextColor; } set { selectedTextColor = value; UpdateColors(); } } public bool Sort { get { return sort; } set { sort = value; if (Sort && isStartedListView && sortFunc != null) { UpdateItems(); } } } public Func, IEnumerable> SortFunc { get { return sortFunc; } set { sortFunc = value; if (Sort && isStartedListView && sortFunc != null) { UpdateItems(); } } } public ScrollRect ScrollRect { get { return scrollRect; } set { if (scrollRect != null) { ResizeListener component = scrollRect.GetComponent(); if (component != null) { component.OnResize.RemoveListener(SetNeedResize); } scrollRect.onValueChanged.RemoveListener(OnScrollUpdate); } scrollRect = value; if (scrollRect != null) { ResizeListener resizeListener = scrollRect.GetComponent() ?? scrollRect.gameObject.AddComponent(); resizeListener.OnResize.AddListener(SetNeedResize); scrollRect.onValueChanged.AddListener(OnScrollUpdate); } } } public ListViewDirection Direction { get { return direction; } set { direction = value; if ((bool)scrollRect) { scrollRect.horizontal = IsHorizontal(); scrollRect.vertical = !IsHorizontal(); } if (CanOptimize() && layout is global::EasyLayout.EasyLayout) { LayoutBridge.IsHorizontal = IsHorizontal(); CalculateMaxVisibleItems(); } UpdateView(); } } public global::EasyLayout.EasyLayout Layout { get { return layout as global::EasyLayout.EasyLayout; } } private void Awake() { Start(); } public override void Start() { if (isStartedListView) { return; } isStartedListView = true; base.Start(); base.Items = new List(); SelectedItemsCache = base.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; RectTransform rectTransform = scrollRect.transform as RectTransform; scrollHeight = rectTransform.rect.height; scrollWidth = rectTransform.rect.width; layout = Container.GetComponent(); if (layout is global::EasyLayout.EasyLayout) { LayoutBridge = new EasyLayoutBridge(layout as global::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); } protected virtual void CalculateItemSize() { if (LayoutBridge != null) { Vector2 itemSize = LayoutBridge.GetItemSize(); itemHeight = itemSize.y; itemWidth = itemSize.x; } } protected string GetDataItem(int index) { return DataSource[index]; } protected bool IsHorizontal() { return direction == ListViewDirection.Horizontal; } private void CalculateMaxVisibleItems() { if (IsHorizontal()) { maxVisibleItems = Mathf.CeilToInt(scrollWidth / itemWidth) + 1; } else { maxVisibleItems = Mathf.CeilToInt(scrollHeight / itemHeight) + 1; } } private void Resize() { needResize = false; RectTransform rectTransform = scrollRect.transform as RectTransform; scrollHeight = rectTransform.rect.height; scrollWidth = rectTransform.rect.width; CalculateMaxVisibleItems(); UpdateView(); } private bool CanOptimize() { return scrollRect != null && (layout != null || Container.GetComponent() != null); } private 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); } } private 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); } } public override void UpdateItems() { if (Source == ListViewSources.List) { SetNewItems(DataSource); return; } Source = ListViewSources.List; GetItemsFromFile(File); } public override void Clear() { DataSource.Clear(); } public void GetItemsFromFile() { GetItemsFromFile(File); } private string StringTrimEnd(string str) { return str.TrimEnd(); } private bool IsStringNotEmpty(string str) { return str != string.Empty; } private bool NotComment(string str) { return !CommentsStartWith.Any((string comment) => str.StartsWith(comment)); } public void GetItemsFromFile(TextAsset sourceFile) { if (!(file == null)) { IEnumerable enumerable = sourceFile.text.Split(new string[3] { "\r\n", "\r", "\n" }, StringSplitOptions.None).Select(StringTrimEnd); if (Unique) { enumerable = enumerable.Distinct(); } if (!AllowEmptyItems) { enumerable = enumerable.Where(IsStringNotEmpty); } if (CommentsStartWith.Count > 0) { enumerable = enumerable.Where(NotComment); } SetNewItems(enumerable.ToObservableList()); } } public virtual List FindIndicies(string item) { return (from i in Enumerable.Range(0, DataSource.Count) where DataSource[i] == item select i).ToList(); } public virtual int FindIndex(string item) { return DataSource.IndexOf(item); } public virtual int Add(string item) { List second = ((!Sort || SortFunc == null) ? null : FindIndicies(item)); DataSource.Add(item); if (Sort && SortFunc != null) { List list = FindIndicies(item); int[] array = list.Except(second).ToArray(); if (array.Length > 0) { return array[0]; } if (list.Count > 0) { return list[0]; } return -1; } return DataSource.Count - 1; } public virtual int Remove(string item) { int num = FindIndex(item); if (num == -1) { return num; } DataSource.Remove(item); return num; } private void RemoveCallback(ListViewStringComponent component, int index) { if (!(component == null)) { if (index < callbacksEnter.Count) { component.onPointerEnter.RemoveListener(callbacksEnter[index]); } if (index < callbacksExit.Count) { component.onPointerExit.RemoveListener(callbacksExit[index]); } } } private void RemoveCallbacks() { components.ForEach(RemoveCallback); callbacksEnter.Clear(); callbacksExit.Clear(); } private void AddCallbacks() { components.ForEach(AddCallback); } private void AddCallback(ListViewStringComponent component, int index) { callbacksEnter.Add(delegate { OnPointerEnterCallback(component); }); callbacksExit.Add(delegate { OnPointerExitCallback(component); }); component.onPointerEnter.AddListener(callbacksEnter[index]); component.onPointerExit.AddListener(callbacksExit[index]); } public override bool IsValid(int index) { return index >= 0 && index < DataSource.Count; } private void OnPointerEnterCallback(ListViewStringComponent component) { if (!IsValid(component.Index)) { string message = string.Format("Index must be between 0 and Items.Count ({0})", DataSource.Count); throw new IndexOutOfRangeException(message); } if (!IsSelected(component.Index)) { HighlightColoring(component); } } private void OnPointerExitCallback(ListViewStringComponent component) { if (!IsValid(component.Index)) { string message = string.Format("Index must be between 0 and Items.Count ({0})", DataSource.Count); throw new IndexOutOfRangeException(message); } if (!IsSelected(component.Index)) { DefaultColoring(component); } } protected void SetScrollValue(float value) { Vector2 anchoredPosition = scrollRect.content.anchoredPosition; Vector2 vector = new Vector2(anchoredPosition.x, value); if (vector != anchoredPosition) { scrollRect.content.anchoredPosition = vector; ScrollUpdate(); } } protected float GetScrollValue() { Vector2 anchoredPosition = scrollRect.content.anchoredPosition; return Mathf.Max(0f, (!IsHorizontal()) ? anchoredPosition.y : (0f - anchoredPosition.x)); } protected float GetItemSize() { return (!IsHorizontal()) ? (itemHeight + LayoutBridge.GetSpacing()) : (itemWidth + LayoutBridge.GetSpacing()); } protected float GetScrollSize() { return (!IsHorizontal()) ? scrollHeight : scrollWidth; } protected override void ScrollTo(int index) { if (CanOptimize()) { int firstVisibleIndex = GetFirstVisibleIndex(true); int lastVisibleIndex = GetLastVisibleIndex(true); if (firstVisibleIndex > index) { float scrollValue = (float)index * GetItemSize(); SetScrollValue(scrollValue); } else if (lastVisibleIndex < index) { float scrollValue2 = (float)(index + 1) * GetItemSize() - LayoutBridge.GetSpacing() + LayoutBridge.GetMargin() - GetScrollSize(); SetScrollValue(scrollValue2); } } } private int GetLastVisibleIndex(bool strict = false) { float num = GetScrollValue() + GetScrollSize(); int num2 = ((!strict) ? Mathf.CeilToInt(num / GetItemSize()) : Mathf.FloorToInt(num / GetItemSize())); return num2 - 1; } private int GetFirstVisibleIndex(bool strict = false) { int num = ((!strict) ? Mathf.FloorToInt(GetScrollValue() / GetItemSize()) : Mathf.CeilToInt(GetScrollValue() / GetItemSize())); if (strict) { return num; } return Mathf.Min(num, Mathf.Max(0, DataSource.Count - visibleItems)); } private ListViewStringComponent ComponentTopToBottom() { int index = components.Count - 1; ListViewStringComponent listViewStringComponent = components[index]; components.RemoveAt(index); components.Insert(0, listViewStringComponent); listViewStringComponent.transform.SetAsFirstSibling(); return listViewStringComponent; } private ListViewStringComponent ComponentBottomToTop() { ListViewStringComponent listViewStringComponent = components[0]; components.RemoveAt(0); components.Add(listViewStringComponent); listViewStringComponent.transform.SetAsLastSibling(); return listViewStringComponent; } private void OnScrollUpdate(Vector2 position) { ScrollUpdate(); } private void OnScroll(float value) { ScrollUpdate(); } private void ScrollUpdate() { int num = topHiddenItems; topHiddenItems = GetFirstVisibleIndex(); bottomHiddenItems = Mathf.Max(0, DataSource.Count - visibleItems - topHiddenItems); if (num != topHiddenItems) { if (num == topHiddenItems + 1) { ListViewStringComponent listViewStringComponent = ComponentTopToBottom(); listViewStringComponent.Index = topHiddenItems; listViewStringComponent.Text.text = DataSource[topHiddenItems]; Coloring(listViewStringComponent); } else if (num == topHiddenItems - 1) { ListViewStringComponent listViewStringComponent2 = ComponentBottomToTop(); int index = (listViewStringComponent2.Index = topHiddenItems + visibleItems - 1); listViewStringComponent2.Text.text = DataSource[index]; Coloring(listViewStringComponent2); } else { int[] new_indicies = Enumerable.Range(topHiddenItems, visibleItems).ToArray(); components.ForEach(delegate(ListViewStringComponent x, int i) { x.Index = new_indicies[i]; x.Text.text = DataSource[new_indicies[i]]; Coloring(x); }); } } if (LayoutBridge != null) { LayoutBridge.SetFiller(CalculateTopFillerSize(), CalculateBottomFillerSize()); LayoutBridge.UpdateLayout(); } } private bool IsNullComponent(ListViewStringComponent component) { return component == null; } private List GetNewComponents() { componentsCache.RemoveAll(IsNullComponent); List new_components = new List(); DataSource.ForEach(delegate(string x, int i) { if (i < visibleItems) { 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 { ImageAdvanced imageAdvanced = UnityEngine.Object.Instantiate(DefaultItem); imageAdvanced.gameObject.SetActive(true); ListViewStringComponent listViewStringComponent = imageAdvanced.GetComponent(); if (listViewStringComponent == null) { listViewStringComponent = imageAdvanced.gameObject.AddComponent(); listViewStringComponent.Background = imageAdvanced; listViewStringComponent.Text = imageAdvanced.GetComponentInChildren(); } Utilites.FixInstantiated(DefaultItem, imageAdvanced); listViewStringComponent.gameObject.SetActive(true); new_components.Add(listViewStringComponent); } } }); components.ForEach(delegate(ListViewStringComponent x) { x.MovedToCache(); x.Index = -1; x.gameObject.SetActive(false); }); componentsCache.AddRange(components); components.Clear(); return new_components; } private void UpdateView() { RemoveCallbacks(); if (CanOptimize() && DataSource.Count > 0) { visibleItems = ((maxVisibleItems >= DataSource.Count) ? DataSource.Count : maxVisibleItems); } else { visibleItems = DataSource.Count; } components = GetNewComponents(); base.Items = components.Convert((Converter)((ListViewStringComponent x) => x)); components.ForEach(SetComponentData); AddCallbacks(); topHiddenItems = 0; bottomHiddenItems = DataSource.Count() - visibleItems; if (LayoutBridge != null) { LayoutBridge.SetFiller(CalculateTopFillerSize(), CalculateBottomFillerSize()); LayoutBridge.UpdateLayout(); } if (scrollRect != null) { RectTransform rectTransform = scrollRect.transform as RectTransform; rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, rectTransform.rect.width); } } private void SetComponentData(ListViewStringComponent component, int index) { component.Index = index; component.Text.text = DataSource[index]; Coloring(component); } private bool IndexNotFound(int index) { return index == -1; } private void SetNewItems(ObservableList newItems) { DataSource.OnChange -= UpdateItems; if (Sort && SortFunc != null) { newItems.BeginUpdate(); string[] array = SortFunc(newItems).ToArray(); newItems.Clear(); newItems.AddRange(array); newItems.EndUpdate(); } SilentDeselect(base.SelectedIndicies); List list = SelectedItemsCache.Convert(newItems.IndexOf); list.RemoveAll(IndexNotFound); dataSource = newItems; SilentSelect(list); SelectedItemsCache = base.SelectedIndicies.Convert(GetDataItem); UpdateView(); DataSource.OnChange += UpdateItems; } private float CalculateBottomFillerSize() { return (bottomHiddenItems != 0) ? ((float)bottomHiddenItems * GetItemSize() - LayoutBridge.GetSpacing()) : 0f; } private float CalculateTopFillerSize() { return (topHiddenItems != 0) ? ((float)topHiddenItems * GetItemSize() - LayoutBridge.GetSpacing()) : 0f; } private List NewSelectedIndicies(ObservableList newItems) { List selected_indicies = new List(); if (newItems.Count == 0) { return selected_indicies; } List new_items_copy = new List(newItems); List selected_items = base.SelectedIndicies.Convert(GetDataItem); selected_items = selected_items.Where(delegate(string x) { bool flag = newItems.Contains(x); if (flag) { new_items_copy.Remove(x); } return flag; }).ToList(); newItems.ForEach(delegate(string item, int index) { if (selected_items.Contains(item)) { selected_items.Remove(item); selected_indicies.Add(index); } }); return selected_indicies; } protected override void Coloring(ListViewItem component) { if (!(component == null)) { if (base.SelectedIndicies.Contains(component.Index)) { SelectColoring(component); } else { DefaultColoring(component); } } } private void UpdateColors() { components.ForEach(Coloring); } private ListViewStringComponent GetComponent(int index) { return components.Find((ListViewStringComponent x) => x.Index == index); } public int Set(string item, bool allowDuplicate = true) { int num; if (!allowDuplicate) { num = DataSource.IndexOf(item); if (num == -1) { num = Add(item); } } else { num = Add(item); } Select(num); return num; } protected override void SelectItem(int index) { SelectColoring(GetComponent(index)); } protected override void DeselectItem(int index) { DefaultColoring(GetComponent(index)); } protected override void HighlightColoring(ListViewItem component) { if (!IsSelected(component.Index)) { HighlightColoring(component as ListViewStringComponent); } } protected virtual void HighlightColoring(ListViewStringComponent component) { if (!(component == null)) { component.Background.color = HighlightedBackgroundColor; component.Text.color = HighlightedTextColor; } } protected virtual void SelectColoring(ListViewItem component) { if (!(component == null)) { SelectColoring(component as ListViewStringComponent); } } protected virtual void SelectColoring(ListViewStringComponent component) { if (!(component == null)) { component.Background.color = selectedBackgroundColor; component.Text.color = selectedTextColor; } } protected virtual void DefaultColoring(ListViewItem component) { if (!(component == null)) { DefaultColoring(component as ListViewStringComponent); } } protected virtual void DefaultColoring(ListViewStringComponent component) { if (!(component == null)) { component.Background.color = backgroundColor; component.Text.color = textColor; } } protected override void OnDestroy() { OnSelect.RemoveListener(OnSelectCallback); OnDeselect.RemoveListener(OnDeselectCallback); ScrollRect = null; RemoveCallbacks(); base.OnDestroy(); } private void Update() { if (needResize) { Resize(); } } private void SetNeedResize() { if (CanOptimize()) { needResize = true; } } } }