UGUIScrollRect.cs 55 KB

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