using System;
using System.Collections.Generic;
using FairyGUI.Utils;
using UnityEngine;
namespace FairyGUI
{
///
/// Callback function when an item is needed to update its look.
///
/// Item index.
/// Item object.
public delegate void ListItemRenderer(int index, GObject item);
///
///
///
///
///
public delegate string ListItemProvider(int index);
///
/// GList class.
///
public class GList : GComponent
{
///
/// 如果true,当item不可见时自动折叠,否则依然占位
///
public bool foldInvisibleItems = false;
///
/// List selection mode
///
///
public ListSelectionMode selectionMode;
///
/// Callback function when an item is needed to update its look.
///
public ListItemRenderer itemRenderer;
///
/// Callback funtion to return item resource url.
///
public ListItemProvider itemProvider;
///
///
///
public bool scrollItemToViewOnClick;
string _defaultItem;
ListLayoutType _layout;
int _lineCount;
int _columnCount;
int _lineGap;
int _columnGap;
AlignType _align;
VertAlignType _verticalAlign;
bool _autoResizeItem;
Controller _selectionController;
GObjectPool _pool;
int _lastSelectedIndex;
EventListener _onClickItem;
EventListener _onRightClickItem;
//Virtual List support
bool _virtual;
bool _loop;
int _numItems;
int _realNumItems;
int _firstIndex; //the top left index
int _curLineItemCount; //item count in one line
int _curLineItemCount2; //只用在页面模式,表示垂直方向的项目数
Vector2 _itemSize;
int _virtualListChanged; //1-content changed, 2-size changed
uint itemInfoVer; //用来标志item是否在本次处理中已经被重用了
int _miscFlags; //1-event locked, 2-focus events registered
class ItemInfo
{
public Vector2 size;
public GObject obj;
public uint updateFlag;
public bool selected;
}
List _virtualItems;
EventCallback1 _itemClickDelegate;
public GList()
: base()
{
_trackBounds = true;
this.opaque = true;
scrollItemToViewOnClick = true;
container = new Container();
rootContainer.AddChild(container);
rootContainer.gameObject.name = "GList";
_pool = new GObjectPool(container.cachedTransform);
_itemClickDelegate = __clickItem;
}
public override void Dispose()
{
_pool.Clear();
if (_virtualListChanged != 0)
Timers.inst.Remove(this.RefreshVirtualList);
_selectionController = null;
scrollItemToViewOnClick = false;
itemRenderer = null;
itemProvider = null;
base.Dispose();
}
///
/// Dispatched when a list item being clicked.
///
public EventListener onClickItem
{
get { return _onClickItem ?? (_onClickItem = new EventListener(this, "onClickItem")); }
}
///
/// Dispatched when a list item being clicked with right button.
///
public EventListener onRightClickItem
{
get { return _onRightClickItem ?? (_onRightClickItem = new EventListener(this, "onRightClickItem")); }
}
///
/// Resource url of the default item.
///
public string defaultItem
{
get { return _defaultItem; }
set
{
_defaultItem = UIPackage.NormalizeURL(value);
}
}
///
/// List layout type.
///
public ListLayoutType layout
{
get { return _layout; }
set
{
if (_layout != value)
{
_layout = value;
SetBoundsChangedFlag();
if (_virtual)
SetVirtualListChangedFlag(true);
}
}
}
///
///
///
public int lineCount
{
get { return _lineCount; }
set
{
if (_lineCount != value)
{
_lineCount = value;
if (_layout == ListLayoutType.FlowVertical || _layout == ListLayoutType.Pagination)
{
SetBoundsChangedFlag();
if (_virtual)
SetVirtualListChangedFlag(true);
}
}
}
}
///
///
///
public int columnCount
{
get { return _columnCount; }
set
{
if (_columnCount != value)
{
_columnCount = value;
if (_layout == ListLayoutType.FlowHorizontal || _layout == ListLayoutType.Pagination)
{
SetBoundsChangedFlag();
if (_virtual)
SetVirtualListChangedFlag(true);
}
}
}
}
///
///
///
public int lineGap
{
get { return _lineGap; }
set
{
if (_lineGap != value)
{
_lineGap = value;
SetBoundsChangedFlag();
if (_virtual)
SetVirtualListChangedFlag(true);
}
}
}
///
///
///
public int columnGap
{
get { return _columnGap; }
set
{
if (_columnGap != value)
{
_columnGap = value;
SetBoundsChangedFlag();
if (_virtual)
SetVirtualListChangedFlag(true);
}
}
}
///
///
///
public AlignType align
{
get { return _align; }
set
{
if (_align != value)
{
_align = value;
SetBoundsChangedFlag();
if (_virtual)
SetVirtualListChangedFlag(true);
}
}
}
///
///
///
public VertAlignType verticalAlign
{
get { return _verticalAlign; }
set
{
if (_verticalAlign != value)
{
_verticalAlign = value;
SetBoundsChangedFlag();
if (_virtual)
SetVirtualListChangedFlag(true);
}
}
}
///
/// If the item will resize itself to fit the list width/height.
///
public bool autoResizeItem
{
get { return _autoResizeItem; }
set
{
if (_autoResizeItem != value)
{
_autoResizeItem = value;
SetBoundsChangedFlag();
if (_virtual)
SetVirtualListChangedFlag(true);
}
}
}
///
///
///
///
public Vector2 defaultItemSize
{
get { return _itemSize; }
set
{
_itemSize = value;
if (_virtual)
{
if (_layout == ListLayoutType.SingleColumn || _layout == ListLayoutType.FlowHorizontal)
this.scrollPane.scrollStep = _itemSize.y;
else
this.scrollPane.scrollStep = _itemSize.x;
SetVirtualListChangedFlag(true);
}
}
}
///
///
///
public GObjectPool itemPool
{
get { return _pool; }
}
///
///
///
///
///
public GObject GetFromPool(string url)
{
if (string.IsNullOrEmpty(url))
url = _defaultItem;
GObject ret = _pool.GetObject(url);
if (ret != null)
ret.visible = true;
return ret;
}
void ReturnToPool(GObject obj)
{
_pool.ReturnObject(obj);
}
///
/// Add a item to list, same as GetFromPool+AddChild
///
/// Item object
public GObject AddItemFromPool()
{
GObject obj = GetFromPool(null);
return AddChild(obj);
}
///
/// Add a item to list, same as GetFromPool+AddChild
///
/// Item resource url
/// Item object
public GObject AddItemFromPool(string url)
{
GObject obj = GetFromPool(url);
return AddChild(obj);
}
///
///
///
///
///
///
override public GObject AddChildAt(GObject child, int index)
{
base.AddChildAt(child, index);
if (child is GButton)
{
GButton button = (GButton)child;
button.selected = false;
button.changeStateOnClick = false;
}
child.onClick.Add(_itemClickDelegate);
child.onRightClick.Add(_itemClickDelegate);
return child;
}
///
///
///
///
///
///
override public GObject RemoveChildAt(int index, bool dispose)
{
GObject child = base.RemoveChildAt(index, dispose);
child.onClick.Remove(_itemClickDelegate);
child.onRightClick.Remove(_itemClickDelegate);
return child;
}
///
///
///
///
public void RemoveChildToPoolAt(int index)
{
GObject child = base.RemoveChildAt(index);
ReturnToPool(child);
}
///
///
///
///
public void RemoveChildToPool(GObject child)
{
base.RemoveChild(child);
ReturnToPool(child);
}
///
///
///
public void RemoveChildrenToPool()
{
RemoveChildrenToPool(0, -1);
}
///
///
///
///
///
public void RemoveChildrenToPool(int beginIndex, int endIndex)
{
if (endIndex < 0 || endIndex >= _children.Count)
endIndex = _children.Count - 1;
for (int i = beginIndex; i <= endIndex; ++i)
RemoveChildToPoolAt(beginIndex);
}
///
///
///
public int selectedIndex
{
get
{
if (_virtual)
{
int cnt = _realNumItems;
for (int i = 0; i < cnt; i++)
{
ItemInfo ii = _virtualItems[i];
if ((ii.obj is GButton) && ((GButton)ii.obj).selected
|| ii.obj == null && ii.selected)
{
if (_loop)
return i % _numItems;
else
return i;
}
}
}
else
{
int cnt = _children.Count;
for (int i = 0; i < cnt; i++)
{
GButton obj = _children[i].asButton;
if (obj != null && obj.selected)
return i;
}
}
return -1;
}
set
{
if (value >= 0 && value < this.numItems)
{
if (selectionMode != ListSelectionMode.Single)
ClearSelection();
AddSelection(value, false);
}
else
ClearSelection();
}
}
///
///
///
public Controller selectionController
{
get { return _selectionController; }
set { _selectionController = value; }
}
///
///
///
///
public List GetSelection()
{
return GetSelection(null);
}
///
///
///
///
public List GetSelection(List result)
{
if (result == null)
result = new List();
if (_virtual)
{
int cnt = _realNumItems;
for (int i = 0; i < cnt; i++)
{
ItemInfo ii = _virtualItems[i];
if ((ii.obj is GButton) && ((GButton)ii.obj).selected
|| ii.obj == null && ii.selected)
{
int j = i;
if (_loop)
{
j = i % _numItems;
if (result.Contains(j))
continue;
}
result.Add(j);
}
}
}
else
{
int cnt = _children.Count;
for (int i = 0; i < cnt; i++)
{
GButton obj = _children[i].asButton;
if (obj != null && obj.selected)
result.Add(i);
}
}
return result;
}
///
///
///
///
///
public void AddSelection(int index, bool scrollItToView)
{
if (selectionMode == ListSelectionMode.None)
return;
CheckVirtualList();
if (selectionMode == ListSelectionMode.Single)
ClearSelection();
if (scrollItToView)
ScrollToView(index);
_lastSelectedIndex = index;
GButton obj = null;
if (_virtual)
{
ItemInfo ii = _virtualItems[index];
if (ii.obj != null)
obj = ii.obj.asButton;
ii.selected = true;
}
else
obj = GetChildAt(index).asButton;
if (obj != null && !obj.selected)
{
obj.selected = true;
UpdateSelectionController(index);
}
}
///
///
///
///
public void RemoveSelection(int index)
{
if (selectionMode == ListSelectionMode.None)
return;
GButton obj = null;
if (_virtual)
{
ItemInfo ii = _virtualItems[index];
if (ii.obj != null)
obj = ii.obj.asButton;
ii.selected = false;
}
else
obj = GetChildAt(index).asButton;
if (obj != null)
obj.selected = false;
}
///
///
///
public void ClearSelection()
{
if (_virtual)
{
int cnt = _realNumItems;
for (int i = 0; i < cnt; i++)
{
ItemInfo ii = _virtualItems[i];
if ((ii.obj is GButton))
((GButton)ii.obj).selected = false;
ii.selected = false;
}
}
else
{
int cnt = _children.Count;
for (int i = 0; i < cnt; i++)
{
GButton obj = _children[i].asButton;
if (obj != null)
obj.selected = false;
}
}
}
void ClearSelectionExcept(GObject g)
{
if (_virtual)
{
int cnt = _realNumItems;
for (int i = 0; i < cnt; i++)
{
ItemInfo ii = _virtualItems[i];
if (ii.obj != g)
{
if ((ii.obj is GButton))
((GButton)ii.obj).selected = false;
ii.selected = false;
}
}
}
else
{
int cnt = _children.Count;
for (int i = 0; i < cnt; i++)
{
GButton obj = _children[i].asButton;
if (obj != null && obj != g)
obj.selected = false;
}
}
}
///
///
///
public void SelectAll()
{
CheckVirtualList();
int last = -1;
if (_virtual)
{
int cnt = _realNumItems;
for (int i = 0; i < cnt; i++)
{
ItemInfo ii = _virtualItems[i];
if ((ii.obj is GButton) && !((GButton)ii.obj).selected)
{
((GButton)ii.obj).selected = true;
last = i;
}
ii.selected = true;
}
}
else
{
int cnt = _children.Count;
for (int i = 0; i < cnt; i++)
{
GButton obj = _children[i].asButton;
if (obj != null && !obj.selected)
{
obj.selected = true;
last = i;
}
}
}
if (last != -1)
UpdateSelectionController(last);
}
///
///
///
public void SelectNone()
{
ClearSelection();
}
///
///
///
public void SelectReverse()
{
CheckVirtualList();
int last = -1;
if (_virtual)
{
int cnt = _realNumItems;
for (int i = 0; i < cnt; i++)
{
ItemInfo ii = _virtualItems[i];
if ((ii.obj is GButton))
{
((GButton)ii.obj).selected = !((GButton)ii.obj).selected;
if (((GButton)ii.obj).selected)
last = i;
}
ii.selected = !ii.selected;
}
}
else
{
int cnt = _children.Count;
for (int i = 0; i < cnt; i++)
{
GButton obj = _children[i].asButton;
if (obj != null)
{
obj.selected = !obj.selected;
if (obj.selected)
last = i;
}
}
}
if (last != -1)
UpdateSelectionController(last);
}
///
///
///
///
public void EnableSelectionFocusEvents(bool enabled)
{
if (((_miscFlags & 2) != 0) == enabled)
return;
if (enabled)
{
_miscFlags |= 2;
this.tabStopChildren = true;
onFocusIn.Add(NotifySelection);
onFocusOut.Add(NotifySelection);
}
else
{
_miscFlags &= 0xFD;
onFocusIn.Remove(NotifySelection);
onFocusOut.Remove(NotifySelection);
}
}
void NotifySelection(EventContext context)
{
string eventType = context.type == "onFocusIn" ? "onListFocusIn" : "onListFocusOut";
int cnt = _children.Count;
for (int i = 0; i < cnt; i++)
{
GButton obj = _children[i].asButton;
if (obj != null && obj.selected)
obj.DispatchEvent(eventType);
}
}
///
///
///
public void EnableArrowKeyNavigation(bool enabled)
{
if (enabled)
{
this.tabStopChildren = true;
onKeyDown.Add(__keydown);
}
else
{
this.tabStopChildren = false;
onKeyDown.Remove(__keydown);
}
}
void __keydown(EventContext context)
{
int index = -1;
switch (context.inputEvent.keyCode)
{
case KeyCode.LeftArrow:
index = HandleArrowKey(7);
break;
case KeyCode.RightArrow:
index = HandleArrowKey(3);
break;
case KeyCode.UpArrow:
index = HandleArrowKey(1);
break;
case KeyCode.DownArrow:
index = HandleArrowKey(5);
break;
}
if (index != -1)
{
index = ItemIndexToChildIndex(index);
if (index != -1)
DispatchItemEvent(GetChildAt(index), context);
context.StopPropagation();
}
}
///
///
///
///
public int HandleArrowKey(int dir)
{
int curIndex = this.selectedIndex;
if (curIndex == -1)
return -1;
int index = curIndex;
switch (dir)
{
case 1://up
if (_layout == ListLayoutType.SingleColumn || _layout == ListLayoutType.FlowVertical)
{
index--;
}
else if (_layout == ListLayoutType.FlowHorizontal || _layout == ListLayoutType.Pagination)
{
if (_virtual)
{
index -= _curLineItemCount;
}
else
{
GObject current = _children[index];
int k = 0;
int i;
for (i = index - 1; i >= 0; i--)
{
GObject obj = _children[i];
if (obj.y != current.y)
{
current = obj;
break;
}
k++;
}
for (; i >= 0; i--)
{
GObject obj = _children[i];
if (obj.y != current.y)
{
index = i + k + 1;
break;
}
}
}
}
break;
case 3://right
if (_layout == ListLayoutType.SingleRow || _layout == ListLayoutType.FlowHorizontal || _layout == ListLayoutType.Pagination)
{
index++;
}
else if (_layout == ListLayoutType.FlowVertical)
{
if (_virtual)
{
index += _curLineItemCount;
}
else
{
GObject current = _children[index];
int k = 0;
int cnt = _children.Count;
int i;
for (i = index + 1; i < cnt; i++)
{
GObject obj = _children[i];
if (obj.x != current.x)
{
current = obj;
break;
}
k++;
}
for (; i < cnt; i++)
{
GObject obj = _children[i];
if (obj.x != current.x)
{
index = i - k - 1;
break;
}
}
}
}
break;
case 5://down
if (_layout == ListLayoutType.SingleColumn || _layout == ListLayoutType.FlowVertical)
{
index++;
}
else if (_layout == ListLayoutType.FlowHorizontal || _layout == ListLayoutType.Pagination)
{
if (_virtual)
{
index += _curLineItemCount;
}
else
{
GObject current = _children[index];
int k = 0;
int cnt = _children.Count;
int i;
for (i = index + 1; i < cnt; i++)
{
GObject obj = _children[i];
if (obj.y != current.y)
{
current = obj;
break;
}
k++;
}
for (; i < cnt; i++)
{
GObject obj = _children[i];
if (obj.y != current.y)
{
index = i - k - 1;
break;
}
}
}
}
break;
case 7://left
if (_layout == ListLayoutType.SingleRow || _layout == ListLayoutType.FlowHorizontal || _layout == ListLayoutType.Pagination)
{
index--;
}
else if (_layout == ListLayoutType.FlowVertical)
{
if (_virtual)
{
index -= _curLineItemCount;
}
else
{
GObject current = _children[index];
int k = 0;
int i;
for (i = index - 1; i >= 0; i--)
{
GObject obj = _children[i];
if (obj.x != current.x)
{
current = obj;
break;
}
k++;
}
for (; i >= 0; i--)
{
GObject obj = _children[i];
if (obj.x != current.x)
{
index = i + k + 1;
break;
}
}
}
}
break;
}
if (index != curIndex && index >= 0 && index < this.numItems)
{
ClearSelection();
AddSelection(index, true);
return index;
}
else
return -1;
}
void __clickItem(EventContext context)
{
GObject item = context.sender as GObject;
if ((item is GButton) && selectionMode != ListSelectionMode.None)
SetSelectionOnEvent(item, context.inputEvent);
if (scrollPane != null && scrollItemToViewOnClick)
scrollPane.ScrollToView(item, true);
DispatchItemEvent(item, context);
}
virtual protected void DispatchItemEvent(GObject item, EventContext context)
{
if (context.type == item.onRightClick.type)
DispatchEvent("onRightClickItem", item);
else
DispatchEvent("onClickItem", item);
}
void SetSelectionOnEvent(GObject item, InputEvent evt)
{
bool dontChangeLastIndex = false;
GButton button = (GButton)item;
int index = ChildIndexToItemIndex(GetChildIndex(item));
if (selectionMode == ListSelectionMode.Single)
{
if (!button.selected)
{
ClearSelectionExcept(button);
button.selected = true;
}
}
else
{
if (evt.shift)
{
if (!button.selected)
{
if (_lastSelectedIndex != -1)
{
int min = Math.Min(_lastSelectedIndex, index);
int max = Math.Max(_lastSelectedIndex, index);
max = Math.Min(max, this.numItems - 1);
if (_virtual)
{
for (int i = min; i <= max; i++)
{
ItemInfo ii = _virtualItems[i];
if (ii.obj is GButton)
((GButton)ii.obj).selected = true;
ii.selected = true;
}
}
else
{
for (int i = min; i <= max; i++)
{
GButton obj = GetChildAt(i).asButton;
if (obj != null && !obj.selected)
obj.selected = true;
}
}
dontChangeLastIndex = true;
}
else
{
button.selected = true;
}
}
}
else if (evt.ctrlOrCmd || selectionMode == ListSelectionMode.Multiple_SingleClick)
{
button.selected = !button.selected;
}
else
{
if (!button.selected)
{
ClearSelectionExcept(button);
button.selected = true;
}
else if (evt.button == 0)
ClearSelectionExcept(button);
}
}
if (!dontChangeLastIndex)
_lastSelectedIndex = index;
if (button.selected)
UpdateSelectionController(index);
}
///
/// Resize to list size to fit specified item count.
/// If list layout is single column or flow horizontally, the height will change to fit.
/// If list layout is single row or flow vertically, the width will change to fit.
///
public void ResizeToFit()
{
ResizeToFit(int.MaxValue, 0);
}
///
/// Resize to list size to fit specified item count.
/// If list layout is single column or flow horizontally, the height will change to fit.
/// If list layout is single row or flow vertically, the width will change to fit.
///
/// Item count
public void ResizeToFit(int itemCount)
{
ResizeToFit(itemCount, 0);
}
///
/// Resize to list size to fit specified item count.
/// If list layout is single column or flow horizontally, the height will change to fit.
/// If list layout is single row or flow vertically, the width will change to fit.
///
/// >Item count
/// If the result size if smaller than minSize, then use minSize.
public void ResizeToFit(int itemCount, int minSize)
{
EnsureBoundsCorrect();
int curCount = this.numItems;
if (itemCount > curCount)
itemCount = curCount;
if (_virtual)
{
int lineCount = Mathf.CeilToInt((float)itemCount / _curLineItemCount);
if (_layout == ListLayoutType.SingleColumn || _layout == ListLayoutType.FlowHorizontal)
this.viewHeight = lineCount * _itemSize.y + Math.Max(0, lineCount - 1) * _lineGap;
else
this.viewWidth = lineCount * _itemSize.x + Math.Max(0, lineCount - 1) * _columnGap;
}
else if (itemCount == 0)
{
if (_layout == ListLayoutType.SingleColumn || _layout == ListLayoutType.FlowHorizontal)
this.viewHeight = minSize;
else
this.viewWidth = minSize;
}
else
{
int i = itemCount - 1;
GObject obj = null;
while (i >= 0)
{
obj = this.GetChildAt(i);
if (!foldInvisibleItems || obj.visible)
break;
i--;
}
if (i < 0)
{
if (_layout == ListLayoutType.SingleColumn || _layout == ListLayoutType.FlowHorizontal)
this.viewHeight = minSize;
else
this.viewWidth = minSize;
}
else
{
float size;
if (_layout == ListLayoutType.SingleColumn || _layout == ListLayoutType.FlowHorizontal)
{
size = obj.y + obj.height;
if (size < minSize)
size = minSize;
this.viewHeight = size;
}
else
{
size = obj.x + obj.width;
if (size < minSize)
size = minSize;
this.viewWidth = size;
}
}
}
}
///
///
///
override protected void HandleSizeChanged()
{
base.HandleSizeChanged();
SetBoundsChangedFlag();
if (_virtual)
SetVirtualListChangedFlag(true);
}
override public void HandleControllerChanged(Controller c)
{
base.HandleControllerChanged(c);
if (_selectionController == c)
this.selectedIndex = c.selectedIndex;
}
void UpdateSelectionController(int index)
{
if (_selectionController != null && !_selectionController.changing
&& index < _selectionController.pageCount)
{
Controller c = _selectionController;
_selectionController = null;
c.selectedIndex = index;
_selectionController = c;
}
}
///
/// Scroll the list to make an item with certain index visible.
///
/// Item index
public void ScrollToView(int index)
{
ScrollToView(index, false);
}
///
/// Scroll the list to make an item with certain index visible.
///
/// Item index
/// True to scroll smoothly, othewise immdediately.
public void ScrollToView(int index, bool ani)
{
ScrollToView(index, ani, false);
}
///
/// Scroll the list to make an item with certain index visible.
///
/// Item index
/// True to scroll smoothly, othewise immdediately.
/// If true, scroll to make the target on the top/left; If false, scroll to make the target any position in view.
public void ScrollToView(int index, bool ani, bool setFirst)
{
if (_virtual)
{
if (_numItems == 0)
return;
CheckVirtualList();
if (index >= _virtualItems.Count)
throw new Exception("Invalid child index: " + index + ">" + _virtualItems.Count);
if (_loop)
index = Mathf.FloorToInt((float)_firstIndex / _numItems) * _numItems + index;
Rect rect;
ItemInfo ii = _virtualItems[index];
if (_layout == ListLayoutType.SingleColumn || _layout == ListLayoutType.FlowHorizontal)
{
float pos = 0;
for (int i = _curLineItemCount - 1; i < index; i += _curLineItemCount)
pos += _virtualItems[i].size.y + _lineGap;
rect = new Rect(0, pos, _itemSize.x, ii.size.y);
}
else if (_layout == ListLayoutType.SingleRow || _layout == ListLayoutType.FlowVertical)
{
float pos = 0;
for (int i = _curLineItemCount - 1; i < index; i += _curLineItemCount)
pos += _virtualItems[i].size.x + _columnGap;
rect = new Rect(pos, 0, ii.size.x, _itemSize.y);
}
else
{
int page = index / (_curLineItemCount * _curLineItemCount2);
rect = new Rect(page * viewWidth + (index % _curLineItemCount) * (ii.size.x + _columnGap),
(index / _curLineItemCount) % _curLineItemCount2 * (ii.size.y + _lineGap),
ii.size.x, ii.size.y);
}
if (this.scrollPane != null)
scrollPane.ScrollToView(rect, ani, setFirst);
else if (parent != null && parent.scrollPane != null)
parent.scrollPane.ScrollToView(this.TransformRect(rect, parent), ani, setFirst);
}
else
{
GObject obj = GetChildAt(index);
if (this.scrollPane != null)
scrollPane.ScrollToView(obj, ani, setFirst);
else if (parent != null && parent.scrollPane != null)
parent.scrollPane.ScrollToView(obj, ani, setFirst);
}
}
///
/// 获取当前点击哪个item
///
public GObject touchItem
{
get
{
//find out which item is under finger
//逐层往上知道查到点击了那个item
GObject obj = GRoot.inst.touchTarget;
GObject p = obj.parent;
while (p != null)
{
if (p == this)
return obj;
obj = p;
p = p.parent;
}
return null;
}
}
///
/// Get first child in view.
///
///
public override int GetFirstChildInView()
{
return ChildIndexToItemIndex(base.GetFirstChildInView());
}
public int ChildIndexToItemIndex(int index)
{
if (!_virtual)
return index;
if (_layout == ListLayoutType.Pagination)
{
for (int i = _firstIndex; i < _realNumItems; i++)
{
if (_virtualItems[i].obj != null)
{
index--;
if (index < 0)
return i;
}
}
return index;
}
else
{
index += _firstIndex;
if (_loop && _numItems > 0)
index = index % _numItems;
return index;
}
}
public int ItemIndexToChildIndex(int index)
{
if (!_virtual)
return index;
if (_layout == ListLayoutType.Pagination)
{
return GetChildIndex(_virtualItems[index].obj);
}
else
{
if (_loop && _numItems > 0)
{
int j = _firstIndex % _numItems;
if (index >= j)
index = index - j;
else
index = _numItems - j + index;
}
else
index -= _firstIndex;
return index;
}
}
///
/// Set the list to be virtual list.
/// 设置列表为虚拟列表模式。在虚拟列表模式下,列表不会为每一条列表数据创建一个实体对象,而是根据视口大小创建最小量的显示对象,然后通过itemRenderer指定的回调函数设置列表数据。
/// 在虚拟模式下,你不能通过AddChild、RemoveChild等方式管理列表,只能通过设置numItems设置列表数据的长度。
/// 如果要刷新列表,可以通过重新设置numItems,或者调用RefreshVirtualList完成。
/// ‘单行’或者‘单列’的列表布局可支持不等高的列表项目。
/// 除了‘页面’的列表布局,其他布局均支持使用不同资源构建列表项目,你可以在itemProvider里返回。如果不提供,默认使用defaultItem。
///
public void SetVirtual()
{
SetVirtual(false);
}
public bool isVirtual
{
get { return _virtual; }
}
///
/// Set the list to be virtual list, and has loop behavior.
///
public void SetVirtualAndLoop()
{
SetVirtual(true);
}
void SetVirtual(bool loop)
{
if (!_virtual)
{
if (this.scrollPane == null)
Debug.LogError("FairyGUI: Virtual list must be scrollable!");
if (loop)
{
if (_layout == ListLayoutType.FlowHorizontal || _layout == ListLayoutType.FlowVertical)
Debug.LogError("FairyGUI: Loop list is not supported for FlowHorizontal or FlowVertical layout!");
this.scrollPane.bouncebackEffect = false;
}
_virtual = true;
_loop = loop;
_virtualItems = new List();
RemoveChildrenToPool();
if (_itemSize.x == 0 || _itemSize.y == 0)
{
GObject obj = GetFromPool(null);
if (obj == null)
{
Debug.LogError("FairyGUI: Virtual List must have a default list item resource.");
_itemSize = new Vector2(100, 100);
}
else
{
_itemSize = obj.size;
_itemSize.x = Mathf.CeilToInt(_itemSize.x);
_itemSize.y = Mathf.CeilToInt(_itemSize.y);
ReturnToPool(obj);
}
}
if (_layout == ListLayoutType.SingleColumn || _layout == ListLayoutType.FlowHorizontal)
{
this.scrollPane.scrollStep = _itemSize.y;
if (_loop)
this.scrollPane._loop = 2;
}
else
{
this.scrollPane.scrollStep = _itemSize.x;
if (_loop)
this.scrollPane._loop = 1;
}
this.scrollPane.onScroll.AddCapture(__scrolled);
SetVirtualListChangedFlag(true);
}
}
///
/// Set the list item count.
/// If the list is not virtual, specified number of items will be created.
/// If the list is virtual, only items in view will be created.
///
public int numItems
{
get
{
if (_virtual)
return _numItems;
else
return _children.Count;
}
set
{
if (_virtual)
{
if (itemRenderer == null)
throw new Exception("FairyGUI: Set itemRenderer first!");
_numItems = value;
if (_loop)
_realNumItems = _numItems * 6;//设置6倍数量,用于循环滚动
else
_realNumItems = _numItems;
//_virtualItems的设计是只增不减的
int oldCount = _virtualItems.Count;
if (_realNumItems > oldCount)
{
for (int i = oldCount; i < _realNumItems; i++)
{
ItemInfo ii = new ItemInfo();
ii.size = _itemSize;
_virtualItems.Add(ii);
}
}
else
{
for (int i = _realNumItems; i < oldCount; i++)
_virtualItems[i].selected = false;
}
if (_virtualListChanged != 0)
Timers.inst.Remove(this.RefreshVirtualList);
//立即刷新
this.RefreshVirtualList(null);
}
else
{
int cnt = _children.Count;
if (value > cnt)
{
for (int i = cnt; i < value; i++)
{
if (itemProvider == null)
AddItemFromPool();
else
AddItemFromPool(itemProvider(i));
}
}
else
{
RemoveChildrenToPool(value, cnt);
}
if (itemRenderer != null)
{
for (int i = 0; i < value; i++)
itemRenderer(i, GetChildAt(i));
}
}
}
}
public void RefreshVirtualList()
{
if (!_virtual)
throw new Exception("FairyGUI: not virtual list");
SetVirtualListChangedFlag(false);
}
void CheckVirtualList()
{
if (_virtualListChanged != 0)
{
this.RefreshVirtualList(null);
Timers.inst.Remove(this.RefreshVirtualList);
}
}
void SetVirtualListChangedFlag(bool layoutChanged)
{
if (layoutChanged)
_virtualListChanged = 2;
else if (_virtualListChanged == 0)
_virtualListChanged = 1;
Timers.inst.CallLater(RefreshVirtualList);
}
void RefreshVirtualList(object param)
{
bool layoutChanged = _virtualListChanged == 2;
_virtualListChanged = 0;
_miscFlags |= 1;
if (layoutChanged)
{
if (_layout == ListLayoutType.SingleColumn || _layout == ListLayoutType.SingleRow)
_curLineItemCount = 1;
else if (_layout == ListLayoutType.FlowHorizontal)
{
if (_columnCount > 0)
_curLineItemCount = _columnCount;
else
{
_curLineItemCount = Mathf.FloorToInt((this.scrollPane.viewWidth + _columnGap) / (_itemSize.x + _columnGap));
if (_curLineItemCount <= 0)
_curLineItemCount = 1;
}
}
else if (_layout == ListLayoutType.FlowVertical)
{
if (_lineCount > 0)
_curLineItemCount = _lineCount;
else
{
_curLineItemCount = Mathf.FloorToInt((this.scrollPane.viewHeight + _lineGap) / (_itemSize.y + _lineGap));
if (_curLineItemCount <= 0)
_curLineItemCount = 1;
}
}
else //pagination
{
if (_columnCount > 0)
_curLineItemCount = _columnCount;
else
{
_curLineItemCount = Mathf.FloorToInt((this.scrollPane.viewWidth + _columnGap) / (_itemSize.x + _columnGap));
if (_curLineItemCount <= 0)
_curLineItemCount = 1;
}
if (_lineCount > 0)
_curLineItemCount2 = _lineCount;
else
{
_curLineItemCount2 = Mathf.FloorToInt((this.scrollPane.viewHeight + _lineGap) / (_itemSize.y + _lineGap));
if (_curLineItemCount2 <= 0)
_curLineItemCount2 = 1;
}
}
}
float ch = 0, cw = 0;
if (_realNumItems > 0)
{
int len = Mathf.CeilToInt((float)_realNumItems / _curLineItemCount) * _curLineItemCount;
int len2 = Math.Min(_curLineItemCount, _realNumItems);
if (_layout == ListLayoutType.SingleColumn || _layout == ListLayoutType.FlowHorizontal)
{
for (int i = 0; i < len; i += _curLineItemCount)
ch += _virtualItems[i].size.y + _lineGap;
if (ch > 0)
ch -= _lineGap;
if (_autoResizeItem)
cw = scrollPane.viewWidth;
else
{
for (int i = 0; i < len2; i++)
cw += _virtualItems[i].size.x + _columnGap;
if (cw > 0)
cw -= _columnGap;
}
}
else if (_layout == ListLayoutType.SingleRow || _layout == ListLayoutType.FlowVertical)
{
for (int i = 0; i < len; i += _curLineItemCount)
cw += _virtualItems[i].size.x + _columnGap;
if (cw > 0)
cw -= _columnGap;
if (_autoResizeItem)
ch = this.scrollPane.viewHeight;
else
{
for (int i = 0; i < len2; i++)
ch += _virtualItems[i].size.y + _lineGap;
if (ch > 0)
ch -= _lineGap;
}
}
else
{
int pageCount = Mathf.CeilToInt((float)len / (_curLineItemCount * _curLineItemCount2));
cw = pageCount * viewWidth;
ch = viewHeight;
}
}
HandleAlign(cw, ch);
this.scrollPane.SetContentSize(cw, ch);
_miscFlags &= 0xFE;
HandleScroll(true);
}
void __scrolled(EventContext context)
{
HandleScroll(false);
}
int GetIndexOnPos1(ref float pos, bool forceUpdate)
{
if (_realNumItems < _curLineItemCount)
{
pos = 0;
return 0;
}
if (numChildren > 0 && !forceUpdate)
{
float pos2 = this.GetChildAt(0).y;
if (pos2 + (_lineGap > 0 ? 0 : -_lineGap) > pos)
{
for (int i = _firstIndex - _curLineItemCount; i >= 0; i -= _curLineItemCount)
{
pos2 -= (_virtualItems[i].size.y + _lineGap);
if (pos2 <= pos)
{
pos = pos2;
return i;
}
}
pos = 0;
return 0;
}
else
{
float testGap = _lineGap > 0 ? _lineGap : 0;
for (int i = _firstIndex; i < _realNumItems; i += _curLineItemCount)
{
float pos3 = pos2 + _virtualItems[i].size.y;
if (pos3 + testGap > pos)
{
pos = pos2;
return i;
}
pos2 = pos3 + _lineGap;
}
pos = pos2;
return _realNumItems - _curLineItemCount;
}
}
else
{
float pos2 = 0;
float testGap = _lineGap > 0 ? _lineGap : 0;
for (int i = 0; i < _realNumItems; i += _curLineItemCount)
{
float pos3 = pos2 + _virtualItems[i].size.y;
if (pos3 + testGap > pos)
{
pos = pos2;
return i;
}
pos2 = pos3 + _lineGap;
}
pos = pos2;
return _realNumItems - _curLineItemCount;
}
}
int GetIndexOnPos2(ref float pos, bool forceUpdate)
{
if (_realNumItems < _curLineItemCount)
{
pos = 0;
return 0;
}
if (numChildren > 0 && !forceUpdate)
{
float pos2 = this.GetChildAt(0).x;
if (pos2 + (_columnGap > 0 ? 0 : -_columnGap) > pos)
{
for (int i = _firstIndex - _curLineItemCount; i >= 0; i -= _curLineItemCount)
{
pos2 -= (_virtualItems[i].size.x + _columnGap);
if (pos2 <= pos)
{
pos = pos2;
return i;
}
}
pos = 0;
return 0;
}
else
{
float testGap = _columnGap > 0 ? _columnGap : 0;
for (int i = _firstIndex; i < _realNumItems; i += _curLineItemCount)
{
float pos3 = pos2 + _virtualItems[i].size.x;
if (pos3 + testGap > pos)
{
pos = pos2;
return i;
}
pos2 = pos3 + _columnGap;
}
pos = pos2;
return _realNumItems - _curLineItemCount;
}
}
else
{
float pos2 = 0;
float testGap = _columnGap > 0 ? _columnGap : 0;
for (int i = 0; i < _realNumItems; i += _curLineItemCount)
{
float pos3 = pos2 + _virtualItems[i].size.x;
if (pos3 + testGap > pos)
{
pos = pos2;
return i;
}
pos2 = pos3 + _columnGap;
}
pos = pos2;
return _realNumItems - _curLineItemCount;
}
}
int GetIndexOnPos3(ref float pos, bool forceUpdate)
{
if (_realNumItems < _curLineItemCount)
{
pos = 0;
return 0;
}
float viewWidth = this.viewWidth;
int page = Mathf.FloorToInt(pos / viewWidth);
int startIndex = page * (_curLineItemCount * _curLineItemCount2);
float pos2 = page * viewWidth;
float testGap = _columnGap > 0 ? _columnGap : 0;
for (int i = 0; i < _curLineItemCount; i++)
{
float pos3 = pos2 + _virtualItems[startIndex + i].size.x;
if (pos3 + testGap > pos)
{
pos = pos2;
return startIndex + i;
}
pos2 = pos3 + _columnGap;
}
pos = pos2;
return startIndex + _curLineItemCount - 1;
}
void HandleScroll(bool forceUpdate)
{
if ((_miscFlags & 1) != 0)
return;
if (_layout == ListLayoutType.SingleColumn || _layout == ListLayoutType.FlowHorizontal)
{
int enterCounter = 0;
while (HandleScroll1(forceUpdate))
{
//可能会因为ITEM资源改变导致ITEM大小发生改变,所有出现最后一页填不满的情况,这时要反复尝试填满。
enterCounter++;
forceUpdate = false;
if (enterCounter > 20)
{
Debug.Log("FairyGUI: list will never be filled as the item renderer function always returns a different size.");
break;
}
}
HandleArchOrder1();
}
else if (_layout == ListLayoutType.SingleRow || _layout == ListLayoutType.FlowVertical)
{
int enterCounter = 0;
while (HandleScroll2(forceUpdate))
{
enterCounter++;
forceUpdate = false;
if (enterCounter > 20)
{
Debug.Log("FairyGUI: list will never be filled as the item renderer function always returns a different size.");
break;
}
}
HandleArchOrder2();
}
else
{
HandleScroll3(forceUpdate);
}
_boundsChanged = false;
}
bool HandleScroll1(bool forceUpdate)
{
float pos = scrollPane.scrollingPosY;
float max = pos + scrollPane.viewHeight;
bool end = max == scrollPane.contentHeight;//这个标志表示当前需要滚动到最末,无论内容变化大小
//寻找当前位置的第一条项目
int newFirstIndex = GetIndexOnPos1(ref pos, forceUpdate);
if (newFirstIndex == _firstIndex && !forceUpdate)
return false;
int oldFirstIndex = _firstIndex;
_firstIndex = newFirstIndex;
int curIndex = newFirstIndex;
bool forward = oldFirstIndex > newFirstIndex;
int childCount = this.numChildren;
int lastIndex = oldFirstIndex + childCount - 1;
int reuseIndex = forward ? lastIndex : oldFirstIndex;
float curX = 0, curY = pos;
bool needRender;
float deltaSize = 0;
float firstItemDeltaSize = 0;
string url = _defaultItem;
int partSize = (int)((scrollPane.viewWidth - _columnGap * (_curLineItemCount - 1)) / _curLineItemCount);
itemInfoVer++;
while (curIndex < _realNumItems && (end || curY < max))
{
ItemInfo ii = _virtualItems[curIndex];
if (ii.obj == null || forceUpdate)
{
if (itemProvider != null)
{
url = itemProvider(curIndex % _numItems);
if (url == null)
url = _defaultItem;
url = UIPackage.NormalizeURL(url);
}
if (ii.obj != null && ii.obj.resourceURL != url)
{
if (ii.obj is GButton)
ii.selected = ((GButton)ii.obj).selected;
RemoveChildToPool(ii.obj);
ii.obj = null;
}
}
if (ii.obj == null)
{
//搜索最适合的重用item,保证每次刷新需要新建或者重新render的item最少
if (forward)
{
for (int j = reuseIndex; j >= oldFirstIndex; j--)
{
ItemInfo ii2 = _virtualItems[j];
if (ii2.obj != null && ii2.updateFlag != itemInfoVer && ii2.obj.resourceURL == url)
{
if (ii2.obj is GButton)
ii2.selected = ((GButton)ii2.obj).selected;
ii.obj = ii2.obj;
ii2.obj = null;
if (j == reuseIndex)
reuseIndex--;
break;
}
}
}
else
{
for (int j = reuseIndex; j <= lastIndex; j++)
{
ItemInfo ii2 = _virtualItems[j];
if (ii2.obj != null && ii2.updateFlag != itemInfoVer && ii2.obj.resourceURL == url)
{
if (ii2.obj is GButton)
ii2.selected = ((GButton)ii2.obj).selected;
ii.obj = ii2.obj;
ii2.obj = null;
if (j == reuseIndex)
reuseIndex++;
break;
}
}
}
if (ii.obj != null)
{
SetChildIndex(ii.obj, forward ? curIndex - newFirstIndex : numChildren);
}
else
{
ii.obj = _pool.GetObject(url);
if (forward)
this.AddChildAt(ii.obj, curIndex - newFirstIndex);
else
this.AddChild(ii.obj);
}
if (ii.obj is GButton)
((GButton)ii.obj).selected = ii.selected;
needRender = true;
}
else
needRender = forceUpdate;
if (needRender)
{
if (_autoResizeItem && (_layout == ListLayoutType.SingleColumn || _columnCount > 0))
ii.obj.SetSize(partSize, ii.obj.height, true);
itemRenderer(curIndex % _numItems, ii.obj);
if (curIndex % _curLineItemCount == 0)
{
deltaSize += Mathf.CeilToInt(ii.obj.size.y) - ii.size.y;
if (curIndex == newFirstIndex && oldFirstIndex > newFirstIndex)
{
//当内容向下滚动时,如果新出现的项目大小发生变化,需要做一个位置补偿,才不会导致滚动跳动
firstItemDeltaSize = Mathf.CeilToInt(ii.obj.size.y) - ii.size.y;
}
}
ii.size.x = Mathf.CeilToInt(ii.obj.size.x);
ii.size.y = Mathf.CeilToInt(ii.obj.size.y);
}
ii.updateFlag = itemInfoVer;
ii.obj.SetXY(curX, curY);
if (curIndex == newFirstIndex) //要显示多一条才不会穿帮
max += ii.size.y;
curX += ii.size.x + _columnGap;
if (curIndex % _curLineItemCount == _curLineItemCount - 1)
{
curX = 0;
curY += ii.size.y + _lineGap;
}
curIndex++;
}
for (int i = 0; i < childCount; i++)
{
ItemInfo ii = _virtualItems[oldFirstIndex + i];
if (ii.updateFlag != itemInfoVer && ii.obj != null)
{
if (ii.obj is GButton)
ii.selected = ((GButton)ii.obj).selected;
RemoveChildToPool(ii.obj);
ii.obj = null;
}
}
childCount = _children.Count;
for (int i = 0; i < childCount; i++)
{
GObject obj = _virtualItems[newFirstIndex + i].obj;
if (_children[i] != obj)
SetChildIndex(obj, i);
}
if (deltaSize != 0 || firstItemDeltaSize != 0)
this.scrollPane.ChangeContentSizeOnScrolling(0, deltaSize, 0, firstItemDeltaSize);
if (curIndex > 0 && this.numChildren > 0 && this.container.y <= 0 && GetChildAt(0).y > -this.container.y)//最后一页没填满!
return true;
else
return false;
}
bool HandleScroll2(bool forceUpdate)
{
float pos = scrollPane.scrollingPosX;
float max = pos + scrollPane.viewWidth;
bool end = pos == scrollPane.contentWidth;//这个标志表示当前需要滚动到最末,无论内容变化大小
//寻找当前位置的第一条项目
int newFirstIndex = GetIndexOnPos2(ref pos, forceUpdate);
if (newFirstIndex == _firstIndex && !forceUpdate)
return false;
int oldFirstIndex = _firstIndex;
_firstIndex = newFirstIndex;
int curIndex = newFirstIndex;
bool forward = oldFirstIndex > newFirstIndex;
int childCount = this.numChildren;
int lastIndex = oldFirstIndex + childCount - 1;
int reuseIndex = forward ? lastIndex : oldFirstIndex;
float curX = pos, curY = 0;
bool needRender;
float deltaSize = 0;
float firstItemDeltaSize = 0;
string url = _defaultItem;
int partSize = (int)((scrollPane.viewHeight - _lineGap * (_curLineItemCount - 1)) / _curLineItemCount);
itemInfoVer++;
while (curIndex < _realNumItems && (end || curX < max))
{
ItemInfo ii = _virtualItems[curIndex];
if (ii.obj == null || forceUpdate)
{
if (itemProvider != null)
{
url = itemProvider(curIndex % _numItems);
if (url == null)
url = _defaultItem;
url = UIPackage.NormalizeURL(url);
}
if (ii.obj != null && ii.obj.resourceURL != url)
{
if (ii.obj is GButton)
ii.selected = ((GButton)ii.obj).selected;
RemoveChildToPool(ii.obj);
ii.obj = null;
}
}
if (ii.obj == null)
{
if (forward)
{
for (int j = reuseIndex; j >= oldFirstIndex; j--)
{
ItemInfo ii2 = _virtualItems[j];
if (ii2.obj != null && ii2.updateFlag != itemInfoVer && ii2.obj.resourceURL == url)
{
if (ii2.obj is GButton)
ii2.selected = ((GButton)ii2.obj).selected;
ii.obj = ii2.obj;
ii2.obj = null;
if (j == reuseIndex)
reuseIndex--;
break;
}
}
}
else
{
for (int j = reuseIndex; j <= lastIndex; j++)
{
ItemInfo ii2 = _virtualItems[j];
if (ii2.obj != null && ii2.updateFlag != itemInfoVer && ii2.obj.resourceURL == url)
{
if (ii2.obj is GButton)
ii2.selected = ((GButton)ii2.obj).selected;
ii.obj = ii2.obj;
ii2.obj = null;
if (j == reuseIndex)
reuseIndex++;
break;
}
}
}
if (ii.obj != null)
{
SetChildIndex(ii.obj, forward ? curIndex - newFirstIndex : numChildren);
}
else
{
ii.obj = _pool.GetObject(url);
if (forward)
this.AddChildAt(ii.obj, curIndex - newFirstIndex);
else
this.AddChild(ii.obj);
}
if (ii.obj is GButton)
((GButton)ii.obj).selected = ii.selected;
needRender = true;
}
else
needRender = forceUpdate;
if (needRender)
{
if (_autoResizeItem && (_layout == ListLayoutType.SingleRow || _lineCount > 0))
ii.obj.SetSize(ii.obj.width, partSize, true);
itemRenderer(curIndex % _numItems, ii.obj);
if (curIndex % _curLineItemCount == 0)
{
deltaSize += Mathf.CeilToInt(ii.obj.size.x) - ii.size.x;
if (curIndex == newFirstIndex && oldFirstIndex > newFirstIndex)
{
//当内容向下滚动时,如果新出现的一个项目大小发生变化,需要做一个位置补偿,才不会导致滚动跳动
firstItemDeltaSize = Mathf.CeilToInt(ii.obj.size.x) - ii.size.x;
}
}
ii.size.x = Mathf.CeilToInt(ii.obj.size.x);
ii.size.y = Mathf.CeilToInt(ii.obj.size.y);
}
ii.updateFlag = itemInfoVer;
ii.obj.SetXY(curX, curY);
if (curIndex == newFirstIndex) //要显示多一条才不会穿帮
max += ii.size.x;
curY += ii.size.y + _lineGap;
if (curIndex % _curLineItemCount == _curLineItemCount - 1)
{
curY = 0;
curX += ii.size.x + _columnGap;
}
curIndex++;
}
for (int i = 0; i < childCount; i++)
{
ItemInfo ii = _virtualItems[oldFirstIndex + i];
if (ii.updateFlag != itemInfoVer && ii.obj != null)
{
if (ii.obj is GButton)
ii.selected = ((GButton)ii.obj).selected;
RemoveChildToPool(ii.obj);
ii.obj = null;
}
}
childCount = _children.Count;
for (int i = 0; i < childCount; i++)
{
GObject obj = _virtualItems[newFirstIndex + i].obj;
if (_children[i] != obj)
SetChildIndex(obj, i);
}
if (deltaSize != 0 || firstItemDeltaSize != 0)
this.scrollPane.ChangeContentSizeOnScrolling(deltaSize, 0, firstItemDeltaSize, 0);
if (curIndex > 0 && this.numChildren > 0 && this.container.x <= 0 && GetChildAt(0).x > -this.container.x)//最后一页没填满!
return true;
else
return false;
}
void HandleScroll3(bool forceUpdate)
{
float pos = scrollPane.scrollingPosX;
//寻找当前位置的第一条项目
int newFirstIndex = GetIndexOnPos3(ref pos, forceUpdate);
if (newFirstIndex == _firstIndex && !forceUpdate)
return;
int oldFirstIndex = _firstIndex;
_firstIndex = newFirstIndex;
//分页模式不支持不等高,所以渲染满一页就好了
int reuseIndex = oldFirstIndex;
int virtualItemCount = _virtualItems.Count;
int pageSize = _curLineItemCount * _curLineItemCount2;
int startCol = newFirstIndex % _curLineItemCount;
float viewWidth = this.viewWidth;
int page = (int)(newFirstIndex / pageSize);
int startIndex = page * pageSize;
int lastIndex = startIndex + pageSize * 2; //测试两页
bool needRender;
string url = _defaultItem;
int partWidth = (int)((scrollPane.viewWidth - _columnGap * (_curLineItemCount - 1)) / _curLineItemCount);
int partHeight = (int)((scrollPane.viewHeight - _lineGap * (_curLineItemCount2 - 1)) / _curLineItemCount2);
itemInfoVer++;
//先标记这次要用到的项目
for (int i = startIndex; i < lastIndex; i++)
{
if (i >= _realNumItems)
continue;
int col = i % _curLineItemCount;
if (i - startIndex < pageSize)
{
if (col < startCol)
continue;
}
else
{
if (col > startCol)
continue;
}
ItemInfo ii = _virtualItems[i];
ii.updateFlag = itemInfoVer;
}
GObject lastObj = null;
int insertIndex = 0;
for (int i = startIndex; i < lastIndex; i++)
{
if (i >= _realNumItems)
continue;
ItemInfo ii = _virtualItems[i];
if (ii.updateFlag != itemInfoVer)
continue;
if (ii.obj == null)
{
//寻找看有没有可重用的
while (reuseIndex < virtualItemCount)
{
ItemInfo ii2 = _virtualItems[reuseIndex];
if (ii2.obj != null && ii2.updateFlag != itemInfoVer)
{
if (ii2.obj is GButton)
ii2.selected = ((GButton)ii2.obj).selected;
ii.obj = ii2.obj;
ii2.obj = null;
break;
}
reuseIndex++;
}
if (insertIndex == -1)
insertIndex = GetChildIndex(lastObj) + 1;
if (ii.obj == null)
{
if (itemProvider != null)
{
url = itemProvider(i % _numItems);
if (url == null)
url = _defaultItem;
url = UIPackage.NormalizeURL(url);
}
ii.obj = _pool.GetObject(url);
this.AddChildAt(ii.obj, insertIndex);
}
else
{
insertIndex = SetChildIndexBefore(ii.obj, insertIndex);
}
insertIndex++;
if (ii.obj is GButton)
((GButton)ii.obj).selected = ii.selected;
needRender = true;
}
else
{
needRender = forceUpdate;
insertIndex = -1;
lastObj = ii.obj;
}
if (needRender)
{
if (_autoResizeItem)
{
if (_curLineItemCount == _columnCount && _curLineItemCount2 == _lineCount)
ii.obj.SetSize(partWidth, partHeight, true);
else if (_curLineItemCount == _columnCount)
ii.obj.SetSize(partWidth, ii.obj.height, true);
else if (_curLineItemCount2 == _lineCount)
ii.obj.SetSize(ii.obj.width, partHeight, true);
}
itemRenderer(i % _numItems, ii.obj);
ii.size.x = Mathf.CeilToInt(ii.obj.size.x);
ii.size.y = Mathf.CeilToInt(ii.obj.size.y);
}
}
//排列item
float borderX = (startIndex / pageSize) * viewWidth;
float xx = borderX;
float yy = 0;
float lineHeight = 0;
for (int i = startIndex; i < lastIndex; i++)
{
if (i >= _realNumItems)
continue;
ItemInfo ii = _virtualItems[i];
if (ii.updateFlag == itemInfoVer)
ii.obj.SetXY(xx, yy);
if (ii.size.y > lineHeight)
lineHeight = ii.size.y;
if (i % _curLineItemCount == _curLineItemCount - 1)
{
xx = borderX;
yy += lineHeight + _lineGap;
lineHeight = 0;
if (i == startIndex + pageSize - 1)
{
borderX += viewWidth;
xx = borderX;
yy = 0;
}
}
else
xx += ii.size.x + _columnGap;
}
//释放未使用的
for (int i = reuseIndex; i < virtualItemCount; i++)
{
ItemInfo ii = _virtualItems[i];
if (ii.updateFlag != itemInfoVer && ii.obj != null)
{
if (ii.obj is GButton)
ii.selected = ((GButton)ii.obj).selected;
RemoveChildToPool(ii.obj);
ii.obj = null;
}
}
}
void HandleArchOrder1()
{
if (this.childrenRenderOrder == ChildrenRenderOrder.Arch)
{
float mid = this.scrollPane.posY + this.viewHeight / 2;
float minDist = int.MaxValue, dist;
int apexIndex = 0;
int cnt = this.numChildren;
for (int i = 0; i < cnt; i++)
{
GObject obj = GetChildAt(i);
if (!foldInvisibleItems || obj.visible)
{
dist = Mathf.Abs(mid - obj.y - obj.height / 2);
if (dist < minDist)
{
minDist = dist;
apexIndex = i;
}
}
}
this.apexIndex = apexIndex;
}
}
void HandleArchOrder2()
{
if (this.childrenRenderOrder == ChildrenRenderOrder.Arch)
{
float mid = this.scrollPane.posX + this.viewWidth / 2;
float minDist = int.MaxValue, dist;
int apexIndex = 0;
int cnt = this.numChildren;
for (int i = 0; i < cnt; i++)
{
GObject obj = GetChildAt(i);
if (!foldInvisibleItems || obj.visible)
{
dist = Mathf.Abs(mid - obj.x - obj.width / 2);
if (dist < minDist)
{
minDist = dist;
apexIndex = i;
}
}
}
this.apexIndex = apexIndex;
}
}
override public void GetSnappingPositionWithDir(ref float xValue, ref float yValue, float xDir, float yDir)
{
if (_virtual)
{
if (_layout == ListLayoutType.SingleColumn || _layout == ListLayoutType.FlowHorizontal)
{
float saved = yValue;
int index = GetIndexOnPos1(ref yValue, false);
if (index < _virtualItems.Count && index < _realNumItems)
{
float size = _virtualItems[index].size.y;
if (ShouldSnapToNext(yDir, saved - yValue, size))
yValue += size + _lineGap;
}
}
else if (_layout == ListLayoutType.SingleRow || _layout == ListLayoutType.FlowVertical)
{
float saved = xValue;
int index = GetIndexOnPos2(ref xValue, false);
if (index < _virtualItems.Count && index < _realNumItems)
{
float size = _virtualItems[index].size.x;
if (ShouldSnapToNext(xDir, saved - xValue, size))
xValue += size + _columnGap;
}
}
else
{
float saved = xValue;
int index = GetIndexOnPos3(ref xValue, false);
if (index < _virtualItems.Count && index < _realNumItems)
{
float size = _virtualItems[index].size.x;
if (ShouldSnapToNext(xDir, saved - xValue, size))
xValue += size + _columnGap;
}
}
}
else
base.GetSnappingPositionWithDir(ref xValue, ref yValue, xDir, yDir);
}
private void HandleAlign(float contentWidth, float contentHeight)
{
Vector2 newOffset = Vector2.zero;
if (contentHeight < viewHeight)
{
if (_verticalAlign == VertAlignType.Middle)
newOffset.y = (int)((viewHeight - contentHeight) / 2);
else if (_verticalAlign == VertAlignType.Bottom)
newOffset.y = viewHeight - contentHeight;
}
if (contentWidth < this.viewWidth)
{
if (_align == AlignType.Center)
newOffset.x = (int)((viewWidth - contentWidth) / 2);
else if (_align == AlignType.Right)
newOffset.x = viewWidth - contentWidth;
}
if (newOffset != _alignOffset)
{
_alignOffset = newOffset;
if (scrollPane != null)
scrollPane.AdjustMaskContainer();
else
container.SetXY(_margin.left + _alignOffset.x, _margin.top + _alignOffset.y);
}
}
override protected void UpdateBounds()
{
if (_virtual)
return;
int cnt = _children.Count;
int i;
int j = 0;
GObject child;
float curX = 0;
float curY = 0;
float cw, ch;
float maxWidth = 0;
float maxHeight = 0;
float viewWidth = this.viewWidth;
float viewHeight = this.viewHeight;
if (_layout == ListLayoutType.SingleColumn)
{
for (i = 0; i < cnt; i++)
{
child = GetChildAt(i);
if (foldInvisibleItems && !child.visible)
continue;
if (curY != 0)
curY += _lineGap;
child.y = curY;
if (_autoResizeItem)
child.SetSize(viewWidth, child.height, true);
curY += Mathf.CeilToInt(child.height);
if (child.width > maxWidth)
maxWidth = child.width;
}
ch = curY;
if (ch <= viewHeight && _autoResizeItem && scrollPane != null && scrollPane._displayInDemand && scrollPane.vtScrollBar != null)
{
viewWidth += scrollPane.vtScrollBar.width;
for (i = 0; i < cnt; i++)
{
child = GetChildAt(i);
if (foldInvisibleItems && !child.visible)
continue;
child.SetSize(viewWidth, child.height, true);
}
}
cw = Mathf.CeilToInt(maxWidth);
}
else if (_layout == ListLayoutType.SingleRow)
{
for (i = 0; i < cnt; i++)
{
child = GetChildAt(i);
if (foldInvisibleItems && !child.visible)
continue;
if (curX != 0)
curX += _columnGap;
child.x = curX;
if (_autoResizeItem)
child.SetSize(child.width, viewHeight, true);
curX += Mathf.CeilToInt(child.width);
if (child.height > maxHeight)
maxHeight = child.height;
}
cw = curX;
if (cw <= viewWidth && _autoResizeItem && scrollPane != null && scrollPane._displayInDemand && scrollPane.hzScrollBar != null)
{
viewHeight += scrollPane.hzScrollBar.height;
for (i = 0; i < cnt; i++)
{
child = GetChildAt(i);
if (foldInvisibleItems && !child.visible)
continue;
child.SetSize(child.width, viewHeight, true);
}
}
ch = Mathf.CeilToInt(maxHeight);
}
else if (_layout == ListLayoutType.FlowHorizontal)
{
if (_autoResizeItem && _columnCount > 0)
{
float lineSize = 0;
int lineStart = 0;
float remainSize;
float remainPercent;
for (i = 0; i < cnt; i++)
{
child = GetChildAt(i);
if (foldInvisibleItems && !child.visible)
continue;
lineSize += child.sourceWidth;
j++;
if (j == _columnCount || i == cnt - 1)
{
remainSize = viewWidth - (j - 1) * _columnGap;
remainPercent = 1;
curX = 0;
for (j = lineStart; j <= i; j++)
{
child = GetChildAt(j);
if (foldInvisibleItems && !child.visible)
continue;
child.SetXY(curX, curY);
float perc = child.sourceWidth / lineSize;
child.SetSize(Mathf.Round(perc / remainPercent * remainSize), child.height, true);
remainSize -= child.width;
remainPercent -= perc;
curX += child.width + _columnGap;
if (child.height > maxHeight)
maxHeight = child.height;
}
//new line
curY += Mathf.CeilToInt(maxHeight) + _lineGap;
maxHeight = 0;
j = 0;
lineStart = i + 1;
lineSize = 0;
}
}
ch = curY + Mathf.CeilToInt(maxHeight);
cw = viewWidth;
}
else
{
for (i = 0; i < cnt; i++)
{
child = GetChildAt(i);
if (foldInvisibleItems && !child.visible)
continue;
if (curX != 0)
curX += _columnGap;
if (_columnCount != 0 && j >= _columnCount
|| _columnCount == 0 && curX + child.width > viewWidth && maxHeight != 0)
{
//new line
curX = 0;
curY += Mathf.CeilToInt(maxHeight) + _lineGap;
maxHeight = 0;
j = 0;
}
child.SetXY(curX, curY);
curX += Mathf.CeilToInt(child.width);
if (curX > maxWidth)
maxWidth = curX;
if (child.height > maxHeight)
maxHeight = child.height;
j++;
}
ch = curY + Mathf.CeilToInt(maxHeight);
cw = Mathf.CeilToInt(maxWidth);
}
}
else if (_layout == ListLayoutType.FlowVertical)
{
if (_autoResizeItem && _lineCount > 0)
{
float lineSize = 0;
int lineStart = 0;
float remainSize;
float remainPercent;
for (i = 0; i < cnt; i++)
{
child = GetChildAt(i);
if (foldInvisibleItems && !child.visible)
continue;
lineSize += child.sourceHeight;
j++;
if (j == _lineCount || i == cnt - 1)
{
remainSize = viewHeight - (j - 1) * _lineGap;
remainPercent = 1;
curY = 0;
for (j = lineStart; j <= i; j++)
{
child = GetChildAt(j);
if (foldInvisibleItems && !child.visible)
continue;
child.SetXY(curX, curY);
float perc = child.sourceHeight / lineSize;
child.SetSize(child.width, Mathf.Round(perc / remainPercent * remainSize), true);
remainSize -= child.height;
remainPercent -= perc;
curY += child.height + _lineGap;
if (child.width > maxWidth)
maxWidth = child.width;
}
//new line
curX += Mathf.CeilToInt(maxWidth) + _columnGap;
maxWidth = 0;
j = 0;
lineStart = i + 1;
lineSize = 0;
}
}
cw = curX + Mathf.CeilToInt(maxWidth);
ch = viewHeight;
}
else
{
for (i = 0; i < cnt; i++)
{
child = GetChildAt(i);
if (foldInvisibleItems && !child.visible)
continue;
if (curY != 0)
curY += _lineGap;
if (_lineCount != 0 && j >= _lineCount
|| _lineCount == 0 && curY + child.height > viewHeight && maxWidth != 0)
{
curY = 0;
curX += Mathf.CeilToInt(maxWidth) + _columnGap;
maxWidth = 0;
j = 0;
}
child.SetXY(curX, curY);
curY += child.height;
if (curY > maxHeight)
maxHeight = curY;
if (child.width > maxWidth)
maxWidth = child.width;
j++;
}
cw = curX + Mathf.CeilToInt(maxWidth);
ch = Mathf.CeilToInt(maxHeight);
}
}
else //pagination
{
int page = 0;
int k = 0;
float eachHeight = 0;
if (_autoResizeItem && _lineCount > 0)
eachHeight = Mathf.Floor((viewHeight - (_lineCount - 1) * _lineGap) / _lineCount);
if (_autoResizeItem && _columnCount > 0)
{
float lineSize = 0;
int lineStart = 0;
float remainSize;
float remainPercent;
for (i = 0; i < cnt; i++)
{
child = GetChildAt(i);
if (foldInvisibleItems && !child.visible)
continue;
if (j == 0 && (_lineCount != 0 && k >= _lineCount
|| _lineCount == 0 && curY + (_lineCount > 0 ? eachHeight : child.height) > viewHeight))
{
//new page
page++;
curY = 0;
k = 0;
}
lineSize += child.sourceWidth;
j++;
if (j == _columnCount || i == cnt - 1)
{
remainSize = viewWidth - (j - 1) * _columnGap;
remainPercent = 1;
curX = 0;
for (j = lineStart; j <= i; j++)
{
child = GetChildAt(j);
if (foldInvisibleItems && !child.visible)
continue;
child.SetXY(page * viewWidth + curX, curY);
float perc = child.sourceWidth / lineSize;
child.SetSize(Mathf.Round(perc / remainPercent * remainSize), _lineCount > 0 ? eachHeight : child.height, true);
remainSize -= child.width;
remainPercent -= perc;
curX += child.width + _columnGap;
if (child.height > maxHeight)
maxHeight = child.height;
}
//new line
curY += Mathf.CeilToInt(maxHeight) + _lineGap;
maxHeight = 0;
j = 0;
lineStart = i + 1;
lineSize = 0;
k++;
}
}
}
else
{
for (i = 0; i < cnt; i++)
{
child = GetChildAt(i);
if (foldInvisibleItems && !child.visible)
continue;
if (curX != 0)
curX += _columnGap;
if (_autoResizeItem && _lineCount > 0)
child.SetSize(child.width, eachHeight, true);
if (_columnCount != 0 && j >= _columnCount
|| _columnCount == 0 && curX + child.width > viewWidth && maxHeight != 0)
{
curX = 0;
curY += maxHeight + _lineGap;
maxHeight = 0;
j = 0;
k++;
if (_lineCount != 0 && k >= _lineCount
|| _lineCount == 0 && curY + child.height > viewHeight && maxWidth != 0)//new page
{
page++;
curY = 0;
k = 0;
}
}
child.SetXY(page * viewWidth + curX, curY);
curX += Mathf.CeilToInt(child.width);
if (curX > maxWidth)
maxWidth = curX;
if (child.height > maxHeight)
maxHeight = child.height;
j++;
}
}
ch = page > 0 ? viewHeight : (curY + Mathf.CeilToInt(maxHeight));
cw = (page + 1) * viewWidth;
}
HandleAlign(cw, ch);
SetBounds(0, 0, cw, ch);
InvalidateBatchingState(true);
}
override public void Setup_BeforeAdd(ByteBuffer buffer, int beginPos)
{
base.Setup_BeforeAdd(buffer, beginPos);
buffer.Seek(beginPos, 5);
_layout = (ListLayoutType)buffer.ReadByte();
selectionMode = (ListSelectionMode)buffer.ReadByte();
_align = (AlignType)buffer.ReadByte();
_verticalAlign = (VertAlignType)buffer.ReadByte();
_lineGap = buffer.ReadShort();
_columnGap = buffer.ReadShort();
_lineCount = buffer.ReadShort();
_columnCount = buffer.ReadShort();
_autoResizeItem = buffer.ReadBool();
_childrenRenderOrder = (ChildrenRenderOrder)buffer.ReadByte();
_apexIndex = buffer.ReadShort();
if (buffer.ReadBool())
{
_margin.top = buffer.ReadInt();
_margin.bottom = buffer.ReadInt();
_margin.left = buffer.ReadInt();
_margin.right = buffer.ReadInt();
}
OverflowType overflow = (OverflowType)buffer.ReadByte();
if (overflow == OverflowType.Scroll)
{
int savedPos = buffer.position;
buffer.Seek(beginPos, 7);
SetupScroll(buffer);
buffer.position = savedPos;
}
else
SetupOverflow(overflow);
if (buffer.ReadBool())
{
int i1 = buffer.ReadInt();
int i2 = buffer.ReadInt();
this.clipSoftness = new Vector2(i1, i2);
}
if (buffer.version >= 2)
{
scrollItemToViewOnClick = buffer.ReadBool();
foldInvisibleItems = buffer.ReadBool();
}
buffer.Seek(beginPos, 8);
_defaultItem = buffer.ReadS();
ReadItems(buffer);
}
virtual protected void ReadItems(ByteBuffer buffer)
{
int itemCount = buffer.ReadShort();
for (int i = 0; i < itemCount; i++)
{
int nextPos = buffer.ReadUshort();
nextPos += buffer.position;
string str = buffer.ReadS();
if (str == null)
{
str = _defaultItem;
if (string.IsNullOrEmpty(str))
{
buffer.position = nextPos;
continue;
}
}
GObject obj = GetFromPool(str);
if (obj != null)
{
AddChild(obj);
SetupItem(buffer, obj);
}
buffer.position = nextPos;
}
}
protected void SetupItem(ByteBuffer buffer, GObject obj)
{
string str;
str = buffer.ReadS();
if (str != null)
obj.text = str;
str = buffer.ReadS();
if (str != null && (obj is GButton))
(obj as GButton).selectedTitle = str;
str = buffer.ReadS();
if (str != null)
obj.icon = str;
str = buffer.ReadS();
if (str != null && (obj is GButton))
(obj as GButton).selectedIcon = str;
str = buffer.ReadS();
if (str != null)
obj.name = str;
if (obj is GComponent)
{
int cnt = buffer.ReadShort();
for (int i = 0; i < cnt; i++)
{
Controller cc = ((GComponent)obj).GetController(buffer.ReadS());
str = buffer.ReadS();
if (cc != null)
cc.selectedPageId = str;
}
if (buffer.version >= 2)
{
cnt = buffer.ReadShort();
for (int i = 0; i < cnt; i++)
{
string target = buffer.ReadS();
int propertyId = buffer.ReadShort();
string value = buffer.ReadS();
GObject obj2 = ((GComponent)obj).GetChildByPath(target);
if (obj2 != null)
{
if (propertyId == 0)
obj2.text = value;
else if (propertyId == 1)
obj2.icon = value;
}
}
}
}
}
override public void Setup_AfterAdd(ByteBuffer buffer, int beginPos)
{
base.Setup_AfterAdd(buffer, beginPos);
buffer.Seek(beginPos, 6);
int i = buffer.ReadShort();
if (i != -1)
_selectionController = parent.GetControllerAt(i);
}
}
}