using UnityEngine; using UnityEngine.UI; using System.Collections.Generic; using UnityEngine.Events; using UnityEngine.EventSystems; using System; using System.Linq; namespace UIWidgets { /// /// Tile view. /// public class TileView : ListViewCustom where TComponent : ListViewItem { int itemsPerRow; int itemsPerColumn; /// /// Determines whether this instance can optimize. /// /// true if this instance can optimize; otherwise, false. protected override bool CanOptimize() { var scrollRectSpecified = scrollRect!=null; var containerSpecified = Container!=null; var currentLayout = containerSpecified ? ((layout!=null) ? layout : Container.GetComponent()) : null; var validLayout = currentLayout is EasyLayout.EasyLayout; return scrollRectSpecified && validLayout; } /// /// 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); 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)); } } /// /// Gets the item position. /// /// The item position. /// Index. public override float GetItemPosition(int index) { var block_index = Mathf.FloorToInt((float)index / (float)ItemsPerBlock()); return block_index * GetItemSize() - GetItemSpacing(); } /// /// Calculates the max count of visible items. /// 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; } /// /// Gets the index of first visible item. /// /// The first visible index. /// If set to true strict. protected override int GetFirstVisibleIndex(bool strict=false) { return Mathf.Max(0, base.GetFirstVisibleIndex(strict) * ItemsPerBlock()); } /// /// Gets the index of last visible item. /// /// The last visible index. /// If set to true strict. protected override int GetLastVisibleIndex(bool strict=false) { return (base.GetLastVisibleIndex(strict) + 1) * ItemsPerBlock() - 1; } /// /// Scrolls the update. /// 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(GetComponentIndex); var new_indicies_to_change = new_visible_range.Except(current_visible_range).ToList(); var components_to_change = new Stack(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(); } } /// /// Raises the item move event. /// /// Event data. /// Item. 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; } } /// /// Gets the index of the nearest item. /// /// The nearest item index. /// Point. 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; } /// /// Count of items the per block. /// /// The per block. int ItemsPerBlock() { return IsHorizontal() ? itemsPerColumn : itemsPerRow; } /// /// Gets the blocks count. /// /// The blocks count. /// Items. int GetBlocksCount(int items) { return Mathf.CeilToInt((float)items / (float)ItemsPerBlock()); } /// /// Calculates the size of the bottom filler. /// /// The bottom filler size. protected override float CalculateBottomFillerSize() { var blocks = GetBlocksCount(bottomHiddenItems); if (blocks==0) { return 0f; } return Mathf.Max(0, blocks * GetItemSize() - LayoutBridge.GetSpacing()); } /// /// Calculates the size of the top filler. /// /// The top filler size. 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 /// /// Gets the items per block count. /// /// The items per block. public override int GetItemsPerBlock() { return ItemsPerBlock(); } /// /// Gets the index of the nearest item. /// /// The nearest item index. public override int GetNearestItemIndex() { return base.GetNearestItemIndex() * ItemsPerBlock(); } #endregion } }