1474 lines
52 KiB
C#
1474 lines
52 KiB
C#
using UnityEngine;
|
||
using UnityEngine.Events;
|
||
using UnityEngine.EventSystems;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using LuaInterface;
|
||
|
||
namespace UnityEngine.UI
|
||
{
|
||
//Lua中调用的回调函数
|
||
public delegate void LuaUpdateCellCallback(LuaTable self, GameObject go, int dataIndex);
|
||
|
||
[AddComponentMenu("")]
|
||
[DisallowMultipleComponent]
|
||
[RequireComponent(typeof(RectTransform))]
|
||
public abstract class LoopScrollRect : UIBehaviour, IInitializePotentialDragHandler, IBeginDragHandler, IEndDragHandler, IDragHandler, IScrollHandler, ICanvasElement, ILayoutElement, ILayoutGroup
|
||
{
|
||
/// <summary>
|
||
/// 更新Cell中的回调函数
|
||
/// </summary>
|
||
/// <param name="go"></param>
|
||
/// <param name="dataIndex"></param>
|
||
public delegate void UpdateCellCallback(GameObject go,int dataIndex);
|
||
|
||
//==========LoopScrollRect==========
|
||
[HideInInspector]
|
||
public int totalCount; //negative means INFINITE mode
|
||
[HideInInspector]
|
||
[NonSerialized]
|
||
|
||
public object[] objectsToFill = null;
|
||
[HideInInspector]
|
||
|
||
public bool reverseDirection = false;
|
||
[HideInInspector]
|
||
|
||
public float rubberScale = 1;
|
||
[HideInInspector]
|
||
|
||
public float threshold = 100;
|
||
|
||
[HideInInspector]
|
||
|
||
public bool lowGrid = false;
|
||
|
||
private int m_nFrame = 3;
|
||
private bool m_bClampedMove = false;
|
||
|
||
protected int itemTypeStart = 0;
|
||
protected int itemTypeEnd = 0;
|
||
|
||
protected abstract float GetSize(RectTransform item);
|
||
protected abstract float GetDimension(Vector2 vector);
|
||
protected abstract Vector2 GetVector(float value);
|
||
protected int directionSign = 0;
|
||
|
||
private float m_ContentSpacing = -1;
|
||
protected GridLayoutGroup m_GridLayout = null;
|
||
protected float contentSpacing
|
||
{
|
||
get
|
||
{
|
||
if (m_ContentSpacing >= 0)
|
||
{
|
||
return m_ContentSpacing;
|
||
}
|
||
m_ContentSpacing = 0;
|
||
if (content != null)
|
||
{
|
||
HorizontalOrVerticalLayoutGroup layout1 = content.GetComponent<HorizontalOrVerticalLayoutGroup>();
|
||
if (layout1 != null)
|
||
{
|
||
m_ContentSpacing = layout1.spacing;
|
||
}
|
||
m_GridLayout = content.GetComponent<GridLayoutGroup>();
|
||
if (m_GridLayout != null)
|
||
{
|
||
m_ContentSpacing = GetDimension(m_GridLayout.spacing);
|
||
}
|
||
}
|
||
return m_ContentSpacing;
|
||
}
|
||
}
|
||
|
||
protected int mContentCount = 0;
|
||
|
||
public int ContentCount
|
||
{
|
||
get { return mContentCount; }
|
||
set { mContentCount = value; }
|
||
}
|
||
|
||
private UpdateCellCallback mCellCallback = null;
|
||
|
||
int mMovePos = 0;
|
||
bool dirty = false;
|
||
bool force = false;
|
||
|
||
public UpdateCellCallback CellCallback
|
||
{
|
||
set { mCellCallback = value; }
|
||
}
|
||
|
||
private LuaUpdateCellCallback mLuaCellCallback = null;
|
||
private LuaTable luaSelf = null;
|
||
|
||
private LuaFunction mCellCallbackLua = null;
|
||
|
||
public LuaFunction CellCallbackLua
|
||
{
|
||
set { mCellCallbackLua = value; }
|
||
}
|
||
|
||
private LuaFunction mLuaCellCallbackLua = null;
|
||
|
||
private int m_ContentConstraintCount = 0;
|
||
public int contentConstraintCount
|
||
{
|
||
get
|
||
{
|
||
if (m_ContentConstraintCount > 0)
|
||
{
|
||
return m_ContentConstraintCount;
|
||
}
|
||
m_ContentConstraintCount = 1;
|
||
if (content != null)
|
||
{
|
||
GridLayoutGroup layout2 = content.GetComponent<GridLayoutGroup>();
|
||
if (layout2 != null)
|
||
{
|
||
if (layout2.constraint == GridLayoutGroup.Constraint.Flexible)
|
||
{
|
||
DebugHelper.Log("[LoopScrollRect] Flexible not supported yet");
|
||
}
|
||
m_ContentConstraintCount = layout2.constraintCount;
|
||
}
|
||
}
|
||
return m_ContentConstraintCount;
|
||
}
|
||
}
|
||
|
||
public int ItemTypeStart
|
||
{
|
||
get { return itemTypeStart; }
|
||
}
|
||
|
||
public int ItemTypeEnd
|
||
{
|
||
get { return itemTypeEnd; }
|
||
}
|
||
|
||
protected virtual bool UpdateItems(Bounds viewBounds, Bounds contentBounds) { return false; }
|
||
//==========LoopScrollRect==========
|
||
|
||
public enum MovementType
|
||
{
|
||
Unrestricted, // Unrestricted movement -- can scroll forever
|
||
Elastic, // Restricted but flexible -- can go past the edges, but springs back in place
|
||
Clamped, // Restricted movement where it's not possible to go past the edges
|
||
}
|
||
|
||
public enum ScrollbarVisibility
|
||
{
|
||
Permanent,
|
||
AutoHide,
|
||
AutoHideAndExpandViewport,
|
||
}
|
||
|
||
[Serializable]
|
||
public class ScrollRectEvent : UnityEvent<Vector2> { }
|
||
|
||
[SerializeField]
|
||
private Transform m_Cell;
|
||
|
||
public Transform Cell { get { return m_Cell; } set { m_Cell = value; } }
|
||
|
||
|
||
[SerializeField]
|
||
private RectTransform m_Content;
|
||
|
||
public RectTransform content { get { return m_Content; } set { m_Content = value; } }
|
||
|
||
[SerializeField]
|
||
private bool m_Horizontal = true;
|
||
|
||
public bool horizontal { get { return m_Horizontal; } set { m_Horizontal = value; } }
|
||
|
||
[SerializeField]
|
||
private bool m_Vertical = true;
|
||
|
||
public bool vertical { get { return m_Vertical; } set { m_Vertical = value; } }
|
||
|
||
[SerializeField]
|
||
private MovementType m_MovementType = MovementType.Elastic;
|
||
|
||
public MovementType movementType { get { return m_MovementType; } set { m_MovementType = value; } }
|
||
|
||
[SerializeField]
|
||
private float m_Elasticity = 0.1f; // Only used for MovementType.Elastic
|
||
|
||
public float elasticity { get { return m_Elasticity; } set { m_Elasticity = value; } }
|
||
|
||
[SerializeField]
|
||
private bool m_Inertia = true;
|
||
|
||
public bool inertia { get { return m_Inertia; } set { m_Inertia = value; } }
|
||
|
||
[SerializeField]
|
||
private float m_DecelerationRate = 0.135f; // Only used when inertia is enabled
|
||
|
||
public float decelerationRate { get { return m_DecelerationRate; } set { m_DecelerationRate = value; } }
|
||
|
||
[SerializeField]
|
||
private float m_ScrollSensitivity = 1.0f;
|
||
|
||
public float scrollSensitivity { get { return m_ScrollSensitivity; } set { m_ScrollSensitivity = value; } }
|
||
|
||
[SerializeField]
|
||
private RectTransform m_Viewport;
|
||
|
||
public RectTransform viewport { get { return m_Viewport; } set { m_Viewport = value; SetDirtyCaching(); } }
|
||
|
||
[SerializeField]
|
||
private Scrollbar m_HorizontalScrollbar;
|
||
|
||
public Scrollbar horizontalScrollbar
|
||
{
|
||
get
|
||
{
|
||
return m_HorizontalScrollbar;
|
||
}
|
||
set
|
||
{
|
||
if (m_HorizontalScrollbar)
|
||
m_HorizontalScrollbar.onValueChanged.RemoveListener(SetHorizontalNormalizedPosition);
|
||
m_HorizontalScrollbar = value;
|
||
if (m_HorizontalScrollbar)
|
||
m_HorizontalScrollbar.onValueChanged.AddListener(SetHorizontalNormalizedPosition);
|
||
SetDirtyCaching();
|
||
}
|
||
}
|
||
|
||
[SerializeField]
|
||
private Scrollbar m_VerticalScrollbar;
|
||
|
||
public Scrollbar verticalScrollbar
|
||
{
|
||
get
|
||
{
|
||
return m_VerticalScrollbar;
|
||
}
|
||
set
|
||
{
|
||
if (m_VerticalScrollbar)
|
||
m_VerticalScrollbar.onValueChanged.RemoveListener(SetVerticalNormalizedPosition);
|
||
m_VerticalScrollbar = value;
|
||
if (m_VerticalScrollbar)
|
||
m_VerticalScrollbar.onValueChanged.AddListener(SetVerticalNormalizedPosition);
|
||
SetDirtyCaching();
|
||
}
|
||
}
|
||
|
||
[SerializeField]
|
||
private ScrollbarVisibility m_HorizontalScrollbarVisibility;
|
||
|
||
public ScrollbarVisibility horizontalScrollbarVisibility { get { return m_HorizontalScrollbarVisibility; } set { m_HorizontalScrollbarVisibility = value; SetDirtyCaching(); } }
|
||
|
||
[SerializeField]
|
||
private ScrollbarVisibility m_VerticalScrollbarVisibility;
|
||
|
||
public ScrollbarVisibility verticalScrollbarVisibility { get { return m_VerticalScrollbarVisibility; } set { m_VerticalScrollbarVisibility = value; SetDirtyCaching(); } }
|
||
|
||
[SerializeField]
|
||
private float m_HorizontalScrollbarSpacing;
|
||
|
||
public float horizontalScrollbarSpacing { get { return m_HorizontalScrollbarSpacing; } set { m_HorizontalScrollbarSpacing = value; SetDirty(); } }
|
||
|
||
[SerializeField]
|
||
private float m_VerticalScrollbarSpacing;
|
||
|
||
public float verticalScrollbarSpacing { get { return m_VerticalScrollbarSpacing; } set { m_VerticalScrollbarSpacing = value; SetDirty(); } }
|
||
|
||
[SerializeField]
|
||
private ScrollRectEvent m_OnValueChanged = new ScrollRectEvent();
|
||
|
||
public ScrollRectEvent onValueChanged { get { return m_OnValueChanged; } set { m_OnValueChanged = value; } }
|
||
|
||
// The offset from handle position to mouse down position
|
||
private Vector2 m_PointerStartLocalCursor = Vector2.zero;
|
||
private Vector2 m_ContentStartPosition = Vector2.zero;
|
||
|
||
private RectTransform m_ViewRect;
|
||
|
||
protected RectTransform viewRect
|
||
{
|
||
get
|
||
{
|
||
if (m_ViewRect == null)
|
||
m_ViewRect = m_Viewport;
|
||
if (m_ViewRect == null)
|
||
m_ViewRect = (RectTransform)transform;
|
||
return m_ViewRect;
|
||
}
|
||
}
|
||
|
||
private Bounds m_ContentBounds;
|
||
private Bounds m_ViewBounds;
|
||
|
||
private Vector2 m_Velocity;
|
||
|
||
public Vector2 velocity { get { return m_Velocity; } set { m_Velocity = value; } }
|
||
|
||
private bool m_Dragging;
|
||
|
||
public bool IsDragging { get { return m_Dragging; } }
|
||
|
||
private Vector2 m_PrevPosition = Vector2.zero;
|
||
private Bounds m_PrevContentBounds;
|
||
private Bounds m_PrevViewBounds;
|
||
[NonSerialized]
|
||
private bool m_HasRebuiltLayout = false;
|
||
|
||
private bool m_HSliderExpand;
|
||
private bool m_VSliderExpand;
|
||
private float m_HSliderHeight;
|
||
private float m_VSliderWidth;
|
||
|
||
[System.NonSerialized]
|
||
private RectTransform m_Rect;
|
||
private RectTransform rectTransform
|
||
{
|
||
get
|
||
{
|
||
if (m_Rect == null)
|
||
m_Rect = GetComponent<RectTransform>();
|
||
return m_Rect;
|
||
}
|
||
}
|
||
|
||
private RectTransform m_HorizontalScrollbarRect;
|
||
private RectTransform m_VerticalScrollbarRect;
|
||
|
||
private DrivenRectTransformTracker m_Tracker;
|
||
|
||
public System.Action mOnBeginDragAction = null;
|
||
public System.Action mOnDragingAction = null;
|
||
public System.Action mOnEndDragAction = null;
|
||
|
||
private LuaFunction mOnBeginDragLuaFun = null;
|
||
private LuaFunction mOnDragLuaFun = null;
|
||
private LuaFunction mOnEndDragLuaFun = null;
|
||
|
||
protected LoopScrollRect()
|
||
{
|
||
flexibleWidth = -1;
|
||
}
|
||
|
||
public void ForbidMove()
|
||
{
|
||
m_MovementType = MovementType.Clamped;
|
||
}
|
||
|
||
int preIdx = 0;
|
||
//==========LoopScrollRect==========
|
||
void UpdateScrollCell(Transform go,int idx)
|
||
{
|
||
if(mCellCallback != null)
|
||
{
|
||
mCellCallback(go.gameObject, idx);
|
||
}
|
||
|
||
if(luaSelf!=null && mLuaCellCallback!=null)
|
||
{
|
||
preIdx = idx;
|
||
mLuaCellCallback.DynamicInvoke(luaSelf, go.gameObject,idx);
|
||
}
|
||
}
|
||
|
||
public void ClearCells()
|
||
{
|
||
if (Application.isPlaying)
|
||
{
|
||
/*Vector2 sizeDelta = m_Content.sizeDelta;
|
||
sizeDelta.x = 0;
|
||
sizeDelta.y = 0;
|
||
m_Content.sizeDelta = sizeDelta;*/
|
||
itemTypeStart = 0;
|
||
itemTypeEnd = 0;
|
||
totalCount = 0;
|
||
objectsToFill = null;
|
||
for (int i = content.childCount - 1; i >= 0; i--)
|
||
{
|
||
GarbageGo(content.GetChild(i).gameObject);
|
||
}
|
||
//m_Content.anchoredPosition3D = Vector3.zero;
|
||
}
|
||
}
|
||
|
||
public void ClearAnchoredPostion()
|
||
{
|
||
if (null == m_Content)
|
||
return;
|
||
m_Content.anchoredPosition3D = Vector3.zero;
|
||
}
|
||
|
||
public void ResetClampOffset()
|
||
{
|
||
m_nFrame = 3;
|
||
m_bClampedMove = true;
|
||
}
|
||
|
||
public void Reset()
|
||
{
|
||
itemTypeStart = 0;
|
||
itemTypeEnd = 0;
|
||
totalCount = 0;
|
||
objectsToFill = null;
|
||
}
|
||
|
||
public void DestroyCells()
|
||
{
|
||
DestroyCells(false);
|
||
}
|
||
|
||
public void DestroyCells(bool immediate)
|
||
{
|
||
itemTypeStart = 0;
|
||
itemTypeEnd = 0;
|
||
totalCount = 0;
|
||
|
||
for (int i = content.childCount-1; i>=0;i--)
|
||
{
|
||
//这里改成DestroyImmediate确保childCount立即更新
|
||
if(immediate)
|
||
{
|
||
content.GetChild(i).gameObject.DestroyImmediate();
|
||
}
|
||
else
|
||
{
|
||
GameObject.Destroy(content.GetChild(i).gameObject);
|
||
}
|
||
//
|
||
}
|
||
|
||
while(mAvailableObj.Count > 0)
|
||
{
|
||
if(immediate)
|
||
{
|
||
var go = mAvailableObj.Pop();
|
||
if (go != null)
|
||
go.DestroyImmediate();
|
||
}
|
||
else
|
||
{
|
||
GameObject.Destroy(mAvailableObj.Pop());
|
||
}
|
||
}
|
||
}
|
||
|
||
public void ResetItemTypeStart()
|
||
{
|
||
itemTypeStart = 0;
|
||
}
|
||
|
||
public void RefreshCells()
|
||
{
|
||
if (Application.isPlaying && this.isActiveAndEnabled)
|
||
{
|
||
itemTypeEnd = itemTypeStart;
|
||
|
||
// recycle items if we can
|
||
for (int i = 0; i < content.childCount; i++)
|
||
{
|
||
if (itemTypeEnd < totalCount)
|
||
{
|
||
UpdateScrollCell(content.GetChild(i), itemTypeEnd);
|
||
itemTypeEnd++;
|
||
}
|
||
else
|
||
{
|
||
GarbageGo(content.GetChild(i).gameObject);
|
||
i--;
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
public void RefreshCellsData()
|
||
{
|
||
if (Application.isPlaying && this.isActiveAndEnabled)
|
||
{
|
||
for (int idx = itemTypeStart; idx < totalCount; idx++)
|
||
{
|
||
int childIdx = idx - itemTypeStart;
|
||
if (childIdx < content.childCount)
|
||
{
|
||
UpdateScrollCell(content.GetChild(childIdx), idx);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
public void MoveTo(int index)
|
||
{
|
||
if (index < 0 || index >= totalCount)
|
||
return;
|
||
|
||
mMovePos = index;
|
||
dirty = true;
|
||
force = false;
|
||
}
|
||
|
||
public void ForceMoveTo(int index)
|
||
{
|
||
if (index < 0 || index >= totalCount)
|
||
return;
|
||
|
||
mMovePos = index;
|
||
dirty = true;
|
||
force = true;
|
||
}
|
||
|
||
public void SetItemStartIdx(int index)
|
||
{
|
||
itemTypeStart = index;
|
||
itemTypeEnd = index;
|
||
}
|
||
|
||
public void SetUpdateCellCallback(LuaTable self,LuaUpdateCellCallback cb)
|
||
{
|
||
luaSelf = self;
|
||
mLuaCellCallback = cb;
|
||
}
|
||
|
||
public void SetDragLuaCallback(LuaFunction cb)
|
||
{
|
||
mOnDragLuaFun = cb;
|
||
}
|
||
|
||
public void SetOnDragLuaCallback(LuaFunction cb)
|
||
{
|
||
mOnBeginDragLuaFun = cb;
|
||
}
|
||
|
||
public void SetOnEndDragLuaCallback(LuaFunction cb)
|
||
{
|
||
mOnEndDragLuaFun = cb;
|
||
}
|
||
|
||
protected float NewItemAtStart()
|
||
{
|
||
if (totalCount >= 0 && itemTypeStart - contentConstraintCount < 0)
|
||
{
|
||
return 0;
|
||
}
|
||
float size = 0;
|
||
for (int i = 0; i < contentConstraintCount; i++)
|
||
{
|
||
itemTypeStart--;
|
||
RectTransform newItem = InstantiateNextItem(itemTypeStart);
|
||
newItem.SetAsFirstSibling();
|
||
size = Mathf.Max(GetSize(newItem), size);
|
||
}
|
||
|
||
if (!reverseDirection)
|
||
{
|
||
Vector2 offset = GetVector(size);
|
||
content.anchoredPosition += offset;
|
||
m_PrevPosition += offset;
|
||
m_ContentStartPosition += offset;
|
||
}
|
||
return size;
|
||
}
|
||
|
||
protected float DeleteItemAtStart()
|
||
{
|
||
if ((totalCount >= 0 && itemTypeEnd >= totalCount - 1) || content.childCount == 0)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
float size = 0;
|
||
for (int i = 0; i < contentConstraintCount; i++)
|
||
{
|
||
RectTransform oldItem = content.GetChild(0) as RectTransform;
|
||
size = Mathf.Max(GetSize(oldItem), size);
|
||
GarbageGo(oldItem.gameObject);
|
||
|
||
itemTypeStart++;
|
||
|
||
if (content.childCount == 0)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!reverseDirection)
|
||
{
|
||
Vector2 offset = GetVector(size);
|
||
content.anchoredPosition -= offset;
|
||
m_PrevPosition -= offset;
|
||
m_ContentStartPosition -= offset;
|
||
}
|
||
return size;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 在尾部追加一个元素
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
protected float NewItemAtEnd()
|
||
{
|
||
if (totalCount >= 0 && itemTypeEnd >= totalCount)
|
||
{
|
||
return 0;
|
||
}
|
||
float size = 0;
|
||
// issue 4: fill lines to end first
|
||
int count = contentConstraintCount - (content.childCount % contentConstraintCount);
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
RectTransform newItem = InstantiateNextItem(itemTypeEnd);
|
||
size = Mathf.Max(GetSize(newItem), size);
|
||
itemTypeEnd++;
|
||
if (totalCount >= 0 && itemTypeEnd >= totalCount)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (reverseDirection)
|
||
{
|
||
Vector2 offset = GetVector(size);
|
||
content.anchoredPosition -= offset;
|
||
m_PrevPosition -= offset;
|
||
m_ContentStartPosition -= offset;
|
||
}
|
||
return size;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除列表后面的元素
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
protected float DeleteItemAtEnd()
|
||
{
|
||
if ((totalCount >= 0 && itemTypeStart < contentConstraintCount) || content.childCount == 0 || itemTypeEnd == 0)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
float size = 0;
|
||
for (int i = 0; i < contentConstraintCount; i++)
|
||
{
|
||
|
||
|
||
RectTransform oldItem = content.GetChild(content.childCount - 1) as RectTransform;
|
||
size = Mathf.Max(GetSize(oldItem), size);
|
||
GarbageGo(oldItem.gameObject);
|
||
itemTypeEnd--;
|
||
if (itemTypeEnd % contentConstraintCount == 0 || content.childCount == 0)
|
||
{
|
||
break; //just delete the whole row
|
||
}
|
||
}
|
||
|
||
if (reverseDirection)
|
||
{
|
||
Vector2 offset = GetVector(size);
|
||
content.anchoredPosition += offset;
|
||
m_PrevPosition += offset;
|
||
m_ContentStartPosition += offset;
|
||
}
|
||
return size;
|
||
}
|
||
|
||
private RectTransform InstantiateNextItem(int itemIdx)
|
||
{
|
||
GameObject go = CreateGO();
|
||
if (go == null) return null;
|
||
|
||
RectTransform nextItem = go.GetComponent<RectTransform>();
|
||
nextItem.transform.SetParent(content, false);
|
||
nextItem.gameObject.SetActive(true);
|
||
UpdateScrollCell(nextItem, itemIdx);
|
||
return nextItem;
|
||
}
|
||
|
||
Stack<GameObject> mAvailableObj = new Stack<GameObject>();
|
||
GameObject CreateGO()
|
||
{
|
||
if (m_Cell == null) return null;
|
||
|
||
GameObject go = null;
|
||
if(mAvailableObj.Count > 0)
|
||
{
|
||
go = mAvailableObj.Pop();
|
||
}else
|
||
{
|
||
go = GameObject.Instantiate(m_Cell.gameObject);
|
||
go.transform.SetParent(content, false);
|
||
go.transform.localScale = m_Cell.transform.localScale;
|
||
}
|
||
return go;
|
||
}
|
||
|
||
void GarbageGo(GameObject go)
|
||
{
|
||
go.SetActive(false);
|
||
go.transform.SetParent(this.transform, false);
|
||
mAvailableObj.Push(go);
|
||
}
|
||
|
||
//==========LoopScrollRect==========
|
||
|
||
public virtual void Rebuild(CanvasUpdate executing)
|
||
{
|
||
if (executing == CanvasUpdate.Prelayout)
|
||
{
|
||
UpdateCachedData();
|
||
}
|
||
|
||
if (executing == CanvasUpdate.PostLayout)
|
||
{
|
||
UpdateBounds(false);
|
||
UpdateScrollbars(Vector2.zero);
|
||
UpdatePrevData();
|
||
|
||
m_HasRebuiltLayout = true;
|
||
}
|
||
}
|
||
|
||
|
||
public virtual void LayoutComplete()
|
||
{ }
|
||
|
||
|
||
public virtual void GraphicUpdateComplete()
|
||
{ }
|
||
|
||
void UpdateCachedData()
|
||
{
|
||
Transform transform = this.transform;
|
||
m_HorizontalScrollbarRect = m_HorizontalScrollbar == null ? null : m_HorizontalScrollbar.transform as RectTransform;
|
||
m_VerticalScrollbarRect = m_VerticalScrollbar == null ? null : m_VerticalScrollbar.transform as RectTransform;
|
||
|
||
// These are true if either the elements are children, or they don't exist at all.
|
||
bool viewIsChild = (viewRect.parent == transform);
|
||
bool hScrollbarIsChild = (!m_HorizontalScrollbarRect || m_HorizontalScrollbarRect.parent == transform);
|
||
bool vScrollbarIsChild = (!m_VerticalScrollbarRect || m_VerticalScrollbarRect.parent == transform);
|
||
bool allAreChildren = (viewIsChild && hScrollbarIsChild && vScrollbarIsChild);
|
||
|
||
m_HSliderExpand = allAreChildren && m_HorizontalScrollbarRect && horizontalScrollbarVisibility == ScrollbarVisibility.AutoHideAndExpandViewport;
|
||
m_VSliderExpand = allAreChildren && m_VerticalScrollbarRect && verticalScrollbarVisibility == ScrollbarVisibility.AutoHideAndExpandViewport;
|
||
m_HSliderHeight = (m_HorizontalScrollbarRect == null ? 0 : m_HorizontalScrollbarRect.rect.height);
|
||
m_VSliderWidth = (m_VerticalScrollbarRect == null ? 0 : m_VerticalScrollbarRect.rect.width);
|
||
}
|
||
|
||
protected override void OnEnable()
|
||
{
|
||
base.OnEnable();
|
||
|
||
if (m_HorizontalScrollbar)
|
||
m_HorizontalScrollbar.onValueChanged.AddListener(SetHorizontalNormalizedPosition);
|
||
if (m_VerticalScrollbar)
|
||
m_VerticalScrollbar.onValueChanged.AddListener(SetVerticalNormalizedPosition);
|
||
|
||
CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
|
||
}
|
||
|
||
protected override void OnDisable()
|
||
{
|
||
CanvasUpdateRegistry.UnRegisterCanvasElementForRebuild(this);
|
||
|
||
if (m_HorizontalScrollbar)
|
||
m_HorizontalScrollbar.onValueChanged.RemoveListener(SetHorizontalNormalizedPosition);
|
||
if (m_VerticalScrollbar)
|
||
m_VerticalScrollbar.onValueChanged.RemoveListener(SetVerticalNormalizedPosition);
|
||
|
||
m_HasRebuiltLayout = false;
|
||
m_Tracker.Clear();
|
||
m_Velocity = Vector2.zero;
|
||
LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
|
||
base.OnDisable();
|
||
}
|
||
|
||
|
||
public override bool IsActive()
|
||
{
|
||
return base.IsActive() && m_Content != null;
|
||
}
|
||
|
||
private void EnsureLayoutHasRebuilt()
|
||
{
|
||
if (!m_HasRebuiltLayout && !CanvasUpdateRegistry.IsRebuildingLayout())
|
||
Canvas.ForceUpdateCanvases();
|
||
}
|
||
|
||
|
||
public virtual void StopMovement()
|
||
{
|
||
m_Velocity = Vector2.zero;
|
||
}
|
||
|
||
public void SetScrollToPosition(Vector2 pos)
|
||
{
|
||
DebugHelper.LogError(pos);
|
||
SetContentAnchoredPosition(pos);
|
||
}
|
||
|
||
public virtual void OnScroll(PointerEventData data)
|
||
{
|
||
if (!IsActive())
|
||
return;
|
||
|
||
EnsureLayoutHasRebuilt();
|
||
UpdateBounds();
|
||
|
||
Vector2 delta = data.scrollDelta;
|
||
// Down is positive for scroll events, while in UI system up is positive.
|
||
delta.y *= -1;
|
||
if (vertical && !horizontal)
|
||
{
|
||
if (Mathf.Abs(delta.x) > Mathf.Abs(delta.y))
|
||
delta.y = delta.x;
|
||
delta.x = 0;
|
||
}
|
||
if (horizontal && !vertical)
|
||
{
|
||
if (Mathf.Abs(delta.y) > Mathf.Abs(delta.x))
|
||
delta.x = delta.y;
|
||
delta.y = 0;
|
||
}
|
||
|
||
Vector2 position = m_Content.anchoredPosition;
|
||
position += delta * m_ScrollSensitivity;
|
||
if (m_MovementType == MovementType.Clamped)
|
||
position += CalculateOffset(position - m_Content.anchoredPosition);
|
||
|
||
SetContentAnchoredPosition(position);
|
||
UpdateBounds();
|
||
}
|
||
|
||
|
||
public virtual void OnInitializePotentialDrag(PointerEventData eventData)
|
||
{
|
||
if (eventData.button != PointerEventData.InputButton.Left)
|
||
return;
|
||
|
||
m_Velocity = Vector2.zero;
|
||
}
|
||
|
||
|
||
public virtual void OnBeginDrag(PointerEventData eventData)
|
||
{
|
||
if (eventData.button != PointerEventData.InputButton.Left)
|
||
return;
|
||
|
||
if (!IsActive())
|
||
return;
|
||
|
||
UpdateBounds();
|
||
|
||
m_PointerStartLocalCursor = Vector2.zero;
|
||
RectTransformUtility.ScreenPointToLocalPointInRectangle(viewRect, eventData.position, eventData.pressEventCamera, out m_PointerStartLocalCursor);
|
||
m_ContentStartPosition = m_Content.anchoredPosition;
|
||
m_Dragging = true;
|
||
|
||
if (mOnBeginDragAction != null)
|
||
mOnBeginDragAction();
|
||
|
||
if(mOnBeginDragLuaFun != null && luaSelf != null)
|
||
mOnBeginDragLuaFun.Call(luaSelf);
|
||
|
||
}
|
||
|
||
|
||
public virtual void OnEndDrag(PointerEventData eventData)
|
||
{
|
||
if (eventData.button != PointerEventData.InputButton.Left)
|
||
return;
|
||
|
||
m_Dragging = false;
|
||
|
||
if (mOnEndDragAction != null)
|
||
mOnEndDragAction();
|
||
|
||
if (mOnEndDragLuaFun != null && luaSelf != null)
|
||
mOnEndDragLuaFun.Call(luaSelf);
|
||
}
|
||
|
||
|
||
public virtual void OnDrag(PointerEventData eventData)
|
||
{
|
||
if (eventData.button != PointerEventData.InputButton.Left)
|
||
return;
|
||
|
||
if (!IsActive())
|
||
return;
|
||
|
||
Vector2 localCursor;
|
||
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(viewRect, eventData.position, eventData.pressEventCamera, out localCursor))
|
||
return;
|
||
|
||
UpdateBounds();
|
||
var pointerDelta = localCursor - m_PointerStartLocalCursor;
|
||
|
||
Vector2 position = m_ContentStartPosition + pointerDelta;
|
||
// Offset to get content into place in the view.
|
||
Vector2 offset = CalculateOffset(position - m_Content.anchoredPosition);
|
||
position += offset;
|
||
if (m_MovementType == MovementType.Elastic)
|
||
{
|
||
//==========LoopScrollRect==========
|
||
if (offset.x != 0)
|
||
position.x = position.x - RubberDelta(offset.x, m_ViewBounds.size.x) * rubberScale;
|
||
if (offset.y != 0)
|
||
position.y = position.y - RubberDelta(offset.y, m_ViewBounds.size.y) * rubberScale;
|
||
//==========LoopScrollRect==========
|
||
}
|
||
|
||
SetContentAnchoredPosition(position);
|
||
|
||
if (mOnDragingAction != null)
|
||
mOnDragingAction();
|
||
|
||
if (mOnDragLuaFun != null && luaSelf != null)
|
||
mOnDragLuaFun.Call(luaSelf);
|
||
}
|
||
|
||
protected virtual void SetContentAnchoredPosition(Vector2 position)
|
||
{
|
||
if (!m_Horizontal)
|
||
position.x = m_Content.anchoredPosition.x;
|
||
if (!m_Vertical)
|
||
position.y = m_Content.anchoredPosition.y;
|
||
|
||
if (position != m_Content.anchoredPosition)
|
||
{
|
||
m_Content.anchoredPosition = position;
|
||
UpdateBounds();
|
||
}
|
||
}
|
||
|
||
protected virtual void LateUpdate()
|
||
{
|
||
if (!m_Content)
|
||
return;
|
||
|
||
EnsureLayoutHasRebuilt();
|
||
UpdateScrollbarVisibility();
|
||
UpdateBounds();
|
||
float deltaTime = Time.unscaledDeltaTime;
|
||
Vector2 offset = CalculateOffset(Vector2.zero);
|
||
if (!m_Dragging && (offset != Vector2.zero || m_Velocity != Vector2.zero))
|
||
{
|
||
Vector2 position = m_Content.anchoredPosition;
|
||
for (int axis = 0; axis < 2; axis++)
|
||
{
|
||
// Apply spring physics if movement is elastic and content has an offset from the view.
|
||
if (m_MovementType == MovementType.Elastic && offset[axis] != 0)
|
||
{
|
||
float speed = m_Velocity[axis];
|
||
position[axis] = Mathf.SmoothDamp(m_Content.anchoredPosition[axis], m_Content.anchoredPosition[axis] + offset[axis], ref speed, m_Elasticity, Mathf.Infinity, deltaTime);
|
||
m_Velocity[axis] = speed;
|
||
}
|
||
// Else move content according to velocity with deceleration applied.
|
||
else if (m_Inertia)
|
||
{
|
||
m_Velocity[axis] *= Mathf.Pow(m_DecelerationRate, deltaTime);
|
||
if (Mathf.Abs(m_Velocity[axis]) < 1)
|
||
m_Velocity[axis] = 0;
|
||
position[axis] += m_Velocity[axis] * deltaTime;
|
||
}
|
||
// If we have neither elaticity or friction, there shouldn't be any velocity.
|
||
else
|
||
{
|
||
m_Velocity[axis] = 0;
|
||
}
|
||
}
|
||
|
||
if (m_Velocity != Vector2.zero)
|
||
{
|
||
if (m_MovementType == MovementType.Clamped)
|
||
{
|
||
offset = CalculateOffset(position - m_Content.anchoredPosition);
|
||
position += offset;
|
||
}
|
||
|
||
SetContentAnchoredPosition(position);
|
||
}
|
||
}
|
||
|
||
//Clamped 模式 三帧后强制对齐(确保加载后)在iphonex适配下启用
|
||
if(m_bClampedMove)
|
||
{
|
||
if (m_nFrame != 0)
|
||
m_nFrame -= 1;
|
||
if (m_MovementType == MovementType.Clamped && offset.y != 0.0f && m_nFrame == 0)
|
||
{
|
||
Vector2 v2Postion = m_Content.anchoredPosition + offset;
|
||
SetContentAnchoredPosition(v2Postion);
|
||
m_bClampedMove = false;
|
||
}
|
||
}
|
||
|
||
if (m_Dragging && m_Inertia)
|
||
{
|
||
Vector3 newVelocity = (m_Content.anchoredPosition - m_PrevPosition) / deltaTime;
|
||
m_Velocity = Vector3.Lerp(m_Velocity, newVelocity, deltaTime * 10);
|
||
}
|
||
|
||
if (m_ViewBounds != m_PrevViewBounds || m_ContentBounds != m_PrevContentBounds || m_Content.anchoredPosition != m_PrevPosition)
|
||
{
|
||
UpdateScrollbars(offset);
|
||
m_OnValueChanged.Invoke(normalizedPosition);
|
||
UpdatePrevData();
|
||
}
|
||
|
||
if (dirty && (mMovePos > 0 || force) && content.childCount >= mContentCount)
|
||
{
|
||
if (mMovePos == totalCount - 1)
|
||
{
|
||
itemTypeStart = totalCount - content.childCount + 1;
|
||
}
|
||
else if((mMovePos + content.childCount) >= totalCount -1)
|
||
{
|
||
itemTypeStart = totalCount - mContentCount;
|
||
}
|
||
else
|
||
itemTypeStart = mMovePos;
|
||
|
||
RefreshCells();
|
||
dirty = false;
|
||
mMovePos = 0;
|
||
force = false;
|
||
}
|
||
}
|
||
|
||
private void UpdatePrevData()
|
||
{
|
||
if (m_Content == null)
|
||
m_PrevPosition = Vector2.zero;
|
||
else
|
||
m_PrevPosition = m_Content.anchoredPosition;
|
||
m_PrevViewBounds = m_ViewBounds;
|
||
m_PrevContentBounds = m_ContentBounds;
|
||
}
|
||
|
||
private void UpdateScrollbars(Vector2 offset)
|
||
{
|
||
if (m_HorizontalScrollbar)
|
||
{
|
||
//==========LoopScrollRect==========
|
||
if (m_ContentBounds.size.x > 0 && totalCount > 0)
|
||
{
|
||
m_HorizontalScrollbar.size = Mathf.Clamp01((m_ViewBounds.size.x - Mathf.Abs(offset.x)) / m_ContentBounds.size.x * (itemTypeEnd - itemTypeStart) / totalCount);
|
||
}
|
||
//==========LoopScrollRect==========
|
||
else
|
||
m_HorizontalScrollbar.size = 1;
|
||
|
||
m_HorizontalScrollbar.value = horizontalNormalizedPosition;
|
||
}
|
||
|
||
if (m_VerticalScrollbar)
|
||
{
|
||
//==========LoopScrollRect==========
|
||
if (m_ContentBounds.size.y > 0 && totalCount > 0)
|
||
{
|
||
m_VerticalScrollbar.size = Mathf.Clamp01((m_ViewBounds.size.y - Mathf.Abs(offset.y)) / m_ContentBounds.size.y * (itemTypeEnd - itemTypeStart) / totalCount);
|
||
}
|
||
//==========LoopScrollRect==========
|
||
else
|
||
m_VerticalScrollbar.size = 1;
|
||
|
||
m_VerticalScrollbar.value = verticalNormalizedPosition;
|
||
}
|
||
}
|
||
|
||
public Vector2 normalizedPosition
|
||
{
|
||
get
|
||
{
|
||
return new Vector2(horizontalNormalizedPosition, verticalNormalizedPosition);
|
||
}
|
||
set
|
||
{
|
||
SetNormalizedPosition(value.x, 0);
|
||
SetNormalizedPosition(value.y, 1);
|
||
}
|
||
}
|
||
|
||
public float horizontalNormalizedPosition
|
||
{
|
||
get
|
||
{
|
||
UpdateBounds(false);
|
||
//==========LoopScrollRect==========
|
||
if(totalCount > 0 && itemTypeEnd > itemTypeStart)
|
||
{
|
||
//TODO: space
|
||
float elementSize = m_ContentBounds.size.x / (itemTypeEnd - itemTypeStart);
|
||
float totalSize = elementSize * totalCount;
|
||
float offset = m_ContentBounds.min.x - elementSize * itemTypeStart;
|
||
|
||
if (totalSize <= m_ViewBounds.size.x)
|
||
return (m_ViewBounds.min.x > offset) ? 1 : 0;
|
||
return (m_ViewBounds.min.x - offset) / (totalSize - m_ViewBounds.size.x);
|
||
}
|
||
else
|
||
return 0.5f;
|
||
//==========LoopScrollRect==========
|
||
}
|
||
set
|
||
{
|
||
SetNormalizedPosition(value, 0);
|
||
}
|
||
}
|
||
|
||
public float verticalNormalizedPosition
|
||
{
|
||
get
|
||
{
|
||
UpdateBounds(false);
|
||
//==========LoopScrollRect==========
|
||
if(totalCount > 0 && itemTypeEnd > itemTypeStart)
|
||
{
|
||
//TODO: space
|
||
float elementSize = m_ContentBounds.size.y / (itemTypeEnd - itemTypeStart);
|
||
float totalSize = elementSize * totalCount;
|
||
float offset = m_ContentBounds.max.y + elementSize * itemTypeStart;
|
||
|
||
if (totalSize <= m_ViewBounds.size.y)
|
||
return (offset > m_ViewBounds.max.y) ? 1 : 0;
|
||
return (offset - m_ViewBounds.max.y) / (totalSize - m_ViewBounds.size.y);
|
||
}
|
||
else
|
||
return 0.5f;
|
||
//==========LoopScrollRect==========
|
||
}
|
||
set
|
||
{
|
||
SetNormalizedPosition(value, 1);
|
||
}
|
||
}
|
||
|
||
private void SetHorizontalNormalizedPosition(float value) { SetNormalizedPosition(value, 0); }
|
||
private void SetVerticalNormalizedPosition(float value) { SetNormalizedPosition(value, 1); }
|
||
|
||
private void SetNormalizedPosition(float value, int axis)
|
||
{
|
||
//==========LoopScrollRect==========
|
||
if (totalCount <= 0 || itemTypeEnd <= itemTypeStart)
|
||
return;
|
||
//==========LoopScrollRect==========
|
||
|
||
EnsureLayoutHasRebuilt();
|
||
UpdateBounds();
|
||
|
||
//==========LoopScrollRect==========
|
||
Vector3 localPosition = m_Content.localPosition;
|
||
float newLocalPosition = localPosition[axis];
|
||
if (axis == 0)
|
||
{
|
||
float elementSize = m_ContentBounds.size.x / (itemTypeEnd - itemTypeStart);
|
||
float totalSize = elementSize * totalCount;
|
||
float offset = m_ContentBounds.min.x - elementSize * itemTypeStart;
|
||
|
||
newLocalPosition += m_ViewBounds.min.x - value * (totalSize - m_ViewBounds.size[axis]) - offset;
|
||
}
|
||
else if(axis == 1)
|
||
{
|
||
float elementSize = m_ContentBounds.size.y / (itemTypeEnd - itemTypeStart);
|
||
float totalSize = elementSize * totalCount;
|
||
float offset = m_ContentBounds.max.y + elementSize * itemTypeStart;
|
||
|
||
newLocalPosition -= offset - value * (totalSize - m_ViewBounds.size.y) - m_ViewBounds.max.y;
|
||
}
|
||
//==========LoopScrollRect==========
|
||
|
||
if (Mathf.Abs(localPosition[axis] - newLocalPosition) > 0.01f)
|
||
{
|
||
localPosition[axis] = newLocalPosition;
|
||
m_Content.localPosition = localPosition;
|
||
m_Velocity[axis] = 0;
|
||
UpdateBounds();
|
||
}
|
||
}
|
||
|
||
private static float RubberDelta(float overStretching, float viewSize)
|
||
{
|
||
return (1 - (1 / ((Mathf.Abs(overStretching) * 0.55f / viewSize) + 1))) * viewSize * Mathf.Sign(overStretching);
|
||
}
|
||
|
||
protected override void OnRectTransformDimensionsChange()
|
||
{
|
||
SetDirty();
|
||
}
|
||
|
||
private bool hScrollingNeeded
|
||
{
|
||
get
|
||
{
|
||
if (Application.isPlaying)
|
||
return m_ContentBounds.size.x > m_ViewBounds.size.x + 0.01f;
|
||
return true;
|
||
}
|
||
}
|
||
private bool vScrollingNeeded
|
||
{
|
||
get
|
||
{
|
||
if (Application.isPlaying)
|
||
return m_ContentBounds.size.y > m_ViewBounds.size.y + 0.01f;
|
||
return true;
|
||
}
|
||
}
|
||
|
||
|
||
public virtual void CalculateLayoutInputHorizontal() { }
|
||
|
||
|
||
public virtual void CalculateLayoutInputVertical() { }
|
||
|
||
|
||
public virtual float minWidth { get { return -1; } }
|
||
|
||
|
||
public virtual float preferredWidth { get { return -1; } }
|
||
|
||
|
||
public virtual float flexibleWidth { get; private set; }
|
||
|
||
|
||
public virtual float minHeight { get { return -1; } }
|
||
|
||
|
||
public virtual float preferredHeight { get { return -1; } }
|
||
|
||
|
||
public virtual float flexibleHeight { get { return -1; } }
|
||
|
||
|
||
public virtual int layoutPriority { get { return -1; } }
|
||
|
||
|
||
public virtual void SetLayoutHorizontal()
|
||
{
|
||
m_Tracker.Clear();
|
||
|
||
if (m_HSliderExpand || m_VSliderExpand)
|
||
{
|
||
m_Tracker.Add(this, viewRect,
|
||
DrivenTransformProperties.Anchors |
|
||
DrivenTransformProperties.SizeDelta |
|
||
DrivenTransformProperties.AnchoredPosition);
|
||
|
||
// Make view full size to see if content fits.
|
||
viewRect.anchorMin = Vector2.zero;
|
||
viewRect.anchorMax = Vector2.one;
|
||
viewRect.sizeDelta = Vector2.zero;
|
||
viewRect.anchoredPosition = Vector2.zero;
|
||
|
||
// Recalculate content layout with this size to see if it fits when there are no scrollbars.
|
||
LayoutRebuilder.ForceRebuildLayoutImmediate(content);
|
||
m_ViewBounds = new Bounds(viewRect.rect.center, viewRect.rect.size);
|
||
m_ContentBounds = GetBounds();
|
||
}
|
||
|
||
// If it doesn't fit vertically, enable vertical scrollbar and shrink view horizontally to make room for it.
|
||
if (m_VSliderExpand && vScrollingNeeded)
|
||
{
|
||
viewRect.sizeDelta = new Vector2(-(m_VSliderWidth + m_VerticalScrollbarSpacing), viewRect.sizeDelta.y);
|
||
|
||
// Recalculate content layout with this size to see if it fits vertically
|
||
// when there is a vertical scrollbar (which may reflowed the content to make it taller).
|
||
LayoutRebuilder.ForceRebuildLayoutImmediate(content);
|
||
m_ViewBounds = new Bounds(viewRect.rect.center, viewRect.rect.size);
|
||
m_ContentBounds = GetBounds();
|
||
}
|
||
|
||
// If it doesn't fit horizontally, enable horizontal scrollbar and shrink view vertically to make room for it.
|
||
if (m_HSliderExpand && hScrollingNeeded)
|
||
{
|
||
viewRect.sizeDelta = new Vector2(viewRect.sizeDelta.x, -(m_HSliderHeight + m_HorizontalScrollbarSpacing));
|
||
m_ViewBounds = new Bounds(viewRect.rect.center, viewRect.rect.size);
|
||
m_ContentBounds = GetBounds();
|
||
}
|
||
|
||
// If the vertical slider didn't kick in the first time, and the horizontal one did,
|
||
// we need to check again if the vertical slider now needs to kick in.
|
||
// If it doesn't fit vertically, enable vertical scrollbar and shrink view horizontally to make room for it.
|
||
if (m_VSliderExpand && vScrollingNeeded && viewRect.sizeDelta.x == 0 && viewRect.sizeDelta.y < 0)
|
||
{
|
||
viewRect.sizeDelta = new Vector2(-(m_VSliderWidth + m_VerticalScrollbarSpacing), viewRect.sizeDelta.y);
|
||
}
|
||
}
|
||
|
||
|
||
public virtual void SetLayoutVertical()
|
||
{
|
||
UpdateScrollbarLayout();
|
||
m_ViewBounds = new Bounds(viewRect.rect.center, viewRect.rect.size);
|
||
m_ContentBounds = GetBounds();
|
||
}
|
||
|
||
void UpdateScrollbarVisibility()
|
||
{
|
||
if (m_VerticalScrollbar && m_VerticalScrollbarVisibility != ScrollbarVisibility.Permanent && m_VerticalScrollbar.gameObject.activeSelf != vScrollingNeeded)
|
||
m_VerticalScrollbar.gameObject.SetActive(vScrollingNeeded);
|
||
|
||
if (m_HorizontalScrollbar && m_HorizontalScrollbarVisibility != ScrollbarVisibility.Permanent && m_HorizontalScrollbar.gameObject.activeSelf != hScrollingNeeded)
|
||
m_HorizontalScrollbar.gameObject.SetActive(hScrollingNeeded);
|
||
}
|
||
|
||
void UpdateScrollbarLayout()
|
||
{
|
||
if (m_VSliderExpand && m_HorizontalScrollbar)
|
||
{
|
||
m_Tracker.Add(this, m_HorizontalScrollbarRect,
|
||
DrivenTransformProperties.AnchorMinX |
|
||
DrivenTransformProperties.AnchorMaxX |
|
||
DrivenTransformProperties.SizeDeltaX |
|
||
DrivenTransformProperties.AnchoredPositionX);
|
||
m_HorizontalScrollbarRect.anchorMin = new Vector2(0, m_HorizontalScrollbarRect.anchorMin.y);
|
||
m_HorizontalScrollbarRect.anchorMax = new Vector2(1, m_HorizontalScrollbarRect.anchorMax.y);
|
||
m_HorizontalScrollbarRect.anchoredPosition = new Vector2(0, m_HorizontalScrollbarRect.anchoredPosition.y);
|
||
if (vScrollingNeeded)
|
||
m_HorizontalScrollbarRect.sizeDelta = new Vector2(-(m_VSliderWidth + m_VerticalScrollbarSpacing), m_HorizontalScrollbarRect.sizeDelta.y);
|
||
else
|
||
m_HorizontalScrollbarRect.sizeDelta = new Vector2(0, m_HorizontalScrollbarRect.sizeDelta.y);
|
||
}
|
||
|
||
if (m_HSliderExpand && m_VerticalScrollbar)
|
||
{
|
||
m_Tracker.Add(this, m_VerticalScrollbarRect,
|
||
DrivenTransformProperties.AnchorMinY |
|
||
DrivenTransformProperties.AnchorMaxY |
|
||
DrivenTransformProperties.SizeDeltaY |
|
||
DrivenTransformProperties.AnchoredPositionY);
|
||
m_VerticalScrollbarRect.anchorMin = new Vector2(m_VerticalScrollbarRect.anchorMin.x, 0);
|
||
m_VerticalScrollbarRect.anchorMax = new Vector2(m_VerticalScrollbarRect.anchorMax.x, 1);
|
||
m_VerticalScrollbarRect.anchoredPosition = new Vector2(m_VerticalScrollbarRect.anchoredPosition.x, 0);
|
||
if (hScrollingNeeded)
|
||
m_VerticalScrollbarRect.sizeDelta = new Vector2(m_VerticalScrollbarRect.sizeDelta.x, -(m_HSliderHeight + m_HorizontalScrollbarSpacing));
|
||
else
|
||
m_VerticalScrollbarRect.sizeDelta = new Vector2(m_VerticalScrollbarRect.sizeDelta.x, 0);
|
||
}
|
||
}
|
||
|
||
private void UpdateBounds(bool updateItems = true)
|
||
{
|
||
m_ViewBounds = new Bounds(viewRect.rect.center, viewRect.rect.size);
|
||
m_ContentBounds = GetBounds();
|
||
|
||
if (m_Content == null)
|
||
return;
|
||
|
||
// ============LoopScrollRect============
|
||
// Don't do this in Rebuild
|
||
if (Application.isPlaying && updateItems && UpdateItems(m_ViewBounds, m_ContentBounds))
|
||
{
|
||
Canvas.ForceUpdateCanvases();
|
||
m_ContentBounds = GetBounds();
|
||
}
|
||
// ============LoopScrollRect============
|
||
|
||
// Make sure content bounds are at least as large as view by adding padding if not.
|
||
// One might think at first that if the content is smaller than the view, scrolling should be allowed.
|
||
// However, that's not how scroll views normally work.
|
||
// Scrolling is *only* possible when content is *larger* than view.
|
||
// We use the pivot of the content rect to decide in which directions the content bounds should be expanded.
|
||
// E.g. if pivot is at top, bounds are expanded downwards.
|
||
// This also works nicely when ContentSizeFitter is used on the content.
|
||
Vector3 contentSize = m_ContentBounds.size;
|
||
Vector3 contentPos = m_ContentBounds.center;
|
||
Vector3 excess = m_ViewBounds.size - contentSize;
|
||
if (excess.x > 0)
|
||
{
|
||
contentPos.x -= excess.x * (m_Content.pivot.x - 0.5f);
|
||
contentSize.x = m_ViewBounds.size.x;
|
||
}
|
||
if (excess.y > 0)
|
||
{
|
||
contentPos.y -= excess.y * (m_Content.pivot.y - 0.5f);
|
||
contentSize.y = m_ViewBounds.size.y;
|
||
}
|
||
|
||
m_ContentBounds.size = contentSize;
|
||
m_ContentBounds.center = contentPos;
|
||
}
|
||
|
||
private readonly Vector3[] m_Corners = new Vector3[4];
|
||
private Bounds GetBounds()
|
||
{
|
||
if (m_Content == null)
|
||
return new Bounds();
|
||
|
||
var vMin = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
|
||
var vMax = new Vector3(float.MinValue, float.MinValue, float.MinValue);
|
||
|
||
var toLocal = viewRect.worldToLocalMatrix;
|
||
m_Content.GetWorldCorners(m_Corners);
|
||
for (int j = 0; j < 4; j++)
|
||
{
|
||
Vector3 v = toLocal.MultiplyPoint3x4(m_Corners[j]);
|
||
vMin = Vector3.Min(v, vMin);
|
||
vMax = Vector3.Max(v, vMax);
|
||
}
|
||
|
||
var bounds = new Bounds(vMin, Vector3.zero);
|
||
bounds.Encapsulate(vMax);
|
||
return bounds;
|
||
}
|
||
|
||
private Vector2 CalculateOffset(Vector2 delta)
|
||
{
|
||
Vector2 offset = Vector2.zero;
|
||
if (m_MovementType == MovementType.Unrestricted)
|
||
return offset;
|
||
|
||
Vector2 min = m_ContentBounds.min;
|
||
Vector2 max = m_ContentBounds.max;
|
||
|
||
if (m_Horizontal)
|
||
{
|
||
min.x += delta.x;
|
||
max.x += delta.x;
|
||
if (min.x > m_ViewBounds.min.x)
|
||
offset.x = m_ViewBounds.min.x - min.x;
|
||
else if (max.x < m_ViewBounds.max.x)
|
||
offset.x = m_ViewBounds.max.x - max.x;
|
||
}
|
||
|
||
if (m_Vertical)
|
||
{
|
||
min.y += delta.y;
|
||
max.y += delta.y;
|
||
if (max.y < m_ViewBounds.max.y)
|
||
offset.y = m_ViewBounds.max.y - max.y;
|
||
else if (min.y > m_ViewBounds.min.y)
|
||
offset.y = m_ViewBounds.min.y - min.y;
|
||
}
|
||
|
||
return offset;
|
||
}
|
||
|
||
protected void SetDirty()
|
||
{
|
||
if (!IsActive())
|
||
return;
|
||
|
||
LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
|
||
}
|
||
|
||
protected void SetDirtyCaching()
|
||
{
|
||
if (!IsActive())
|
||
return;
|
||
|
||
CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
|
||
LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
|
||
}
|
||
|
||
#if UNITY_EDITOR
|
||
protected override void OnValidate()
|
||
{
|
||
SetDirtyCaching();
|
||
}
|
||
#endif
|
||
}
|
||
} |