ScrollRect.cs 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423
  1. using System;
  2. using UnityEngine.Events;
  3. using UnityEngine.EventSystems;
  4. namespace UnityEngine.UI
  5. {
  6. [AddComponentMenu("UI/Scroll Rect", 37)]
  7. [SelectionBase]
  8. [ExecuteAlways]
  9. [DisallowMultipleComponent]
  10. [RequireComponent(typeof(RectTransform))]
  11. /// <summary>
  12. /// A component for making a child RectTransform scroll.
  13. /// </summary>
  14. /// <remarks>
  15. /// ScrollRect will not do any clipping on its own. Combined with a Mask component, it can be turned into a scroll view.
  16. /// </remarks>
  17. public class ScrollRect : UIBehaviour, IInitializePotentialDragHandler, IBeginDragHandler, IEndDragHandler, IDragHandler, IScrollHandler, ICanvasElement, ILayoutElement, ILayoutGroup
  18. {
  19. /// <summary>
  20. /// A setting for which behavior to use when content moves beyond the confines of its container.
  21. /// </summary>
  22. /// <example>
  23. /// <code>
  24. /// using UnityEngine;
  25. /// using System.Collections;
  26. /// using UnityEngine.UI; // Required when Using UI elements.
  27. ///
  28. /// public class ExampleClass : MonoBehaviour
  29. /// {
  30. /// public ScrollRect myScrollRect;
  31. /// public Scrollbar newScrollBar;
  32. ///
  33. /// //Called when a button is pressed
  34. /// public void Example(int option)
  35. /// {
  36. /// if (option == 0)
  37. /// {
  38. /// myScrollRect.movementType = ScrollRect.MovementType.Clamped;
  39. /// }
  40. /// else if (option == 1)
  41. /// {
  42. /// myScrollRect.movementType = ScrollRect.MovementType.Elastic;
  43. /// }
  44. /// else if (option == 2)
  45. /// {
  46. /// myScrollRect.movementType = ScrollRect.MovementType.Unrestricted;
  47. /// }
  48. /// }
  49. /// }
  50. /// </code>
  51. /// </example>
  52. public enum MovementType
  53. {
  54. /// <summary>
  55. /// Unrestricted movement. The content can move forever.
  56. /// </summary>
  57. Unrestricted,
  58. /// <summary>
  59. /// Elastic movement. The content is allowed to temporarily move beyond the container, but is pulled back elastically.
  60. /// </summary>
  61. Elastic,
  62. /// <summary>
  63. /// Clamped movement. The content can not be moved beyond its container.
  64. /// </summary>
  65. Clamped,
  66. }
  67. /// <summary>
  68. /// Enum for which behavior to use for scrollbar visibility.
  69. /// </summary>
  70. public enum ScrollbarVisibility
  71. {
  72. /// <summary>
  73. /// Always show the scrollbar.
  74. /// </summary>
  75. Permanent,
  76. /// <summary>
  77. /// Automatically hide the scrollbar when no scrolling is needed on this axis. The viewport rect will not be changed.
  78. /// </summary>
  79. AutoHide,
  80. /// <summary>
  81. /// Automatically hide the scrollbar when no scrolling is needed on this axis, and expand the viewport rect accordingly.
  82. /// </summary>
  83. /// <remarks>
  84. /// When this setting is used, the scrollbar and the viewport rect become driven, meaning that values in the RectTransform are calculated automatically and can't be manually edited.
  85. /// </remarks>
  86. AutoHideAndExpandViewport,
  87. }
  88. [Serializable]
  89. /// <summary>
  90. /// Event type used by the ScrollRect.
  91. /// </summary>
  92. public class ScrollRectEvent : UnityEvent<Vector2> {}
  93. [SerializeField]
  94. private RectTransform m_Content;
  95. /// <summary>
  96. /// The content that can be scrolled. It should be a child of the GameObject with ScrollRect on it.
  97. /// </summary>
  98. /// <example>
  99. /// <code>
  100. /// using UnityEngine;
  101. /// using System.Collections;
  102. /// using UnityEngine.UI; // Required when Using UI elements.
  103. ///
  104. /// public class ExampleClass : MonoBehaviour
  105. /// {
  106. /// public ScrollRect myScrollRect;
  107. /// public RectTransform scrollableContent;
  108. ///
  109. /// //Do this when the Save button is selected.
  110. /// public void Start()
  111. /// {
  112. /// // assigns the contect that can be scrolled using the ScrollRect.
  113. /// myScrollRect.content = scrollableContent;
  114. /// }
  115. /// }
  116. /// </code>
  117. /// </example>
  118. public RectTransform content { get { return m_Content; } set { m_Content = value; } }
  119. [SerializeField]
  120. private bool m_Horizontal = true;
  121. /// <summary>
  122. /// Should horizontal scrolling be enabled?
  123. /// </summary>
  124. /// <example>
  125. /// <code>
  126. /// using UnityEngine;
  127. /// using System.Collections;
  128. /// using UnityEngine.UI; // Required when Using UI elements.
  129. ///
  130. /// public class ExampleClass : MonoBehaviour
  131. /// {
  132. /// public ScrollRect myScrollRect;
  133. ///
  134. /// public void Start()
  135. /// {
  136. /// // Is horizontal scrolling enabled?
  137. /// if (myScrollRect.horizontal == true)
  138. /// {
  139. /// Debug.Log("Horizontal Scrolling is Enabled!");
  140. /// }
  141. /// }
  142. /// }
  143. /// </code>
  144. /// </example>
  145. public bool horizontal { get { return m_Horizontal; } set { m_Horizontal = value; } }
  146. [SerializeField]
  147. private bool m_Vertical = true;
  148. /// <summary>
  149. /// Should vertical scrolling be enabled?
  150. /// </summary>
  151. /// <example>
  152. /// <code>
  153. /// using UnityEngine;
  154. /// using System.Collections;
  155. /// using UnityEngine.UI; // Required when Using UI elements.
  156. ///
  157. /// public class ExampleClass : MonoBehaviour
  158. /// {
  159. /// public ScrollRect myScrollRect;
  160. ///
  161. /// public void Start()
  162. /// {
  163. /// // Is Vertical scrolling enabled?
  164. /// if (myScrollRect.vertical == true)
  165. /// {
  166. /// Debug.Log("Vertical Scrolling is Enabled!");
  167. /// }
  168. /// }
  169. /// }
  170. /// </code>
  171. /// </example>
  172. public bool vertical { get { return m_Vertical; } set { m_Vertical = value; } }
  173. [SerializeField]
  174. private MovementType m_MovementType = MovementType.Elastic;
  175. /// <summary>
  176. /// The behavior to use when the content moves beyond the scroll rect.
  177. /// </summary>
  178. public MovementType movementType { get { return m_MovementType; } set { m_MovementType = value; } }
  179. [SerializeField]
  180. private float m_Elasticity = 0.1f;
  181. /// <summary>
  182. /// The amount of elasticity to use when the content moves beyond the scroll rect.
  183. /// </summary>
  184. /// <example>
  185. /// <code>
  186. /// using UnityEngine;
  187. /// using System.Collections;
  188. /// using UnityEngine.UI;
  189. ///
  190. /// public class ExampleClass : MonoBehaviour
  191. /// {
  192. /// public ScrollRect myScrollRect;
  193. ///
  194. /// public void Start()
  195. /// {
  196. /// // assigns a new value to the elasticity of the scroll rect.
  197. /// // The higher the number the longer it takes to snap back.
  198. /// myScrollRect.elasticity = 3.0f;
  199. /// }
  200. /// }
  201. /// </code>
  202. /// </example>
  203. public float elasticity { get { return m_Elasticity; } set { m_Elasticity = value; } }
  204. [SerializeField]
  205. private bool m_Inertia = true;
  206. /// <summary>
  207. /// Should movement inertia be enabled?
  208. /// </summary>
  209. /// <remarks>
  210. /// Inertia means that the scrollrect content will keep scrolling for a while after being dragged. It gradually slows down according to the decelerationRate.
  211. /// </remarks>
  212. public bool inertia { get { return m_Inertia; } set { m_Inertia = value; } }
  213. [SerializeField]
  214. private float m_DecelerationRate = 0.135f; // Only used when inertia is enabled
  215. /// <summary>
  216. /// The rate at which movement slows down.
  217. /// </summary>
  218. /// <remarks>
  219. /// The deceleration rate is the speed reduction per second. A value of 0.5 halves the speed each second. The default is 0.135. The deceleration rate is only used when inertia is enabled.
  220. /// </remarks>
  221. /// <example>
  222. /// <code>
  223. /// using UnityEngine;
  224. /// using System.Collections;
  225. /// using UnityEngine.UI; // Required when Using UI elements.
  226. ///
  227. /// public class ExampleClass : MonoBehaviour
  228. /// {
  229. /// public ScrollRect myScrollRect;
  230. ///
  231. /// public void Start()
  232. /// {
  233. /// // assigns a new value to the decelerationRate of the scroll rect.
  234. /// // The higher the number the longer it takes to decelerate.
  235. /// myScrollRect.decelerationRate = 5.0f;
  236. /// }
  237. /// }
  238. /// </code>
  239. /// </example>
  240. public float decelerationRate { get { return m_DecelerationRate; } set { m_DecelerationRate = value; } }
  241. [SerializeField]
  242. private float m_ScrollSensitivity = 1.0f;
  243. /// <summary>
  244. /// The sensitivity to scroll wheel and track pad scroll events.
  245. /// </summary>
  246. /// <remarks>
  247. /// Higher values indicate higher sensitivity.
  248. /// </remarks>
  249. public float scrollSensitivity { get { return m_ScrollSensitivity; } set { m_ScrollSensitivity = value; } }
  250. [SerializeField]
  251. private RectTransform m_Viewport;
  252. /// <summary>
  253. /// Reference to the viewport RectTransform that is the parent of the content RectTransform.
  254. /// </summary>
  255. public RectTransform viewport { get { return m_Viewport; } set { m_Viewport = value; SetDirtyCaching(); } }
  256. [SerializeField]
  257. private Scrollbar m_HorizontalScrollbar;
  258. /// <summary>
  259. /// Optional Scrollbar object linked to the horizontal scrolling of the ScrollRect.
  260. /// </summary>
  261. /// <example>
  262. /// <code>
  263. /// using UnityEngine;
  264. /// using System.Collections;
  265. /// using UnityEngine.UI; // Required when Using UI elements.
  266. ///
  267. /// public class ExampleClass : MonoBehaviour
  268. /// {
  269. /// public ScrollRect myScrollRect;
  270. /// public Scrollbar newScrollBar;
  271. ///
  272. /// public void Start()
  273. /// {
  274. /// // Assigns a scroll bar element to the ScrollRect, allowing you to scroll in the horizontal axis.
  275. /// myScrollRect.horizontalScrollbar = newScrollBar;
  276. /// }
  277. /// }
  278. /// </code>
  279. /// </example>
  280. public Scrollbar horizontalScrollbar
  281. {
  282. get
  283. {
  284. return m_HorizontalScrollbar;
  285. }
  286. set
  287. {
  288. if (m_HorizontalScrollbar)
  289. m_HorizontalScrollbar.onValueChanged.RemoveListener(SetHorizontalNormalizedPosition);
  290. m_HorizontalScrollbar = value;
  291. if (m_HorizontalScrollbar)
  292. m_HorizontalScrollbar.onValueChanged.AddListener(SetHorizontalNormalizedPosition);
  293. SetDirtyCaching();
  294. }
  295. }
  296. [SerializeField]
  297. private Scrollbar m_VerticalScrollbar;
  298. /// <summary>
  299. /// Optional Scrollbar object linked to the vertical scrolling of the ScrollRect.
  300. /// </summary>
  301. /// <example>
  302. /// <code>
  303. /// using UnityEngine;
  304. /// using System.Collections;
  305. /// using UnityEngine.UI; // Required when Using UI elements.
  306. ///
  307. /// public class ExampleClass : MonoBehaviour
  308. /// {
  309. /// public ScrollRect myScrollRect;
  310. /// public Scrollbar newScrollBar;
  311. ///
  312. /// public void Start()
  313. /// {
  314. /// // Assigns a scroll bar element to the ScrollRect, allowing you to scroll in the vertical axis.
  315. /// myScrollRect.verticalScrollbar = newScrollBar;
  316. /// }
  317. /// }
  318. /// </code>
  319. /// </example>
  320. public Scrollbar verticalScrollbar
  321. {
  322. get
  323. {
  324. return m_VerticalScrollbar;
  325. }
  326. set
  327. {
  328. if (m_VerticalScrollbar)
  329. m_VerticalScrollbar.onValueChanged.RemoveListener(SetVerticalNormalizedPosition);
  330. m_VerticalScrollbar = value;
  331. if (m_VerticalScrollbar)
  332. m_VerticalScrollbar.onValueChanged.AddListener(SetVerticalNormalizedPosition);
  333. SetDirtyCaching();
  334. }
  335. }
  336. [SerializeField]
  337. private ScrollbarVisibility m_HorizontalScrollbarVisibility;
  338. /// <summary>
  339. /// The mode of visibility for the horizontal scrollbar.
  340. /// </summary>
  341. public ScrollbarVisibility horizontalScrollbarVisibility { get { return m_HorizontalScrollbarVisibility; } set { m_HorizontalScrollbarVisibility = value; SetDirtyCaching(); } }
  342. [SerializeField]
  343. private ScrollbarVisibility m_VerticalScrollbarVisibility;
  344. /// <summary>
  345. /// The mode of visibility for the vertical scrollbar.
  346. /// </summary>
  347. public ScrollbarVisibility verticalScrollbarVisibility { get { return m_VerticalScrollbarVisibility; } set { m_VerticalScrollbarVisibility = value; SetDirtyCaching(); } }
  348. [SerializeField]
  349. private float m_HorizontalScrollbarSpacing;
  350. /// <summary>
  351. /// The space between the scrollbar and the viewport.
  352. /// </summary>
  353. public float horizontalScrollbarSpacing { get { return m_HorizontalScrollbarSpacing; } set { m_HorizontalScrollbarSpacing = value; SetDirty(); } }
  354. [SerializeField]
  355. private float m_VerticalScrollbarSpacing;
  356. /// <summary>
  357. /// The space between the scrollbar and the viewport.
  358. /// </summary>
  359. public float verticalScrollbarSpacing { get { return m_VerticalScrollbarSpacing; } set { m_VerticalScrollbarSpacing = value; SetDirty(); } }
  360. [SerializeField]
  361. private ScrollRectEvent m_OnValueChanged = new ScrollRectEvent();
  362. /// <summary>
  363. /// Callback executed when the position of the child changes.
  364. /// </summary>
  365. /// <remarks>
  366. /// onValueChanged is used to watch for changes in the ScrollRect object.
  367. /// The onValueChanged call will use the UnityEvent.AddListener API to watch for
  368. /// changes. When changes happen script code provided by the user will be called.
  369. /// The UnityEvent.AddListener API for UI.ScrollRect._onValueChanged takes a Vector2.
  370. ///
  371. /// Note: The editor allows the onValueChanged value to be set up manually.For example the
  372. /// value can be set to run only a runtime. The object and script function to call are also
  373. /// provided here.
  374. ///
  375. /// The onValueChanged variable can be alternatively set-up at runtime.The script example below
  376. /// shows how this can be done.The script is attached to the ScrollRect object.
  377. /// </remarks>
  378. /// <example>
  379. /// <code>
  380. /// using UnityEngine;
  381. /// using UnityEngine.UI;
  382. ///
  383. /// public class ExampleScript : MonoBehaviour
  384. /// {
  385. /// static ScrollRect scrollRect;
  386. ///
  387. /// void Start()
  388. /// {
  389. /// scrollRect = GetComponent<ScrollRect>();
  390. /// scrollRect.onValueChanged.AddListener(ListenerMethod);
  391. /// }
  392. ///
  393. /// public void ListenerMethod(Vector2 value)
  394. /// {
  395. /// Debug.Log("ListenerMethod: " + value);
  396. /// }
  397. /// }
  398. /// </code>
  399. /// </example>
  400. public ScrollRectEvent onValueChanged { get { return m_OnValueChanged; } set { m_OnValueChanged = value; } }
  401. // The offset from handle position to mouse down position
  402. private Vector2 m_PointerStartLocalCursor = Vector2.zero;
  403. protected Vector2 m_ContentStartPosition = Vector2.zero;
  404. private RectTransform m_ViewRect;
  405. protected RectTransform viewRect
  406. {
  407. get
  408. {
  409. if (m_ViewRect == null)
  410. m_ViewRect = m_Viewport;
  411. if (m_ViewRect == null)
  412. m_ViewRect = (RectTransform)transform;
  413. return m_ViewRect;
  414. }
  415. }
  416. protected Bounds m_ContentBounds;
  417. private Bounds m_ViewBounds;
  418. private Vector2 m_Velocity;
  419. /// <summary>
  420. /// The current velocity of the content.
  421. /// </summary>
  422. /// <remarks>
  423. /// The velocity is defined in units per second.
  424. /// </remarks>
  425. public Vector2 velocity { get { return m_Velocity; } set { m_Velocity = value; } }
  426. private bool m_Dragging;
  427. private bool m_Scrolling;
  428. private Vector2 m_PrevPosition = Vector2.zero;
  429. private Bounds m_PrevContentBounds;
  430. private Bounds m_PrevViewBounds;
  431. [NonSerialized]
  432. private bool m_HasRebuiltLayout = false;
  433. private bool m_HSliderExpand;
  434. private bool m_VSliderExpand;
  435. private float m_HSliderHeight;
  436. private float m_VSliderWidth;
  437. [System.NonSerialized] private RectTransform m_Rect;
  438. private RectTransform rectTransform
  439. {
  440. get
  441. {
  442. if (m_Rect == null)
  443. m_Rect = GetComponent<RectTransform>();
  444. return m_Rect;
  445. }
  446. }
  447. private RectTransform m_HorizontalScrollbarRect;
  448. private RectTransform m_VerticalScrollbarRect;
  449. // field is never assigned warning
  450. #pragma warning disable 649
  451. private DrivenRectTransformTracker m_Tracker;
  452. #pragma warning restore 649
  453. protected ScrollRect()
  454. {}
  455. /// <summary>
  456. /// Rebuilds the scroll rect data after initialization.
  457. /// </summary>
  458. /// <param name="executing">The current step in the rendering CanvasUpdate cycle.</param>
  459. public virtual void Rebuild(CanvasUpdate executing)
  460. {
  461. if (executing == CanvasUpdate.Prelayout)
  462. {
  463. UpdateCachedData();
  464. }
  465. if (executing == CanvasUpdate.PostLayout)
  466. {
  467. UpdateBounds();
  468. UpdateScrollbars(Vector2.zero);
  469. UpdatePrevData();
  470. m_HasRebuiltLayout = true;
  471. }
  472. }
  473. public virtual void LayoutComplete()
  474. {}
  475. public virtual void GraphicUpdateComplete()
  476. {}
  477. void UpdateCachedData()
  478. {
  479. Transform transform = this.transform;
  480. m_HorizontalScrollbarRect = m_HorizontalScrollbar == null ? null : m_HorizontalScrollbar.transform as RectTransform;
  481. m_VerticalScrollbarRect = m_VerticalScrollbar == null ? null : m_VerticalScrollbar.transform as RectTransform;
  482. // These are true if either the elements are children, or they don't exist at all.
  483. bool viewIsChild = (viewRect.parent == transform);
  484. bool hScrollbarIsChild = (!m_HorizontalScrollbarRect || m_HorizontalScrollbarRect.parent == transform);
  485. bool vScrollbarIsChild = (!m_VerticalScrollbarRect || m_VerticalScrollbarRect.parent == transform);
  486. bool allAreChildren = (viewIsChild && hScrollbarIsChild && vScrollbarIsChild);
  487. m_HSliderExpand = allAreChildren && m_HorizontalScrollbarRect && horizontalScrollbarVisibility == ScrollbarVisibility.AutoHideAndExpandViewport;
  488. m_VSliderExpand = allAreChildren && m_VerticalScrollbarRect && verticalScrollbarVisibility == ScrollbarVisibility.AutoHideAndExpandViewport;
  489. m_HSliderHeight = (m_HorizontalScrollbarRect == null ? 0 : m_HorizontalScrollbarRect.rect.height);
  490. m_VSliderWidth = (m_VerticalScrollbarRect == null ? 0 : m_VerticalScrollbarRect.rect.width);
  491. }
  492. protected override void OnEnable()
  493. {
  494. base.OnEnable();
  495. if (m_HorizontalScrollbar)
  496. m_HorizontalScrollbar.onValueChanged.AddListener(SetHorizontalNormalizedPosition);
  497. if (m_VerticalScrollbar)
  498. m_VerticalScrollbar.onValueChanged.AddListener(SetVerticalNormalizedPosition);
  499. CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
  500. SetDirty();
  501. }
  502. protected override void OnDisable()
  503. {
  504. CanvasUpdateRegistry.UnRegisterCanvasElementForRebuild(this);
  505. if (m_HorizontalScrollbar)
  506. m_HorizontalScrollbar.onValueChanged.RemoveListener(SetHorizontalNormalizedPosition);
  507. if (m_VerticalScrollbar)
  508. m_VerticalScrollbar.onValueChanged.RemoveListener(SetVerticalNormalizedPosition);
  509. m_Dragging = false;
  510. m_Scrolling = false;
  511. m_HasRebuiltLayout = false;
  512. m_Tracker.Clear();
  513. m_Velocity = Vector2.zero;
  514. LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
  515. base.OnDisable();
  516. }
  517. /// <summary>
  518. /// See member in base class.
  519. /// </summary>
  520. /// <example>
  521. /// <code>
  522. /// using UnityEngine;
  523. /// using System.Collections;
  524. /// using UnityEngine.UI; // Required when Using UI elements.
  525. ///
  526. /// public class ExampleClass : MonoBehaviour
  527. /// {
  528. /// public ScrollRect myScrollRect;
  529. ///
  530. /// public void Start()
  531. /// {
  532. /// //Checks if the ScrollRect called "myScrollRect" is active.
  533. /// if (myScrollRect.IsActive())
  534. /// {
  535. /// Debug.Log("The Scroll Rect is active!");
  536. /// }
  537. /// }
  538. /// }
  539. /// </code>
  540. /// </example>
  541. public override bool IsActive()
  542. {
  543. return base.IsActive() && m_Content != null;
  544. }
  545. private void EnsureLayoutHasRebuilt()
  546. {
  547. if (!m_HasRebuiltLayout && !CanvasUpdateRegistry.IsRebuildingLayout())
  548. Canvas.ForceUpdateCanvases();
  549. }
  550. /// <summary>
  551. /// Sets the velocity to zero on both axes so the content stops moving.
  552. /// </summary>
  553. public virtual void StopMovement()
  554. {
  555. m_Velocity = Vector2.zero;
  556. }
  557. public virtual void OnScroll(PointerEventData data)
  558. {
  559. if (!IsActive())
  560. return;
  561. EnsureLayoutHasRebuilt();
  562. UpdateBounds();
  563. Vector2 delta = data.scrollDelta;
  564. // Down is positive for scroll events, while in UI system up is positive.
  565. delta.y *= -1;
  566. if (vertical && !horizontal)
  567. {
  568. if (Mathf.Abs(delta.x) > Mathf.Abs(delta.y))
  569. delta.y = delta.x;
  570. delta.x = 0;
  571. }
  572. if (horizontal && !vertical)
  573. {
  574. if (Mathf.Abs(delta.y) > Mathf.Abs(delta.x))
  575. delta.x = delta.y;
  576. delta.y = 0;
  577. }
  578. if (data.IsScrolling())
  579. m_Scrolling = true;
  580. Vector2 position = m_Content.anchoredPosition;
  581. position += delta * m_ScrollSensitivity;
  582. if (m_MovementType == MovementType.Clamped)
  583. position += CalculateOffset(position - m_Content.anchoredPosition);
  584. SetContentAnchoredPosition(position);
  585. UpdateBounds();
  586. }
  587. public virtual void OnInitializePotentialDrag(PointerEventData eventData)
  588. {
  589. if (eventData.button != PointerEventData.InputButton.Left)
  590. return;
  591. m_Velocity = Vector2.zero;
  592. }
  593. /// <summary>
  594. /// Handling for when the content is beging being dragged.
  595. /// </summary>
  596. ///<example>
  597. /// <code>
  598. /// using UnityEngine;
  599. /// using System.Collections;
  600. /// using UnityEngine.EventSystems; // Required when using event data
  601. ///
  602. /// public class ExampleClass : MonoBehaviour, IBeginDragHandler // required interface when using the OnBeginDrag method.
  603. /// {
  604. /// //Do this when the user starts dragging the element this script is attached to..
  605. /// public void OnBeginDrag(PointerEventData data)
  606. /// {
  607. /// Debug.Log("They started dragging " + this.name);
  608. /// }
  609. /// }
  610. /// </code>
  611. /// </example>
  612. public virtual void OnBeginDrag(PointerEventData eventData)
  613. {
  614. if (eventData.button != PointerEventData.InputButton.Left)
  615. return;
  616. if (!IsActive())
  617. return;
  618. UpdateBounds();
  619. m_PointerStartLocalCursor = Vector2.zero;
  620. RectTransformUtility.ScreenPointToLocalPointInRectangle(viewRect, eventData.position, eventData.pressEventCamera, out m_PointerStartLocalCursor);
  621. m_ContentStartPosition = m_Content.anchoredPosition;
  622. m_Dragging = true;
  623. }
  624. /// <summary>
  625. /// Handling for when the content has finished being dragged.
  626. /// </summary>
  627. /// <example>
  628. /// <code>
  629. /// using UnityEngine;
  630. /// using System.Collections;
  631. /// using UnityEngine.EventSystems; // Required when using event data
  632. ///
  633. /// public class ExampleClass : MonoBehaviour, IEndDragHandler // required interface when using the OnEndDrag method.
  634. /// {
  635. /// //Do this when the user stops dragging this UI Element.
  636. /// public void OnEndDrag(PointerEventData data)
  637. /// {
  638. /// Debug.Log("Stopped dragging " + this.name + "!");
  639. /// }
  640. /// }
  641. /// </code>
  642. /// </example>
  643. public virtual void OnEndDrag(PointerEventData eventData)
  644. {
  645. if (eventData.button != PointerEventData.InputButton.Left)
  646. return;
  647. m_Dragging = false;
  648. }
  649. /// <summary>
  650. /// Handling for when the content is dragged.
  651. /// </summary>
  652. /// <example>
  653. /// <code>
  654. /// using UnityEngine;
  655. /// using System.Collections;
  656. /// using UnityEngine.EventSystems; // Required when using event data
  657. ///
  658. /// public class ExampleClass : MonoBehaviour, IDragHandler // required interface when using the OnDrag method.
  659. /// {
  660. /// //Do this while the user is dragging this UI Element.
  661. /// public void OnDrag(PointerEventData data)
  662. /// {
  663. /// Debug.Log("Currently dragging " + this.name);
  664. /// }
  665. /// }
  666. /// </code>
  667. /// </example>
  668. public virtual void OnDrag(PointerEventData eventData)
  669. {
  670. if (!m_Dragging)
  671. return;
  672. if (eventData.button != PointerEventData.InputButton.Left)
  673. return;
  674. if (!IsActive())
  675. return;
  676. Vector2 localCursor;
  677. if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(viewRect, eventData.position, eventData.pressEventCamera, out localCursor))
  678. return;
  679. UpdateBounds();
  680. var pointerDelta = localCursor - m_PointerStartLocalCursor;
  681. Vector2 position = m_ContentStartPosition + pointerDelta;
  682. // Offset to get content into place in the view.
  683. Vector2 offset = CalculateOffset(position - m_Content.anchoredPosition);
  684. position += offset;
  685. if (m_MovementType == MovementType.Elastic)
  686. {
  687. if (offset.x != 0)
  688. position.x = position.x - RubberDelta(offset.x, m_ViewBounds.size.x);
  689. if (offset.y != 0)
  690. position.y = position.y - RubberDelta(offset.y, m_ViewBounds.size.y);
  691. }
  692. SetContentAnchoredPosition(position);
  693. }
  694. /// <summary>
  695. /// Sets the anchored position of the content.
  696. /// </summary>
  697. protected virtual void SetContentAnchoredPosition(Vector2 position)
  698. {
  699. if (!m_Horizontal)
  700. position.x = m_Content.anchoredPosition.x;
  701. if (!m_Vertical)
  702. position.y = m_Content.anchoredPosition.y;
  703. if (position != m_Content.anchoredPosition)
  704. {
  705. m_Content.anchoredPosition = position;
  706. UpdateBounds();
  707. }
  708. }
  709. protected virtual void LateUpdate()
  710. {
  711. if (!m_Content)
  712. return;
  713. EnsureLayoutHasRebuilt();
  714. UpdateBounds();
  715. float deltaTime = Time.unscaledDeltaTime;
  716. Vector2 offset = CalculateOffset(Vector2.zero);
  717. if (!m_Dragging && (offset != Vector2.zero || m_Velocity != Vector2.zero))
  718. {
  719. Vector2 position = m_Content.anchoredPosition;
  720. for (int axis = 0; axis < 2; axis++)
  721. {
  722. // Apply spring physics if movement is elastic and content has an offset from the view.
  723. if (m_MovementType == MovementType.Elastic && offset[axis] != 0)
  724. {
  725. float speed = m_Velocity[axis];
  726. float smoothTime = m_Elasticity;
  727. if (m_Scrolling)
  728. smoothTime *= 3.0f;
  729. position[axis] = Mathf.SmoothDamp(m_Content.anchoredPosition[axis], m_Content.anchoredPosition[axis] + offset[axis], ref speed, smoothTime, Mathf.Infinity, deltaTime);
  730. if (Mathf.Abs(speed) < 1)
  731. speed = 0;
  732. m_Velocity[axis] = speed;
  733. }
  734. // Else move content according to velocity with deceleration applied.
  735. else if (m_Inertia)
  736. {
  737. m_Velocity[axis] *= Mathf.Pow(m_DecelerationRate, deltaTime);
  738. if (Mathf.Abs(m_Velocity[axis]) < 1)
  739. m_Velocity[axis] = 0;
  740. position[axis] += m_Velocity[axis] * deltaTime;
  741. }
  742. // If we have neither elaticity or friction, there shouldn't be any velocity.
  743. else
  744. {
  745. m_Velocity[axis] = 0;
  746. }
  747. }
  748. if (m_MovementType == MovementType.Clamped)
  749. {
  750. offset = CalculateOffset(position - m_Content.anchoredPosition);
  751. position += offset;
  752. }
  753. SetContentAnchoredPosition(position);
  754. }
  755. if (m_Dragging && m_Inertia)
  756. {
  757. Vector3 newVelocity = (m_Content.anchoredPosition - m_PrevPosition) / deltaTime;
  758. m_Velocity = Vector3.Lerp(m_Velocity, newVelocity, deltaTime * 10);
  759. }
  760. if (m_ViewBounds != m_PrevViewBounds || m_ContentBounds != m_PrevContentBounds || m_Content.anchoredPosition != m_PrevPosition)
  761. {
  762. UpdateScrollbars(offset);
  763. UISystemProfilerApi.AddMarker("ScrollRect.value", this);
  764. m_OnValueChanged.Invoke(normalizedPosition);
  765. UpdatePrevData();
  766. }
  767. UpdateScrollbarVisibility();
  768. m_Scrolling = false;
  769. }
  770. /// <summary>
  771. /// Helper function to update the previous data fields on a ScrollRect. Call this before you change data in the ScrollRect.
  772. /// </summary>
  773. protected void UpdatePrevData()
  774. {
  775. if (m_Content == null)
  776. m_PrevPosition = Vector2.zero;
  777. else
  778. m_PrevPosition = m_Content.anchoredPosition;
  779. m_PrevViewBounds = m_ViewBounds;
  780. m_PrevContentBounds = m_ContentBounds;
  781. }
  782. private void UpdateScrollbars(Vector2 offset)
  783. {
  784. if (m_HorizontalScrollbar)
  785. {
  786. if (m_ContentBounds.size.x > 0)
  787. m_HorizontalScrollbar.size = Mathf.Clamp01((m_ViewBounds.size.x - Mathf.Abs(offset.x)) / m_ContentBounds.size.x);
  788. else
  789. m_HorizontalScrollbar.size = 1;
  790. m_HorizontalScrollbar.value = horizontalNormalizedPosition;
  791. }
  792. if (m_VerticalScrollbar)
  793. {
  794. if (m_ContentBounds.size.y > 0)
  795. m_VerticalScrollbar.size = Mathf.Clamp01((m_ViewBounds.size.y - Mathf.Abs(offset.y)) / m_ContentBounds.size.y);
  796. else
  797. m_VerticalScrollbar.size = 1;
  798. m_VerticalScrollbar.value = verticalNormalizedPosition;
  799. }
  800. }
  801. /// <summary>
  802. /// The scroll position as a Vector2 between (0,0) and (1,1) with (0,0) being the lower left corner.
  803. /// </summary>
  804. /// <example>
  805. /// <code>
  806. /// using UnityEngine;
  807. /// using System.Collections;
  808. /// using UnityEngine.UI; // Required when Using UI elements.
  809. ///
  810. /// public class ExampleClass : MonoBehaviour
  811. /// {
  812. /// public ScrollRect myScrollRect;
  813. /// public Vector2 myPosition = new Vector2(0.5f, 0.5f);
  814. ///
  815. /// public void Start()
  816. /// {
  817. /// //Change the current scroll position.
  818. /// myScrollRect.normalizedPosition = myPosition;
  819. /// }
  820. /// }
  821. /// </code>
  822. /// </example>
  823. public Vector2 normalizedPosition
  824. {
  825. get
  826. {
  827. return new Vector2(horizontalNormalizedPosition, verticalNormalizedPosition);
  828. }
  829. set
  830. {
  831. SetNormalizedPosition(value.x, 0);
  832. SetNormalizedPosition(value.y, 1);
  833. }
  834. }
  835. /// <summary>
  836. /// The horizontal scroll position as a value between 0 and 1, with 0 being at the left.
  837. /// </summary>
  838. /// <example>
  839. /// <code>
  840. /// using UnityEngine;
  841. /// using System.Collections;
  842. /// using UnityEngine.UI; // Required when Using UI elements.
  843. ///
  844. /// public class ExampleClass : MonoBehaviour
  845. /// {
  846. /// public ScrollRect myScrollRect;
  847. /// public Scrollbar newScrollBar;
  848. ///
  849. /// public void Start()
  850. /// {
  851. /// //Change the current horizontal scroll position.
  852. /// myScrollRect.horizontalNormalizedPosition = 0.5f;
  853. /// }
  854. /// }
  855. /// </code>
  856. /// </example>
  857. public float horizontalNormalizedPosition
  858. {
  859. get
  860. {
  861. UpdateBounds();
  862. if ((m_ContentBounds.size.x <= m_ViewBounds.size.x) || Mathf.Approximately(m_ContentBounds.size.x, m_ViewBounds.size.x))
  863. return (m_ViewBounds.min.x > m_ContentBounds.min.x) ? 1 : 0;
  864. return (m_ViewBounds.min.x - m_ContentBounds.min.x) / (m_ContentBounds.size.x - m_ViewBounds.size.x);
  865. }
  866. set
  867. {
  868. SetNormalizedPosition(value, 0);
  869. }
  870. }
  871. /// <summary>
  872. /// The vertical scroll position as a value between 0 and 1, with 0 being at the bottom.
  873. /// </summary>
  874. /// <example>
  875. /// <code>
  876. /// using UnityEngine;
  877. /// using System.Collections;
  878. /// using UnityEngine.UI; // Required when Using UI elements.
  879. ///
  880. /// public class ExampleClass : MonoBehaviour
  881. /// {
  882. /// public ScrollRect myScrollRect;
  883. /// public Scrollbar newScrollBar;
  884. ///
  885. /// public void Start()
  886. /// {
  887. /// //Change the current vertical scroll position.
  888. /// myScrollRect.verticalNormalizedPosition = 0.5f;
  889. /// }
  890. /// }
  891. /// </code>
  892. /// </example>
  893. public float verticalNormalizedPosition
  894. {
  895. get
  896. {
  897. UpdateBounds();
  898. if ((m_ContentBounds.size.y <= m_ViewBounds.size.y) || Mathf.Approximately(m_ContentBounds.size.y, m_ViewBounds.size.y))
  899. return (m_ViewBounds.min.y > m_ContentBounds.min.y) ? 1 : 0;
  900. return (m_ViewBounds.min.y - m_ContentBounds.min.y) / (m_ContentBounds.size.y - m_ViewBounds.size.y);
  901. }
  902. set
  903. {
  904. SetNormalizedPosition(value, 1);
  905. }
  906. }
  907. private void SetHorizontalNormalizedPosition(float value) { SetNormalizedPosition(value, 0); }
  908. private void SetVerticalNormalizedPosition(float value) { SetNormalizedPosition(value, 1); }
  909. /// <summary>
  910. /// >Set the horizontal or vertical scroll position as a value between 0 and 1, with 0 being at the left or at the bottom.
  911. /// </summary>
  912. /// <param name="value">The position to set, between 0 and 1.</param>
  913. /// <param name="axis">The axis to set: 0 for horizontal, 1 for vertical.</param>
  914. protected virtual void SetNormalizedPosition(float value, int axis)
  915. {
  916. EnsureLayoutHasRebuilt();
  917. UpdateBounds();
  918. // How much the content is larger than the view.
  919. float hiddenLength = m_ContentBounds.size[axis] - m_ViewBounds.size[axis];
  920. // Where the position of the lower left corner of the content bounds should be, in the space of the view.
  921. float contentBoundsMinPosition = m_ViewBounds.min[axis] - value * hiddenLength;
  922. // The new content localPosition, in the space of the view.
  923. float newAnchoredPosition = m_Content.anchoredPosition[axis] + contentBoundsMinPosition - m_ContentBounds.min[axis];
  924. Vector3 anchoredPosition = m_Content.anchoredPosition;
  925. if (Mathf.Abs(anchoredPosition[axis] - newAnchoredPosition) > 0.01f)
  926. {
  927. anchoredPosition[axis] = newAnchoredPosition;
  928. m_Content.anchoredPosition = anchoredPosition;
  929. m_Velocity[axis] = 0;
  930. UpdateBounds();
  931. }
  932. }
  933. private static float RubberDelta(float overStretching, float viewSize)
  934. {
  935. return (1 - (1 / ((Mathf.Abs(overStretching) * 0.55f / viewSize) + 1))) * viewSize * Mathf.Sign(overStretching);
  936. }
  937. protected override void OnRectTransformDimensionsChange()
  938. {
  939. SetDirty();
  940. }
  941. private bool hScrollingNeeded
  942. {
  943. get
  944. {
  945. if (Application.isPlaying)
  946. return m_ContentBounds.size.x > m_ViewBounds.size.x + 0.01f;
  947. return true;
  948. }
  949. }
  950. private bool vScrollingNeeded
  951. {
  952. get
  953. {
  954. if (Application.isPlaying)
  955. return m_ContentBounds.size.y > m_ViewBounds.size.y + 0.01f;
  956. return true;
  957. }
  958. }
  959. /// <summary>
  960. /// Called by the layout system.
  961. /// </summary>
  962. public virtual void CalculateLayoutInputHorizontal() {}
  963. /// <summary>
  964. /// Called by the layout system.
  965. /// </summary>
  966. public virtual void CalculateLayoutInputVertical() {}
  967. /// <summary>
  968. /// Called by the layout system.
  969. /// </summary>
  970. public virtual float minWidth { get { return -1; } }
  971. /// <summary>
  972. /// Called by the layout system.
  973. /// </summary>
  974. public virtual float preferredWidth { get { return -1; } }
  975. /// <summary>
  976. /// Called by the layout system.
  977. /// </summary>
  978. public virtual float flexibleWidth { get { return -1; } }
  979. /// <summary>
  980. /// Called by the layout system.
  981. /// </summary>
  982. public virtual float minHeight { get { return -1; } }
  983. /// <summary>
  984. /// Called by the layout system.
  985. /// </summary>
  986. public virtual float preferredHeight { get { return -1; } }
  987. /// <summary>
  988. /// Called by the layout system.
  989. /// </summary>
  990. public virtual float flexibleHeight { get { return -1; } }
  991. /// <summary>
  992. /// Called by the layout system.
  993. /// </summary>
  994. public virtual int layoutPriority { get { return -1; } }
  995. /// <summary>
  996. /// Called by the layout system.
  997. /// </summary>
  998. public virtual void SetLayoutHorizontal()
  999. {
  1000. m_Tracker.Clear();
  1001. UpdateCachedData();
  1002. if (m_HSliderExpand || m_VSliderExpand)
  1003. {
  1004. m_Tracker.Add(this, viewRect,
  1005. DrivenTransformProperties.Anchors |
  1006. DrivenTransformProperties.SizeDelta |
  1007. DrivenTransformProperties.AnchoredPosition);
  1008. // Make view full size to see if content fits.
  1009. viewRect.anchorMin = Vector2.zero;
  1010. viewRect.anchorMax = Vector2.one;
  1011. viewRect.sizeDelta = Vector2.zero;
  1012. viewRect.anchoredPosition = Vector2.zero;
  1013. // Recalculate content layout with this size to see if it fits when there are no scrollbars.
  1014. LayoutRebuilder.ForceRebuildLayoutImmediate(content);
  1015. m_ViewBounds = new Bounds(viewRect.rect.center, viewRect.rect.size);
  1016. m_ContentBounds = GetBounds();
  1017. }
  1018. // If it doesn't fit vertically, enable vertical scrollbar and shrink view horizontally to make room for it.
  1019. if (m_VSliderExpand && vScrollingNeeded)
  1020. {
  1021. viewRect.sizeDelta = new Vector2(-(m_VSliderWidth + m_VerticalScrollbarSpacing), viewRect.sizeDelta.y);
  1022. // Recalculate content layout with this size to see if it fits vertically
  1023. // when there is a vertical scrollbar (which may reflowed the content to make it taller).
  1024. LayoutRebuilder.ForceRebuildLayoutImmediate(content);
  1025. m_ViewBounds = new Bounds(viewRect.rect.center, viewRect.rect.size);
  1026. m_ContentBounds = GetBounds();
  1027. }
  1028. // If it doesn't fit horizontally, enable horizontal scrollbar and shrink view vertically to make room for it.
  1029. if (m_HSliderExpand && hScrollingNeeded)
  1030. {
  1031. viewRect.sizeDelta = new Vector2(viewRect.sizeDelta.x, -(m_HSliderHeight + m_HorizontalScrollbarSpacing));
  1032. m_ViewBounds = new Bounds(viewRect.rect.center, viewRect.rect.size);
  1033. m_ContentBounds = GetBounds();
  1034. }
  1035. // If the vertical slider didn't kick in the first time, and the horizontal one did,
  1036. // we need to check again if the vertical slider now needs to kick in.
  1037. // If it doesn't fit vertically, enable vertical scrollbar and shrink view horizontally to make room for it.
  1038. if (m_VSliderExpand && vScrollingNeeded && viewRect.sizeDelta.x == 0 && viewRect.sizeDelta.y < 0)
  1039. {
  1040. viewRect.sizeDelta = new Vector2(-(m_VSliderWidth + m_VerticalScrollbarSpacing), viewRect.sizeDelta.y);
  1041. }
  1042. }
  1043. /// <summary>
  1044. /// Called by the layout system.
  1045. /// </summary>
  1046. public virtual void SetLayoutVertical()
  1047. {
  1048. UpdateScrollbarLayout();
  1049. m_ViewBounds = new Bounds(viewRect.rect.center, viewRect.rect.size);
  1050. m_ContentBounds = GetBounds();
  1051. }
  1052. void UpdateScrollbarVisibility()
  1053. {
  1054. UpdateOneScrollbarVisibility(vScrollingNeeded, m_Vertical, m_VerticalScrollbarVisibility, m_VerticalScrollbar);
  1055. UpdateOneScrollbarVisibility(hScrollingNeeded, m_Horizontal, m_HorizontalScrollbarVisibility, m_HorizontalScrollbar);
  1056. }
  1057. private static void UpdateOneScrollbarVisibility(bool xScrollingNeeded, bool xAxisEnabled, ScrollbarVisibility scrollbarVisibility, Scrollbar scrollbar)
  1058. {
  1059. if (scrollbar)
  1060. {
  1061. if (scrollbarVisibility == ScrollbarVisibility.Permanent)
  1062. {
  1063. if (scrollbar.gameObject.activeSelf != xAxisEnabled)
  1064. scrollbar.gameObject.SetActive(xAxisEnabled);
  1065. }
  1066. else
  1067. {
  1068. if (scrollbar.gameObject.activeSelf != xScrollingNeeded)
  1069. scrollbar.gameObject.SetActive(xScrollingNeeded);
  1070. }
  1071. }
  1072. }
  1073. void UpdateScrollbarLayout()
  1074. {
  1075. if (m_VSliderExpand && m_HorizontalScrollbar)
  1076. {
  1077. m_Tracker.Add(this, m_HorizontalScrollbarRect,
  1078. DrivenTransformProperties.AnchorMinX |
  1079. DrivenTransformProperties.AnchorMaxX |
  1080. DrivenTransformProperties.SizeDeltaX |
  1081. DrivenTransformProperties.AnchoredPositionX);
  1082. m_HorizontalScrollbarRect.anchorMin = new Vector2(0, m_HorizontalScrollbarRect.anchorMin.y);
  1083. m_HorizontalScrollbarRect.anchorMax = new Vector2(1, m_HorizontalScrollbarRect.anchorMax.y);
  1084. m_HorizontalScrollbarRect.anchoredPosition = new Vector2(0, m_HorizontalScrollbarRect.anchoredPosition.y);
  1085. if (vScrollingNeeded)
  1086. m_HorizontalScrollbarRect.sizeDelta = new Vector2(-(m_VSliderWidth + m_VerticalScrollbarSpacing), m_HorizontalScrollbarRect.sizeDelta.y);
  1087. else
  1088. m_HorizontalScrollbarRect.sizeDelta = new Vector2(0, m_HorizontalScrollbarRect.sizeDelta.y);
  1089. }
  1090. if (m_HSliderExpand && m_VerticalScrollbar)
  1091. {
  1092. m_Tracker.Add(this, m_VerticalScrollbarRect,
  1093. DrivenTransformProperties.AnchorMinY |
  1094. DrivenTransformProperties.AnchorMaxY |
  1095. DrivenTransformProperties.SizeDeltaY |
  1096. DrivenTransformProperties.AnchoredPositionY);
  1097. m_VerticalScrollbarRect.anchorMin = new Vector2(m_VerticalScrollbarRect.anchorMin.x, 0);
  1098. m_VerticalScrollbarRect.anchorMax = new Vector2(m_VerticalScrollbarRect.anchorMax.x, 1);
  1099. m_VerticalScrollbarRect.anchoredPosition = new Vector2(m_VerticalScrollbarRect.anchoredPosition.x, 0);
  1100. if (hScrollingNeeded)
  1101. m_VerticalScrollbarRect.sizeDelta = new Vector2(m_VerticalScrollbarRect.sizeDelta.x, -(m_HSliderHeight + m_HorizontalScrollbarSpacing));
  1102. else
  1103. m_VerticalScrollbarRect.sizeDelta = new Vector2(m_VerticalScrollbarRect.sizeDelta.x, 0);
  1104. }
  1105. }
  1106. /// <summary>
  1107. /// Calculate the bounds the ScrollRect should be using.
  1108. /// </summary>
  1109. protected void UpdateBounds()
  1110. {
  1111. m_ViewBounds = new Bounds(viewRect.rect.center, viewRect.rect.size);
  1112. m_ContentBounds = GetBounds();
  1113. if (m_Content == null)
  1114. return;
  1115. Vector3 contentSize = m_ContentBounds.size;
  1116. Vector3 contentPos = m_ContentBounds.center;
  1117. var contentPivot = m_Content.pivot;
  1118. AdjustBounds(ref m_ViewBounds, ref contentPivot, ref contentSize, ref contentPos);
  1119. m_ContentBounds.size = contentSize;
  1120. m_ContentBounds.center = contentPos;
  1121. if (movementType == MovementType.Clamped)
  1122. {
  1123. // Adjust content so that content bounds bottom (right side) is never higher (to the left) than the view bounds bottom (right side).
  1124. // top (left side) is never lower (to the right) than the view bounds top (left side).
  1125. // All this can happen if content has shrunk.
  1126. // This works because content size is at least as big as view size (because of the call to InternalUpdateBounds above).
  1127. Vector2 delta = Vector2.zero;
  1128. if (m_ViewBounds.max.x > m_ContentBounds.max.x)
  1129. {
  1130. delta.x = Math.Min(m_ViewBounds.min.x - m_ContentBounds.min.x, m_ViewBounds.max.x - m_ContentBounds.max.x);
  1131. }
  1132. else if (m_ViewBounds.min.x < m_ContentBounds.min.x)
  1133. {
  1134. delta.x = Math.Max(m_ViewBounds.min.x - m_ContentBounds.min.x, m_ViewBounds.max.x - m_ContentBounds.max.x);
  1135. }
  1136. if (m_ViewBounds.min.y < m_ContentBounds.min.y)
  1137. {
  1138. delta.y = Math.Max(m_ViewBounds.min.y - m_ContentBounds.min.y, m_ViewBounds.max.y - m_ContentBounds.max.y);
  1139. }
  1140. else if (m_ViewBounds.max.y > m_ContentBounds.max.y)
  1141. {
  1142. delta.y = Math.Min(m_ViewBounds.min.y - m_ContentBounds.min.y, m_ViewBounds.max.y - m_ContentBounds.max.y);
  1143. }
  1144. if (delta.sqrMagnitude > float.Epsilon)
  1145. {
  1146. contentPos = m_Content.anchoredPosition + delta;
  1147. if (!m_Horizontal)
  1148. contentPos.x = m_Content.anchoredPosition.x;
  1149. if (!m_Vertical)
  1150. contentPos.y = m_Content.anchoredPosition.y;
  1151. AdjustBounds(ref m_ViewBounds, ref contentPivot, ref contentSize, ref contentPos);
  1152. }
  1153. }
  1154. }
  1155. internal static void AdjustBounds(ref Bounds viewBounds, ref Vector2 contentPivot, ref Vector3 contentSize, ref Vector3 contentPos)
  1156. {
  1157. // Make sure content bounds are at least as large as view by adding padding if not.
  1158. // One might think at first that if the content is smaller than the view, scrolling should be allowed.
  1159. // However, that's not how scroll views normally work.
  1160. // Scrolling is *only* possible when content is *larger* than view.
  1161. // We use the pivot of the content rect to decide in which directions the content bounds should be expanded.
  1162. // E.g. if pivot is at top, bounds are expanded downwards.
  1163. // This also works nicely when ContentSizeFitter is used on the content.
  1164. Vector3 excess = viewBounds.size - contentSize;
  1165. if (excess.x > 0)
  1166. {
  1167. contentPos.x -= excess.x * (contentPivot.x - 0.5f);
  1168. contentSize.x = viewBounds.size.x;
  1169. }
  1170. if (excess.y > 0)
  1171. {
  1172. contentPos.y -= excess.y * (contentPivot.y - 0.5f);
  1173. contentSize.y = viewBounds.size.y;
  1174. }
  1175. }
  1176. private readonly Vector3[] m_Corners = new Vector3[4];
  1177. private Bounds GetBounds()
  1178. {
  1179. if (m_Content == null)
  1180. return new Bounds();
  1181. m_Content.GetWorldCorners(m_Corners);
  1182. var viewWorldToLocalMatrix = viewRect.worldToLocalMatrix;
  1183. return InternalGetBounds(m_Corners, ref viewWorldToLocalMatrix);
  1184. }
  1185. internal static Bounds InternalGetBounds(Vector3[] corners, ref Matrix4x4 viewWorldToLocalMatrix)
  1186. {
  1187. var vMin = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
  1188. var vMax = new Vector3(float.MinValue, float.MinValue, float.MinValue);
  1189. for (int j = 0; j < 4; j++)
  1190. {
  1191. Vector3 v = viewWorldToLocalMatrix.MultiplyPoint3x4(corners[j]);
  1192. vMin = Vector3.Min(v, vMin);
  1193. vMax = Vector3.Max(v, vMax);
  1194. }
  1195. var bounds = new Bounds(vMin, Vector3.zero);
  1196. bounds.Encapsulate(vMax);
  1197. return bounds;
  1198. }
  1199. private Vector2 CalculateOffset(Vector2 delta)
  1200. {
  1201. return InternalCalculateOffset(ref m_ViewBounds, ref m_ContentBounds, m_Horizontal, m_Vertical, m_MovementType, ref delta);
  1202. }
  1203. internal static Vector2 InternalCalculateOffset(ref Bounds viewBounds, ref Bounds contentBounds, bool horizontal, bool vertical, MovementType movementType, ref Vector2 delta)
  1204. {
  1205. Vector2 offset = Vector2.zero;
  1206. if (movementType == MovementType.Unrestricted)
  1207. return offset;
  1208. Vector2 min = contentBounds.min;
  1209. Vector2 max = contentBounds.max;
  1210. // min/max offset extracted to check if approximately 0 and avoid recalculating layout every frame (case 1010178)
  1211. if (horizontal)
  1212. {
  1213. min.x += delta.x;
  1214. max.x += delta.x;
  1215. float maxOffset = viewBounds.max.x - max.x;
  1216. float minOffset = viewBounds.min.x - min.x;
  1217. if (minOffset < -0.001f)
  1218. offset.x = minOffset;
  1219. else if (maxOffset > 0.001f)
  1220. offset.x = maxOffset;
  1221. }
  1222. if (vertical)
  1223. {
  1224. min.y += delta.y;
  1225. max.y += delta.y;
  1226. float maxOffset = viewBounds.max.y - max.y;
  1227. float minOffset = viewBounds.min.y - min.y;
  1228. if (maxOffset > 0.001f)
  1229. offset.y = maxOffset;
  1230. else if (minOffset < -0.001f)
  1231. offset.y = minOffset;
  1232. }
  1233. return offset;
  1234. }
  1235. /// <summary>
  1236. /// Override to alter or add to the code that keeps the appearance of the scroll rect synced with its data.
  1237. /// </summary>
  1238. protected void SetDirty()
  1239. {
  1240. if (!IsActive())
  1241. return;
  1242. LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
  1243. }
  1244. /// <summary>
  1245. /// Override to alter or add to the code that caches data to avoid repeated heavy operations.
  1246. /// </summary>
  1247. protected void SetDirtyCaching()
  1248. {
  1249. if (!IsActive())
  1250. return;
  1251. CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
  1252. LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
  1253. m_ViewRect = null;
  1254. }
  1255. #if UNITY_EDITOR
  1256. protected override void OnValidate()
  1257. {
  1258. SetDirtyCaching();
  1259. }
  1260. #endif
  1261. }
  1262. }