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); } } }