using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using System.Collections.Generic;
using System;
using System.Linq;
namespace UIWidgets
{
///
/// Base class for custom ListView with dynamic items heights.
///
public class ListViewCustomHeight : ListViewCustom
where TComponent : ListViewItem
where TItem: IItemHeight
{
///
/// Calculate height automaticly without using IListViewItemHeight.Height.
///
[SerializeField]
[Tooltip("Calculate height automaticly without using IListViewItemHeight.Height.")]
bool ForceAutoHeightCalculation = true;
TComponent defaultItemCopy;
RectTransform defaultItemCopyRect;
///
/// Gets the default item copy.
///
/// The default item copy.
protected TComponent DefaultItemCopy {
get {
if (defaultItemCopy==null)
{
defaultItemCopy = Instantiate(DefaultItem) as TComponent;
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;
}
}
bool IsCanCalculateHeight;
public ListViewCustomHeight()
{
IsCanCalculateHeight = typeof(IListViewItemHeight).IsAssignableFrom(typeof(TComponent));
}
///
/// Awake this instance.
///
protected override void Awake()
{
Start();
}
///
/// Calculates the max count of visible items.
///
protected override void CalculateMaxVisibleItems()
{
SetItemsHeight(DataSource, false);
CalculateMaxVisibleItems(DataSource);
}
protected virtual void CalculateMaxVisibleItems(ObservableList data)
{
var height = scrollHeight;
maxVisibleItems = data.OrderBy(GetItemHeight).TakeWhile(x => {
height -= x.Height;
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()
{
return GetItemsHeight(topHiddenItems + visibleItems, bottomHiddenItems);
}
///
/// Calculates the size of the top filler.
///
/// The top filler size.
protected override float CalculateTopFillerSize()
{
return GetItemsHeight(0, topHiddenItems);
}
float GetItemsHeight(int start, int count)
{
if (count==0)
{
return 0f;
}
var height = DataSource.GetRange(start, count).SumFloat(GetItemHeight);
return Mathf.Max(0, height + (LayoutBridge.GetSpacing() * (count - 1)));
}
float GetItemHeight(TItem item)
{
return item.Height;
}
///
/// Gets the item position.
///
/// The item position.
/// Index.
public override float GetItemPosition(int index)
{
return Mathf.Max(0, DataSource.GetRange(0, index).SumFloat(GetItemHeight) + (LayoutBridge.GetSpacing() * (index - 1)));
}
///
/// Gets the item position bottom.
///
/// The item position bottom.
/// Index.
public override float GetItemPositionBottom(int index)
{
return GetItemPosition(index) + DataSource[index].Height - LayoutBridge.GetSpacing() + LayoutBridge.GetMargin() - GetScrollSize();
}
///
/// 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(TItem item)
{
if (item==null)
{
throw new ArgumentNullException("item", "Item is null.");
}
if (item.Height==0)
{
item.Height = 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)
{
items.ForEach(x => {
if ((x.Height==0) || forceUpdate)
{
x.Height = 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(newItems);
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 -= item.Height;
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(TItem item)
{
if (defaultItemLayoutGroup==null)
{
defaultItemLayoutGroup = DefaultItemCopy.GetComponent();
}
float height = 0f;
if (!IsCanCalculateHeight || ForceAutoHeightCalculation)
{
if (defaultItemLayoutGroup!=null)
{
DefaultItemCopy.gameObject.SetActive(true);
SetData(DefaultItemCopy, item);
Utilites.UpdateLayout(defaultItemLayoutGroup);
height = LayoutUtility.GetPreferredHeight(DefaultItemCopyRect);
DefaultItemCopy.gameObject.SetActive(false);
}
}
else
{
SetData(DefaultItemCopy, item);
height = (DefaultItemCopy as IListViewItemHeight).Height;
}
return height;
}
///
/// Adds the callback.
///
/// Item.
/// Index.
protected override void AddCallback(ListViewItem item, int index)
{
item.onResize.AddListener(SizeChanged);
base.AddCallback(item, index);
}
///
/// Removes the callback.
///
/// Item.
/// Index.
protected override void RemoveCallback(ListViewItem item, int index)
{
item.onResize.RemoveListener(SizeChanged);
base.RemoveCallback(item, index);
}
void SizeChanged(int index, Vector2 size)
{
if (DataSource[index].Height!=size.y)
{
DataSource[index].Height = size.y;
var old = maxVisibleItems;
CalculateMaxVisibleItems();
if (maxVisibleItems > old)
{
UpdateView();
}
else
{
ScrollUpdate();
}
}
}
///
/// Calls specified function with each component.
///
/// Func.
public override void ForEachComponent(Action func)
{
base.ForEachComponent(func);
func(DefaultItemCopy);
}
///
/// Gets the index of the nearest item.
///
/// The nearest item index.
/// Point.
public override int GetNearestIndex(Vector2 point)
{
if (IsSortEnabled())
{
return -1;
}
var index = GetIndexByHeight(-point.y);
if (index!=(DataSource.Count - 1))
{
var height = GetItemsHeight(0, index);
var top = -point.y - height;
var bottom = -point.y - (height + DataSource[index+1].Height + LayoutBridge.GetSpacing());
if (bottom < top)
{
index += 1;
}
}
return Mathf.Min(index, DataSource.Count - 1);
}
#region ListViewPaginator support
public override int GetNearestItemIndex()
{
return GetIndexByHeight(GetScrollValue());
}
#endregion
#if UNITY_EDITOR
bool IsItemCanCalculateHeight()
{
return IsCanCalculateHeight;
}
#endif
}
}