using UnityEngine; using UnityEngine.UI; using UnityEngine.Events; using UnityEngine.EventSystems; using System.Collections.Generic; using System; using System.Linq; namespace UIWidgets { /// /// ListView with dynamic items heights. /// [AddComponentMenu("UI/UIWidgets/ListViewHeight")] public class ListViewHeight : ListView { protected Dictionary Heights = new Dictionary(); ListViewStringComponent defaultItemCopy; RectTransform defaultItemCopyRect; /// /// Gets the default item copy. /// /// The default item copy. protected ListViewStringComponent DefaultItemCopy { get { if (defaultItemCopy==null) { var copy = Instantiate(DefaultItem) as ImageAdvanced; copy.gameObject.SetActive(true); defaultItemCopy = copy.GetComponent(); defaultItemCopy.transform.SetParent(DefaultItem.transform.parent, false); defaultItemCopy.gameObject.name = "DefaultItemCopy"; defaultItemCopy.gameObject.SetActive(false); Utilites.FixInstantiated(DefaultItem, defaultItemCopy); } return defaultItemCopy; } } /// /// Gets the RectTransform of DefaultItemCopy. /// /// RectTransform. protected RectTransform DefaultItemCopyRect { get { if (defaultItemCopyRect==null) { defaultItemCopyRect = defaultItemCopy.transform as RectTransform; } return defaultItemCopyRect; } } /// /// Awake this instance. /// protected override void Awake() { Start(); } /// /// Calculates the max count of visible items. /// protected override void CalculateMaxVisibleItems() { SetItemsHeight(DataSource); var height = scrollHeight; maxVisibleItems = DataSource.OrderBy(GetItemHeight).TakeWhile(x => { height -= Heights[x]; return height > 0; }).Count() + 2; } /// /// Calculates the size of the item. /// protected override void CalculateItemSize() { var rect = DefaultItem.transform as RectTransform; #if UNITY_4_6 || UNITY_4_7 var layout_elements = rect.GetComponents().OfType(); #else var layout_elements = rect.GetComponents(); #endif if (itemHeight==0) { var preffered_height = layout_elements.Max(x => Mathf.Max(x.minHeight, x.preferredHeight)); itemHeight = (preffered_height > 0) ? preffered_height : rect.rect.height; } if (itemWidth==0) { var preffered_width = layout_elements.Max(x => Mathf.Max(x.minWidth, x.preferredWidth)); itemWidth = (preffered_width > 0) ? preffered_width : rect.rect.width; } } /// /// Scrolls to item with specifid index. /// /// Index. public override void ScrollTo(int index) { if (!CanOptimize()) { return ; } var top = GetScrollValue(); var bottom = GetScrollValue() + scrollHeight; var item_starts = ItemStartAt(index); var item_ends = ItemEndAt(index) + LayoutBridge.GetMargin(); if (item_starts < top) { SetScrollValue(item_starts); } else if (item_ends > bottom) { SetScrollValue(item_ends - GetScrollSize()); } } /// /// Calculates the size of the bottom filler. /// /// The bottom filler size. protected override float CalculateBottomFillerSize() { if (bottomHiddenItems==0) { return 0f; } var height = DataSource.GetRange(topHiddenItems + visibleItems, bottomHiddenItems).SumFloat(GetItemHeight); return Mathf.Max(0, height + (LayoutBridge.GetSpacing() * (bottomHiddenItems - 1))); } /// /// Calculates the size of the top filler. /// /// The top filler size. protected override float CalculateTopFillerSize() { if (topHiddenItems==0) { return 0f; } var height = DataSource.GetRange(0, topHiddenItems).SumFloat(GetItemHeight); return Mathf.Max(0, height + (LayoutBridge.GetSpacing() * (topHiddenItems - 1))); } float GetItemHeight(string item) { return Heights[item]; } /// /// Total height of items before specified index. /// /// Height. /// Index. float ItemStartAt(int index) { var height = DataSource.GetRange(0, index).SumFloat(GetItemHeight); return height + (LayoutBridge.GetSpacing() * index); } /// /// Total height of items before and with specified index. /// /// The . /// Index. float ItemEndAt(int index) { var height = DataSource.GetRange(0, index + 1).SumFloat(GetItemHeight); return height + (LayoutBridge.GetSpacing() * index); } /// /// Add the specified item. /// /// Item. /// Index of added item. public override int Add(string item) { if (item==null) { throw new ArgumentNullException("item", "Item is null."); } if (!Heights.ContainsKey(item)) { Heights.Add(item, CalculateItemHeight(item)); } return base.Add(item); } /// /// Calculate and sets the height of the items. /// /// Items. /// If set to true force update. void SetItemsHeight(ObservableList items, bool forceUpdate = true) { if (forceUpdate) { Heights.Clear(); } items.Except(Heights.Keys).Distinct().ForEach(x => { Heights.Add(x, CalculateItemHeight(x)); }); } /// /// Resize this instance. /// public override void Resize() { SetItemsHeight(DataSource, true); base.Resize(); } /// /// Updates the items. /// /// New items. protected override void SetNewItems(ObservableList newItems) { SetItemsHeight(newItems); CalculateMaxVisibleItems(); base.SetNewItems(newItems); } /// /// Gets the height of the index by. /// /// The index by height. /// Height. int GetIndexByHeight(float height) { var spacing = LayoutBridge.GetSpacing(); return DataSource.TakeWhile((item, index) => { height -= Heights[item]; if (index > 0) { height -= spacing; } return height >= 0; }).Count(); } /// /// Gets the last index of the visible. /// /// The last visible index. /// If set to true strict. protected override int GetLastVisibleIndex(bool strict=false) { var last_visible_index = GetIndexByHeight(GetScrollValue() + scrollHeight); return (strict) ? last_visible_index : last_visible_index + 2; } /// /// Gets the first index of the visible. /// /// The first visible index. /// If set to true strict. protected override int GetFirstVisibleIndex(bool strict=false) { var first_visible_index = GetIndexByHeight(GetScrollValue()); if (strict) { return first_visible_index; } return Mathf.Min(first_visible_index, Mathf.Max(0, DataSource.Count - visibleItems)); } LayoutGroup defaultItemLayoutGroup; /// /// Gets the height of the item. /// /// The item height. /// Item. float CalculateItemHeight(string item) { if (defaultItemLayoutGroup==null) { defaultItemLayoutGroup = DefaultItemCopy.GetComponent(); } float height = 0f; if (defaultItemLayoutGroup!=null) { DefaultItemCopy.gameObject.SetActive(true); DefaultItemCopy.SetData(item); Utilites.UpdateLayout(defaultItemLayoutGroup); height = LayoutUtility.GetPreferredHeight(DefaultItemCopyRect); DefaultItemCopy.gameObject.SetActive(false); } return height; } /// /// Calls specified function with each component. /// /// Func. public override void ForEachComponent(Action func) { base.ForEachComponent(func); func(DefaultItemCopy); } #region ListViewPaginator support public override int GetNearestItemIndex() { return GetIndexByHeight(GetScrollValue()); } #endregion } }