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.
316 lines
8.5 KiB
316 lines
8.5 KiB
using UnityEngine; |
|
using UnityEngine.UI; |
|
using System.Collections.Generic; |
|
using UnityEngine.Events; |
|
using UnityEngine.EventSystems; |
|
using System; |
|
using System.Linq; |
|
|
|
namespace UIWidgets { |
|
/// <summary> |
|
/// Tile view. |
|
/// </summary> |
|
public class TileView<TComponent,TItem> : ListViewCustom<TComponent,TItem> where TComponent : ListViewItem { |
|
int itemsPerRow; |
|
int itemsPerColumn; |
|
|
|
/// <summary> |
|
/// Determines whether this instance can optimize. |
|
/// </summary> |
|
/// <returns><c>true</c> if this instance can optimize; otherwise, <c>false</c>.</returns> |
|
protected override bool CanOptimize() |
|
{ |
|
var scrollRectSpecified = scrollRect!=null; |
|
var containerSpecified = Container!=null; |
|
var currentLayout = containerSpecified ? ((layout!=null) ? layout : Container.GetComponent<LayoutGroup>()) : null; |
|
var validLayout = currentLayout is EasyLayout.EasyLayout; |
|
|
|
return scrollRectSpecified && validLayout; |
|
} |
|
|
|
/// <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); |
|
|
|
var block_index = Mathf.FloorToInt((float)index / (float)ItemsPerBlock()); |
|
if (first_visible > block_index) |
|
{ |
|
SetScrollValue(GetItemPosition(index)); |
|
} |
|
else if (last_visible < block_index) |
|
{ |
|
SetScrollValue(GetItemPositionBottom(index)); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets the item position. |
|
/// </summary> |
|
/// <returns>The item position.</returns> |
|
/// <param name="index">Index.</param> |
|
public override float GetItemPosition(int index) |
|
{ |
|
var block_index = Mathf.FloorToInt((float)index / (float)ItemsPerBlock()); |
|
return block_index * GetItemSize() - GetItemSpacing(); |
|
} |
|
|
|
/// <summary> |
|
/// Calculates the max count of visible items. |
|
/// </summary> |
|
protected override void CalculateMaxVisibleItems() |
|
{ |
|
var spacing = LayoutBridge.GetSpacing(); |
|
if (IsHorizontal()) |
|
{ |
|
var height = scrollHeight + spacing - LayoutBridge.GetFullMargin(); |
|
|
|
itemsPerRow = Mathf.CeilToInt(scrollWidth / itemWidth) + 1; |
|
itemsPerRow = Mathf.Max(2, itemsPerRow); |
|
|
|
itemsPerColumn = Mathf.FloorToInt(height / (itemHeight + spacing)); |
|
itemsPerColumn = Mathf.Max(1, itemsPerColumn); |
|
} |
|
else |
|
{ |
|
var width = scrollWidth + spacing - LayoutBridge.GetFullMargin(); |
|
|
|
itemsPerRow = Mathf.FloorToInt(width / (itemWidth + spacing)); |
|
itemsPerRow = Mathf.Max(1, itemsPerRow); |
|
|
|
itemsPerColumn = Mathf.CeilToInt(scrollHeight / itemHeight) + 1; |
|
itemsPerColumn = Mathf.Max(2, itemsPerColumn); |
|
} |
|
|
|
maxVisibleItems = itemsPerRow * itemsPerColumn; |
|
} |
|
|
|
/// <summary> |
|
/// Gets the index of first visible item. |
|
/// </summary> |
|
/// <returns>The first visible index.</returns> |
|
/// <param name="strict">If set to <c>true</c> strict.</param> |
|
protected override int GetFirstVisibleIndex(bool strict=false) |
|
{ |
|
return Mathf.Max(0, base.GetFirstVisibleIndex(strict) * ItemsPerBlock()); |
|
} |
|
|
|
/// <summary> |
|
/// Gets the index of last visible item. |
|
/// </summary> |
|
/// <returns>The last visible index.</returns> |
|
/// <param name="strict">If set to <c>true</c> strict.</param> |
|
protected override int GetLastVisibleIndex(bool strict=false) |
|
{ |
|
return (base.GetLastVisibleIndex(strict) + 1) * ItemsPerBlock() - 1; |
|
} |
|
|
|
/// <summary> |
|
/// Scrolls the update. |
|
/// </summary> |
|
protected override void ScrollUpdate() |
|
{ |
|
var oldTopHiddenItems = topHiddenItems; |
|
|
|
topHiddenItems = GetFirstVisibleIndex(); |
|
if (topHiddenItems > (DataSource.Count - 1)) |
|
{ |
|
topHiddenItems = Mathf.Max(0, DataSource.Count - 2); |
|
} |
|
|
|
if (oldTopHiddenItems==topHiddenItems) |
|
{ |
|
return ; |
|
} |
|
|
|
if ((CanOptimize()) && (DataSource.Count > 0)) |
|
{ |
|
visibleItems = (maxVisibleItems < DataSource.Count) ? maxVisibleItems : DataSource.Count; |
|
} |
|
else |
|
{ |
|
visibleItems = DataSource.Count; |
|
} |
|
if ((topHiddenItems + visibleItems) > DataSource.Count) |
|
{ |
|
visibleItems = DataSource.Count - topHiddenItems; |
|
if (visibleItems < ItemsPerBlock()) |
|
{ |
|
visibleItems = Mathf.Min(DataSource.Count, visibleItems + ItemsPerBlock()); |
|
topHiddenItems = DataSource.Count - visibleItems; |
|
} |
|
} |
|
|
|
RemoveCallbacks(); |
|
|
|
UpdateComponentsCount(); |
|
|
|
bottomHiddenItems = Mathf.Max(0, DataSource.Count - visibleItems - topHiddenItems); |
|
|
|
var new_visible_range = Enumerable.Range(topHiddenItems, visibleItems).ToList(); |
|
var current_visible_range = components.Convert<TComponent,int>(GetComponentIndex); |
|
|
|
var new_indicies_to_change = new_visible_range.Except(current_visible_range).ToList(); |
|
var components_to_change = new Stack<TComponent>(components.Where(x => !new_visible_range.Contains(x.Index))); |
|
|
|
new_indicies_to_change.ForEach(index => { |
|
var component = components_to_change.Pop(); |
|
|
|
component.Index = index; |
|
SetData(component, DataSource[index]); |
|
Coloring(component as ListViewItem); |
|
}); |
|
|
|
components.Sort(ComponentsComparer); |
|
components.ForEach(SetComponentAsLastSibling); |
|
|
|
AddCallbacks(); |
|
|
|
if (LayoutBridge!=null) |
|
{ |
|
LayoutBridge.SetFiller(CalculateTopFillerSize(), CalculateBottomFillerSize()); |
|
LayoutBridge.UpdateLayout(); |
|
} |
|
} |
|
|
|
|
|
/// <summary> |
|
/// Raises the item move event. |
|
/// </summary> |
|
/// <param name="eventData">Event data.</param> |
|
/// <param name="item">Item.</param> |
|
protected override void OnItemMove(AxisEventData eventData, ListViewItem item) |
|
{ |
|
var block = item.Index % ItemsPerBlock(); |
|
switch (eventData.moveDir) |
|
{ |
|
case MoveDirection.Left: |
|
if (block > 0) |
|
{ |
|
SelectComponentByIndex(item.Index - 1); |
|
} |
|
break; |
|
case MoveDirection.Right: |
|
if (block < (ItemsPerBlock() - 1)) |
|
{ |
|
SelectComponentByIndex(item.Index + 1); |
|
} |
|
break; |
|
case MoveDirection.Up: |
|
var index_up = item.Index - ItemsPerBlock(); |
|
if (IsValid(index_up)) |
|
{ |
|
SelectComponentByIndex(index_up); |
|
} |
|
break; |
|
case MoveDirection.Down: |
|
var index_down = item.Index + ItemsPerBlock(); |
|
if (IsValid(index_down)) |
|
{ |
|
SelectComponentByIndex(index_down); |
|
} |
|
break; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets the index of the nearest item. |
|
/// </summary> |
|
/// <returns>The nearest item index.</returns> |
|
/// <param name="point">Point.</param> |
|
public override int GetNearestIndex(Vector2 point) |
|
{ |
|
if (IsSortEnabled()) |
|
{ |
|
return -1; |
|
} |
|
|
|
// block index |
|
var pos_block = IsHorizontal() ? point.x : -point.y; |
|
var block = Mathf.RoundToInt(pos_block / GetItemSize()); |
|
|
|
// item index in block |
|
var pos_elem = IsHorizontal() ? -point.y : point.x; |
|
var size = (IsHorizontal()) ? itemHeight + LayoutBridge.GetSpacing() : itemWidth + LayoutBridge.GetSpacing(); |
|
var k = Mathf.FloorToInt(pos_elem / size); |
|
|
|
return block * GetItemsPerBlock() + k; |
|
} |
|
|
|
/// <summary> |
|
/// Count of items the per block. |
|
/// </summary> |
|
/// <returns>The per block.</returns> |
|
int ItemsPerBlock() |
|
{ |
|
return IsHorizontal() ? itemsPerColumn : itemsPerRow; |
|
} |
|
|
|
/// <summary> |
|
/// Gets the blocks count. |
|
/// </summary> |
|
/// <returns>The blocks count.</returns> |
|
/// <param name="items">Items.</param> |
|
int GetBlocksCount(int items) |
|
{ |
|
return Mathf.CeilToInt((float)items / (float)ItemsPerBlock()); |
|
} |
|
|
|
/// <summary> |
|
/// Calculates the size of the bottom filler. |
|
/// </summary> |
|
/// <returns>The bottom filler size.</returns> |
|
protected override float CalculateBottomFillerSize() |
|
{ |
|
var blocks = GetBlocksCount(bottomHiddenItems); |
|
if (blocks==0) |
|
{ |
|
return 0f; |
|
} |
|
return Mathf.Max(0, blocks * GetItemSize() - LayoutBridge.GetSpacing()); |
|
} |
|
|
|
/// <summary> |
|
/// Calculates the size of the top filler. |
|
/// </summary> |
|
/// <returns>The top filler size.</returns> |
|
protected override float CalculateTopFillerSize() |
|
{ |
|
var blocks = GetBlocksCount(topHiddenItems); |
|
if (blocks==0) |
|
{ |
|
return 0f; |
|
} |
|
return Mathf.Max(0, GetBlocksCount(topHiddenItems) * GetItemSize() - LayoutBridge.GetSpacing()); |
|
} |
|
|
|
#region ListViewPaginator support |
|
/// <summary> |
|
/// Gets the items per block count. |
|
/// </summary> |
|
/// <returns>The items per block.</returns> |
|
public override int GetItemsPerBlock() |
|
{ |
|
return ItemsPerBlock(); |
|
} |
|
|
|
/// <summary> |
|
/// Gets the index of the nearest item. |
|
/// </summary> |
|
/// <returns>The nearest item index.</returns> |
|
public override int GetNearestItemIndex() |
|
{ |
|
return base.GetNearestItemIndex() * ItemsPerBlock(); |
|
} |
|
#endregion |
|
} |
|
} |