diff --git a/DFGUI/Scripts/Controls/dfScrollPanel.cs b/DFGUI/Scripts/Controls/dfScrollPanel.cs
index bf0d43c..34d5158 100644
--- a/DFGUI/Scripts/Controls/dfScrollPanel.cs
+++ b/DFGUI/Scripts/Controls/dfScrollPanel.cs
@@ -1,4 +1,4 @@
-/* Copyright 2013 Daikon Forge */
+/* Copyright 2013 Daikon Forge */
using System;
using System.Linq;
@@ -11,472 +11,474 @@
///
/// Implements a scrollable control container
///
-[dfCategory( "Basic Controls" )]
-[dfTooltip( "Implements a scrollable control container" )]
-[dfHelp( "http://www.daikonforge.com/docs/df-gui/classdf_scroll_panel.html" )]
+[dfCategory("Basic Controls")]
+[dfTooltip("Implements a scrollable control container")]
+[dfHelp("http://www.daikonforge.com/docs/df-gui/classdf_scroll_panel.html")]
[Serializable]
[ExecuteInEditMode]
-[AddComponentMenu( "Daikon Forge/User Interface/Containers/Scrollable Panel" )]
+[AddComponentMenu("Daikon Forge/User Interface/Containers/Scrollable Panel")]
public class dfScrollPanel : dfControl
{
- #region Public events
+ #region Public events
- ///
- /// Raised when the value of the property has changed
- ///
- public event PropertyChangedEventHandler ScrollPositionChanged;
+ ///
+ /// Raised when the value of the property has changed
+ ///
+ public event PropertyChangedEventHandler ScrollPositionChanged;
- #endregion
+ #endregion
- #region Enumerations
+ #region Enumerations
- ///
- /// Specifies the direction to arrange controls when flow layout is used
- ///
- public enum LayoutDirection : int
- {
- ///
- /// Controls will be arranged horizontally
- ///
- Horizontal,
- ///
- /// Controls will be arranged vertically
- ///
- Vertical
- }
+ ///
+ /// Specifies the direction to arrange controls when flow layout is used
+ ///
+ public enum LayoutDirection : int
+ {
+ ///
+ /// Controls will be arranged horizontally
+ ///
+ Horizontal,
+ ///
+ /// Controls will be arranged vertically
+ ///
+ Vertical
+ }
- #endregion
+ #endregion
- #region Serialized protected members
+ #region Serialized protected members
- [SerializeField]
- protected dfAtlas atlas;
+ [SerializeField]
+ protected dfAtlas atlas;
- [SerializeField]
- protected string backgroundSprite;
+ [SerializeField]
+ protected string backgroundSprite;
- [SerializeField]
- protected Color32 backgroundColor = UnityEngine.Color.white;
+ [SerializeField]
+ protected Color32 backgroundColor = UnityEngine.Color.white;
- [SerializeField]
- protected bool autoReset = true;
+ [SerializeField]
+ protected bool autoReset = true;
- [SerializeField]
- protected bool autoLayout = false;
+ [SerializeField]
+ protected bool autoLayout = false;
- [SerializeField]
- protected RectOffset scrollPadding = new RectOffset();
-
- [SerializeField]
- protected RectOffset flowPadding = new RectOffset();
-
- [SerializeField]
- protected LayoutDirection flowDirection = LayoutDirection.Horizontal;
-
- [SerializeField]
- protected bool wrapLayout = false;
-
- [SerializeField]
- protected Vector2 scrollPosition = Vector2.zero;
-
- [SerializeField]
- protected int scrollWheelAmount = 10;
-
- [SerializeField]
- protected dfScrollbar horzScroll;
-
- [SerializeField]
- protected dfScrollbar vertScroll;
-
- [SerializeField]
- protected dfControlOrientation wheelDirection = dfControlOrientation.Horizontal;
-
- [SerializeField]
- protected bool scrollWithArrowKeys = false;
-
- [SerializeField]
- protected bool useScrollMomentum = false;
-
- [SerializeField]
- protected bool useVirtualScrolling = false;
-
- [SerializeField]
- protected bool autoFitVirtualTiles = true;
-
- [SerializeField]
- protected dfControl virtualScrollingTile;
-
- #endregion
-
- #region Private instance variables
-
- private bool initialized = false;
- private bool resetNeeded = false;
- private bool scrolling = false;
- private bool isMouseDown = false;
- private Vector2 touchStartPosition = Vector2.zero;
- private Vector2 scrollMomentum = Vector2.zero;
-
- ///
- /// Contains information about virtual data stored for this virtualized dfScrollPanel.
- /// Must be cast to , where T is the Type in the provided as the backing list
- /// in order to be of any use.
- ///
- private object virtualScrollData;
-
- #endregion
-
- #region Public properties
-
- ///
- /// Gets or sets whether scrolling with the mousewheel or touch swipe will
- /// add a momentum effect to scrolling
- ///
- public bool UseScrollMomentum
- {
- get { return this.useScrollMomentum; }
- set { this.useScrollMomentum = value; scrollMomentum = Vector2.zero; }
- }
-
- ///
- /// Set to TRUE if you want the to scroll when the
- /// user presses the arrow keys.
- ///
- public bool ScrollWithArrowKeys
- {
- get { return scrollWithArrowKeys; }
- set { scrollWithArrowKeys = value; }
- }
-
- ///
- /// The Texture Atlas containing the images used by this control
- ///
- public dfAtlas Atlas
- {
- get
- {
- if( this.atlas == null )
- {
- var view = GetManager();
- if( view != null )
- {
- return this.atlas = view.DefaultAtlas;
- }
- }
- return this.atlas;
- }
- set
- {
- if( !dfAtlas.Equals( value, atlas ) )
- {
- this.atlas = value;
- Invalidate();
- }
- }
- }
-
- ///
- /// The name of the image in the that will be used to
- /// render the background of this control
- ///
- public string BackgroundSprite
- {
- get { return backgroundSprite; }
- set
- {
- if( value != backgroundSprite )
- {
- backgroundSprite = value;
- Invalidate();
- }
- }
- }
-
- ///
- /// Gets or set the color that will be applied to the background sprite
- ///
- public Color32 BackgroundColor
- {
- get { return backgroundColor; }
- set
- {
- if( !Color32.Equals( value, backgroundColor ) )
- {
- backgroundColor = value;
- Invalidate();
- }
- }
- }
-
- ///
- /// Gets or sets whether the will automatically
- /// reset the scrolling region
- ///
- public bool AutoReset
- {
- get { return this.autoReset; }
- set
- {
- if( value != this.autoReset )
- {
- this.autoReset = value;
- Reset();
- }
- }
- }
-
- ///
- /// Gets or sets the amount of padding that will be applied when arranging
- /// child controls if the property is set to TRUE
- ///
- public RectOffset ScrollPadding
- {
- get
- {
- if( this.scrollPadding == null )
- this.scrollPadding = new RectOffset();
- return this.scrollPadding;
- }
- set
- {
- value = value.ConstrainPadding();
- if( !RectOffset.Equals( value, this.scrollPadding ) )
- {
- this.scrollPadding = value;
- if( AutoReset || AutoLayout )
- Reset();
- }
- }
- }
-
- ///
- /// Gets or sets whether child controls will be automatically arranged
- ///
- public bool AutoLayout
- {
- get { return this.autoLayout; }
- set
- {
- if( value != this.autoLayout )
- {
- this.autoLayout = value;
- if( AutoReset || AutoLayout )
- Reset();
- }
- }
- }
-
- ///
- /// Gets or sets whether controls that lie outside of this container's
- /// boundaries will be "wrapped" to the next row or column when using AutoLayout
- ///
- public bool WrapLayout
- {
- get { return this.wrapLayout; }
- set
- {
- if( value != this.wrapLayout )
- {
- this.wrapLayout = value;
- Reset();
- }
- }
- }
-
- ///
- /// Gets or sets the direction in which child controls will be arranged
- /// when using AutoLayout
- ///
- public LayoutDirection FlowDirection
- {
- get { return this.flowDirection; }
- set
- {
- if( value != this.flowDirection )
- {
- this.flowDirection = value;
- Reset();
- }
- }
- }
-
- ///
- /// Gets or sets the amount of padding that will be applied to each control
- /// when arranging child controls using AutoLayout
- ///
- public RectOffset FlowPadding
- {
- get
- {
- if( this.flowPadding == null )
- this.flowPadding = new RectOffset();
- return this.flowPadding;
- }
- set
- {
- value = value.ConstrainPadding();
- if( !RectOffset.Equals( value, this.flowPadding ) )
- {
- this.flowPadding = value;
- Reset();
- }
- }
- }
-
- ///
- /// Gets or sets the upper-left position of the viewport relative
- /// to the entire scrollable area
- ///
- public Vector2 ScrollPosition
- {
- get { return this.scrollPosition; }
- set
- {
-
- var viewSize = calculateViewSize();
- var clientSize = new Vector2( this.size.x - this.scrollPadding.horizontal, this.size.y - this.scrollPadding.vertical );
-
- value = Vector2.Min( viewSize - clientSize, value );
- value = Vector2.Max( Vector2.zero, value );
- value = value.RoundToInt();
-
- if( ( value - this.scrollPosition ).sqrMagnitude > float.Epsilon )
- {
-
- var delta = value - scrollPosition;
- this.scrollPosition = value;
-
- scrollChildControls( delta );
- updateScrollbars();
-
- }
-
- OnScrollPositionChanged();
-
- }
- }
-
- ///
- /// Gets or sets the distance in pixels that the scroll panel will be scrolled when
- /// the user rotates the mouse wheel (this value is overridden by scrollbars, if they
- /// are attached)
- ///
- public int ScrollWheelAmount
- {
- get { return this.scrollWheelAmount; }
- set { this.scrollWheelAmount = value; }
- }
-
- ///
- /// Gets or sets a reference the the instance
- /// that is used to scroll this container horizontally
- ///
- public dfScrollbar HorzScrollbar
- {
- get { return this.horzScroll; }
- set
- {
- horzScroll = value;
- updateScrollbars();
- }
- }
-
- ///
- /// Gets or sets a reference the the instance
- /// that is used to scroll this container vertically
- ///
- public dfScrollbar VertScrollbar
- {
- get { return this.vertScroll; }
- set
- {
- vertScroll = value;
- updateScrollbars();
- }
- }
-
- ///
- /// Indicates the direction to scroll when the user scrolls the mouse wheel
- ///
- public dfControlOrientation WheelScrollDirection
- {
- get { return this.wheelDirection; }
- set { this.wheelDirection = value; }
- }
-
- ///
- /// Gets or sets whether or not the will use a virtual scrolling
- /// algorithm for recycling control tiles.
- ///
- public bool UseVirtualScrolling
- {
- get { return useVirtualScrolling; }
- set
- {
- useVirtualScrolling = value;
-
- if ( !value )
- {
- VirtualScrollingTile = null;
- }
- }
- }
-
- ///
- /// Gets or sets whether or not virtualized tiles will be automatically stretched to fit horizontally for vertically
- /// scrolling , or vertically for horizontally scrolling controls.
- ///
- public bool AutoFitVirtualTiles
- {
- get { return autoFitVirtualTiles; }
- set { autoFitVirtualTiles = value; }
- }
-
- ///
- /// The to be copied and recycled during virtual scrolling.
- /// Attached to this control must be a inherited script that also implements
- ///
- public dfControl VirtualScrollingTile
- {
- get { return ( useVirtualScrolling ) ? virtualScrollingTile : null; }
- set { virtualScrollingTile = ( useVirtualScrolling ) ? value : null; }
- }
-
- #endregion
-
- #region Overrides
-
- ///
- /// Returns the padding used when clipping is enabled and
- /// the renderer is using shader-based clipping
- ///
- ///
- protected internal override RectOffset GetClipPadding()
- {
- return this.scrollPadding ?? dfRectOffsetExtensions.Empty;
- }
-
- protected internal override Plane[] GetClippingPlanes()
- {
-
- if( !ClipChildren )
- return null;
-
- var corners = GetCorners();
-
- var right = transform.TransformDirection( Vector3.right );
- var left = transform.TransformDirection( Vector3.left );
- var up = transform.TransformDirection( Vector3.up );
- var down = transform.TransformDirection( Vector3.down );
-
- var p2u = PixelsToUnits();
- var padding = ScrollPadding;
- corners[ 0 ] += right * padding.left * p2u + down * padding.top * p2u;
- corners[ 1 ] += left * padding.right * p2u + down * padding.top * p2u;
- corners[ 2 ] += right * padding.left * p2u + up * padding.bottom * p2u;
-
- return new Plane[]
+ [SerializeField]
+ protected RectOffset scrollPadding = new RectOffset();
+
+ [SerializeField]
+ protected RectOffset flowPadding = new RectOffset();
+
+ [SerializeField]
+ protected LayoutDirection flowDirection = LayoutDirection.Horizontal;
+
+ [SerializeField]
+ protected bool wrapLayout = false;
+
+ [SerializeField]
+ protected Vector2 scrollPosition = Vector2.zero;
+
+ [SerializeField]
+ protected int scrollWheelAmount = 10;
+
+ [SerializeField]
+ protected dfScrollbar horzScroll;
+
+ [SerializeField]
+ protected dfScrollbar vertScroll;
+
+ [SerializeField]
+ protected dfControlOrientation wheelDirection = dfControlOrientation.Horizontal;
+
+ [SerializeField]
+ protected bool scrollWithArrowKeys = false;
+
+ [SerializeField]
+ protected bool useScrollMomentum = false;
+
+ [SerializeField]
+ protected bool useVirtualScrolling = false;
+
+ [SerializeField]
+ protected bool autoFitVirtualTiles = true;
+
+ [SerializeField]
+ protected dfControl virtualScrollingTile;
+
+ #endregion
+
+ #region Private instance variables
+
+ private bool initialized = false;
+ private bool resetNeeded = false;
+ private bool scrolling = false;
+ private bool isMouseDown = false;
+ private Vector2 touchStartPosition = Vector2.zero;
+ private Vector2 scrollMomentum = Vector2.zero;
+
+ ///
+ /// Contains information about virtual data stored for this virtualized dfScrollPanel.
+ /// Must be cast to , where T is the Type in the provided as the backing list
+ /// in order to be of any use.
+ ///
+ private object virtualScrollData;
+
+ private float lastWidth=0;
+
+ #endregion
+
+ #region Public properties
+
+ ///
+ /// Gets or sets whether scrolling with the mousewheel or touch swipe will
+ /// add a momentum effect to scrolling
+ ///
+ public bool UseScrollMomentum
+ {
+ get { return this.useScrollMomentum; }
+ set { this.useScrollMomentum = value; scrollMomentum = Vector2.zero; }
+ }
+
+ ///
+ /// Set to TRUE if you want the to scroll when the
+ /// user presses the arrow keys.
+ ///
+ public bool ScrollWithArrowKeys
+ {
+ get { return scrollWithArrowKeys; }
+ set { scrollWithArrowKeys = value; }
+ }
+
+ ///
+ /// The Texture Atlas containing the images used by this control
+ ///
+ public dfAtlas Atlas
+ {
+ get
+ {
+ if (this.atlas == null)
+ {
+ var view = GetManager();
+ if (view != null)
+ {
+ return this.atlas = view.DefaultAtlas;
+ }
+ }
+ return this.atlas;
+ }
+ set
+ {
+ if (!dfAtlas.Equals(value, atlas))
+ {
+ this.atlas = value;
+ Invalidate();
+ }
+ }
+ }
+
+ ///
+ /// The name of the image in the that will be used to
+ /// render the background of this control
+ ///
+ public string BackgroundSprite
+ {
+ get { return backgroundSprite; }
+ set
+ {
+ if (value != backgroundSprite)
+ {
+ backgroundSprite = value;
+ Invalidate();
+ }
+ }
+ }
+
+ ///
+ /// Gets or set the color that will be applied to the background sprite
+ ///
+ public Color32 BackgroundColor
+ {
+ get { return backgroundColor; }
+ set
+ {
+ if (!Color32.Equals(value, backgroundColor))
+ {
+ backgroundColor = value;
+ Invalidate();
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets whether the will automatically
+ /// reset the scrolling region
+ ///
+ public bool AutoReset
+ {
+ get { return this.autoReset; }
+ set
+ {
+ if (value != this.autoReset)
+ {
+ this.autoReset = value;
+ Reset();
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the amount of padding that will be applied when arranging
+ /// child controls if the property is set to TRUE
+ ///
+ public RectOffset ScrollPadding
+ {
+ get
+ {
+ if (this.scrollPadding == null)
+ this.scrollPadding = new RectOffset();
+ return this.scrollPadding;
+ }
+ set
+ {
+ value = value.ConstrainPadding();
+ if (!RectOffset.Equals(value, this.scrollPadding))
+ {
+ this.scrollPadding = value;
+ if (AutoReset || AutoLayout)
+ Reset();
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets whether child controls will be automatically arranged
+ ///
+ public bool AutoLayout
+ {
+ get { return this.autoLayout; }
+ set
+ {
+ if (value != this.autoLayout)
+ {
+ this.autoLayout = value;
+ if (AutoReset || AutoLayout)
+ Reset();
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets whether controls that lie outside of this container's
+ /// boundaries will be "wrapped" to the next row or column when using AutoLayout
+ ///
+ public bool WrapLayout
+ {
+ get { return this.wrapLayout; }
+ set
+ {
+ if (value != this.wrapLayout)
+ {
+ this.wrapLayout = value;
+ Reset();
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the direction in which child controls will be arranged
+ /// when using AutoLayout
+ ///
+ public LayoutDirection FlowDirection
+ {
+ get { return this.flowDirection; }
+ set
+ {
+ if (value != this.flowDirection)
+ {
+ this.flowDirection = value;
+ Reset();
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the amount of padding that will be applied to each control
+ /// when arranging child controls using AutoLayout
+ ///
+ public RectOffset FlowPadding
+ {
+ get
+ {
+ if (this.flowPadding == null)
+ this.flowPadding = new RectOffset();
+ return this.flowPadding;
+ }
+ set
+ {
+ value = value.ConstrainPadding();
+ if (!RectOffset.Equals(value, this.flowPadding))
+ {
+ this.flowPadding = value;
+ Reset();
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the upper-left position of the viewport relative
+ /// to the entire scrollable area
+ ///
+ public Vector2 ScrollPosition
+ {
+ get { return this.scrollPosition; }
+ set
+ {
+
+ var viewSize = calculateViewSize();
+ var clientSize = new Vector2(this.size.x - this.scrollPadding.horizontal, this.size.y - this.scrollPadding.vertical);
+
+ value = Vector2.Min(viewSize - clientSize, value);
+ value = Vector2.Max(Vector2.zero, value);
+ value = value.RoundToInt();
+
+ if ((value - this.scrollPosition).sqrMagnitude > float.Epsilon)
+ {
+
+ var delta = value - scrollPosition;
+ this.scrollPosition = value;
+
+ scrollChildControls(delta);
+ updateScrollbars();
+
+ }
+
+ OnScrollPositionChanged();
+
+ }
+ }
+
+ ///
+ /// Gets or sets the distance in pixels that the scroll panel will be scrolled when
+ /// the user rotates the mouse wheel (this value is overridden by scrollbars, if they
+ /// are attached)
+ ///
+ public int ScrollWheelAmount
+ {
+ get { return this.scrollWheelAmount; }
+ set { this.scrollWheelAmount = value; }
+ }
+
+ ///
+ /// Gets or sets a reference the the instance
+ /// that is used to scroll this container horizontally
+ ///
+ public dfScrollbar HorzScrollbar
+ {
+ get { return this.horzScroll; }
+ set
+ {
+ horzScroll = value;
+ updateScrollbars();
+ }
+ }
+
+ ///
+ /// Gets or sets a reference the the instance
+ /// that is used to scroll this container vertically
+ ///
+ public dfScrollbar VertScrollbar
+ {
+ get { return this.vertScroll; }
+ set
+ {
+ vertScroll = value;
+ updateScrollbars();
+ }
+ }
+
+ ///
+ /// Indicates the direction to scroll when the user scrolls the mouse wheel
+ ///
+ public dfControlOrientation WheelScrollDirection
+ {
+ get { return this.wheelDirection; }
+ set { this.wheelDirection = value; }
+ }
+
+ ///
+ /// Gets or sets whether or not the will use a virtual scrolling
+ /// algorithm for recycling control tiles.
+ ///
+ public bool UseVirtualScrolling
+ {
+ get { return useVirtualScrolling; }
+ set
+ {
+ useVirtualScrolling = value;
+
+ if (!value)
+ {
+ VirtualScrollingTile = null;
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets whether or not virtualized tiles will be automatically stretched to fit horizontally for vertically
+ /// scrolling , or vertically for horizontally scrolling controls.
+ ///
+ public bool AutoFitVirtualTiles
+ {
+ get { return autoFitVirtualTiles; }
+ set { autoFitVirtualTiles = value; }
+ }
+
+ ///
+ /// The to be copied and recycled during virtual scrolling.
+ /// Attached to this control must be a inherited script that also implements
+ ///
+ public dfControl VirtualScrollingTile
+ {
+ get { return (useVirtualScrolling) ? virtualScrollingTile : null; }
+ set { virtualScrollingTile = (useVirtualScrolling) ? value : null; }
+ }
+
+ #endregion
+
+ #region Overrides
+
+ ///
+ /// Returns the padding used when clipping is enabled and
+ /// the renderer is using shader-based clipping
+ ///
+ ///
+ protected internal override RectOffset GetClipPadding()
+ {
+ return this.scrollPadding ?? dfRectOffsetExtensions.Empty;
+ }
+
+ protected internal override Plane[] GetClippingPlanes()
+ {
+
+ if (!ClipChildren)
+ return null;
+
+ var corners = GetCorners();
+
+ var right = transform.TransformDirection(Vector3.right);
+ var left = transform.TransformDirection(Vector3.left);
+ var up = transform.TransformDirection(Vector3.up);
+ var down = transform.TransformDirection(Vector3.down);
+
+ var p2u = PixelsToUnits();
+ var padding = ScrollPadding;
+ corners[0] += right * padding.left * p2u + down * padding.top * p2u;
+ corners[1] += left * padding.right * p2u + down * padding.top * p2u;
+ corners[2] += right * padding.left * p2u + up * padding.bottom * p2u;
+
+ return new Plane[]
{
new Plane( right, corners[ 0 ] ),
new Plane( left, corners[ 1 ] ),
@@ -484,1330 +486,1372 @@ protected internal override Plane[] GetClippingPlanes()
new Plane( down, corners[ 0 ] )
};
- }
-
- public override bool CanFocus
- {
- get
- {
- if( this.IsEnabled && this.IsVisible )
- return true;
- return base.CanFocus;
- }
- }
-
- public override void OnDestroy()
- {
-
- if( horzScroll != null )
- {
- horzScroll.ValueChanged -= horzScroll_ValueChanged;
- }
-
- if( vertScroll != null )
- {
- vertScroll.ValueChanged -= vertScroll_ValueChanged;
- }
-
- horzScroll = null;
- vertScroll = null;
-
- }
-
- public override void Update()
- {
-
- base.Update();
-
- if( useScrollMomentum && !isMouseDown && scrollMomentum.magnitude > 0.25f )
- {
- ScrollPosition += scrollMomentum;
- scrollMomentum *= ( 0.95f - Time.deltaTime );
- }
-
- if( isControlInvalidated )
- {
-
- if( autoLayout && IsVisible )
- {
- AutoArrange();
- updateScrollbars();
- }
-
- }
-
- }
-
- public override void LateUpdate()
- {
-
- base.LateUpdate();
-
- // HACK: Need to perform initialization after all dependant objects
- initialize();
+ }
- if( resetNeeded && IsVisible )
- {
+ public override bool CanFocus
+ {
+ get
+ {
+ if (this.IsEnabled && this.IsVisible)
+ return true;
+ return base.CanFocus;
+ }
+ }
- resetNeeded = false;
+ public override void OnDestroy()
+ {
- if( autoReset || autoLayout )
- {
- Reset();
- }
+ if (horzScroll != null)
+ {
+ horzScroll.ValueChanged -= horzScroll_ValueChanged;
+ }
- }
+ if (vertScroll != null)
+ {
+ vertScroll.ValueChanged -= vertScroll_ValueChanged;
+ }
- }
+ horzScroll = null;
+ vertScroll = null;
- public override void OnEnable()
- {
+ }
- base.OnEnable();
+ public override void Update()
+ {
- if( this.size == Vector2.zero )
- {
- SuspendLayout();
- var camera = GetCamera();
- Size = new Vector3( camera.pixelWidth / 2, camera.pixelHeight / 2 );
- ResumeLayout();
- }
+ base.Update();
- if( autoLayout )
- {
- AutoArrange();
- }
+ if (useScrollMomentum && !isMouseDown && scrollMomentum.magnitude > 0.25f)
+ {
+ ScrollPosition += scrollMomentum;
+ scrollMomentum *= (0.95f - Time.deltaTime);
+ }
- updateScrollbars();
+ if (isControlInvalidated)
+ {
- }
+ if (autoLayout && IsVisible)
+ {
+ AutoArrange();
+ updateScrollbars();
+ }
- protected internal override void OnIsVisibleChanged()
- {
+ }
- base.OnIsVisibleChanged();
+ }
- if( IsVisible && ( autoReset || autoLayout ) )
- {
- Reset();
- updateScrollbars();
- }
+ public override void LateUpdate()
+ {
- }
+ base.LateUpdate();
- protected internal override void OnSizeChanged()
- {
+ // HACK: Need to perform initialization after all dependant objects
+ initialize();
- base.OnSizeChanged();
+ if (resetNeeded && IsVisible)
+ {
- if( autoReset || autoLayout )
- {
- Reset();
- return;
- }
+ resetNeeded = false;
- var minScrollPos = calculateMinChildPosition();
- if( minScrollPos.x > scrollPadding.left || minScrollPos.y > scrollPadding.top )
- {
+ if (autoReset || autoLayout)
+ {
+ Reset();
+ }
- // Adjust minScrollPos to account for padding and clamp it
- minScrollPos -= new Vector2( scrollPadding.left, scrollPadding.top );
- minScrollPos = Vector2.Max( minScrollPos, Vector2.zero );
+ }
- // Scroll child controls to compensate for container resize
- scrollChildControls( minScrollPos );
+ }
- }
+ public override void OnEnable()
+ {
- updateScrollbars();
+ base.OnEnable();
- }
+ if (this.size == Vector2.zero)
+ {
+ SuspendLayout();
+ var camera = GetCamera();
+ Size = new Vector3(camera.pixelWidth / 2, camera.pixelHeight / 2);
+ ResumeLayout();
+ }
- protected internal override void OnResolutionChanged( Vector2 previousResolution, Vector2 currentResolution )
- {
- base.OnResolutionChanged( previousResolution, currentResolution );
- resetNeeded = AutoLayout || AutoReset;
- }
+ if (autoLayout)
+ {
+ AutoArrange();
+ }
- protected internal override void OnGotFocus( dfFocusEventArgs args )
- {
+ updateScrollbars();
- if( args.Source != this )
- {
- ScrollIntoView( args.Source );
- }
+ }
- base.OnGotFocus( args );
+ protected internal override void OnIsVisibleChanged()
+ {
- }
+ base.OnIsVisibleChanged();
- protected internal override void OnKeyDown( dfKeyEventArgs args )
- {
+ if (IsVisible && (autoReset || autoLayout))
+ {
+ Reset();
+ updateScrollbars();
+ }
- if( !scrollWithArrowKeys || args.Used )
- {
- base.OnKeyDown( args );
- return;
- }
+ }
- var horzAmount = horzScroll != null ? horzScroll.IncrementAmount : 1f;
- var vertAmount = vertScroll != null ? vertScroll.IncrementAmount : 1f;
+ protected internal override void OnSizeChanged()
+ {
- if( args.KeyCode == KeyCode.LeftArrow )
- {
- ScrollPosition += new Vector2( -horzAmount, 0 );
- args.Use();
- }
- else if( args.KeyCode == KeyCode.RightArrow )
- {
- ScrollPosition += new Vector2( horzAmount, 0 );
- args.Use();
- }
- else if( args.KeyCode == KeyCode.UpArrow )
- {
- ScrollPosition += new Vector2( 0, -vertAmount );
- args.Use();
- }
- else if( args.KeyCode == KeyCode.DownArrow )
- {
- ScrollPosition += new Vector2( 0, vertAmount );
- args.Use();
- }
+ base.OnSizeChanged();
- base.OnKeyDown( args );
+ if (autoReset || autoLayout)
+ {
+ Reset();
+ return;
+ }
- }
+ var minScrollPos = calculateMinChildPosition();
+ if (minScrollPos.x > scrollPadding.left || minScrollPos.y > scrollPadding.top)
+ {
- protected internal override void OnMouseEnter( dfMouseEventArgs args )
- {
- base.OnMouseEnter( args );
- touchStartPosition = args.Position;
- }
+ // Adjust minScrollPos to account for padding and clamp it
+ minScrollPos -= new Vector2(scrollPadding.left, scrollPadding.top);
+ minScrollPos = Vector2.Max(minScrollPos, Vector2.zero);
- protected internal override void OnMouseDown( dfMouseEventArgs args )
- {
+ // Scroll child controls to compensate for container resize
+ scrollChildControls(minScrollPos);
- base.OnMouseDown( args );
+ }
- scrollMomentum = Vector2.zero;
- touchStartPosition = args.Position;
- isMouseDown = IsInteractive;
+ updateScrollbars();
- }
+ }
- internal override void OnDragStart( dfDragEventArgs args )
- {
+ protected internal override void OnResolutionChanged(Vector2 previousResolution, Vector2 currentResolution)
+ {
+ base.OnResolutionChanged(previousResolution, currentResolution);
+ resetNeeded = AutoLayout || AutoReset;
+ }
- base.OnDragStart( args );
+ protected internal override void OnGotFocus(dfFocusEventArgs args)
+ {
- scrollMomentum = Vector2.zero;
- if( args.Used )
- {
- isMouseDown = false;
- }
+ if (args.Source != this)
+ {
+ ScrollIntoView(args.Source);
+ }
- }
+ base.OnGotFocus(args);
- protected internal override void OnMouseUp( dfMouseEventArgs args )
- {
- base.OnMouseUp( args );
- isMouseDown = false;
- }
+ }
- protected internal override void OnMouseMove( dfMouseEventArgs args )
- {
+ protected internal override void OnKeyDown(dfKeyEventArgs args)
+ {
- if( args is dfTouchEventArgs || isMouseDown )
- {
+ if (!scrollWithArrowKeys || args.Used)
+ {
+ base.OnKeyDown(args);
+ return;
+ }
- if( !args.Used && ( args.Position - touchStartPosition ).magnitude > 5 )
- {
+ var horzAmount = horzScroll != null ? horzScroll.IncrementAmount : 1f;
+ var vertAmount = vertScroll != null ? vertScroll.IncrementAmount : 1f;
- var delta = args.MoveDelta.Scale( -1, 1 );
+ if (args.KeyCode == KeyCode.LeftArrow)
+ {
+ ScrollPosition += new Vector2(-horzAmount, 0);
+ args.Use();
+ }
+ else if (args.KeyCode == KeyCode.RightArrow)
+ {
+ ScrollPosition += new Vector2(horzAmount, 0);
+ args.Use();
+ }
+ else if (args.KeyCode == KeyCode.UpArrow)
+ {
+ ScrollPosition += new Vector2(0, -vertAmount);
+ args.Use();
+ }
+ else if (args.KeyCode == KeyCode.DownArrow)
+ {
+ ScrollPosition += new Vector2(0, vertAmount);
+ args.Use();
+ }
- // Calculate the effective screen size
- var manager = GetManager();
- var screenSize = manager.GetScreenSize();
+ base.OnKeyDown(args);
- // Obtain a reference to the camera used to render this control
- var renderCamera = manager.RenderCamera;
+ }
- // Scale the movement amount by the difference between the "virtual"
- // screen size and the real screen size
- delta.x = screenSize.x * ( delta.x / renderCamera.pixelWidth );
- delta.y = screenSize.y * ( delta.y / renderCamera.pixelHeight );
+ protected internal override void OnMouseEnter(dfMouseEventArgs args)
+ {
+ base.OnMouseEnter(args);
+ touchStartPosition = args.Position;
+ }
- // Set the new scroll position and momentum
- ScrollPosition += delta;
- scrollMomentum = ( scrollMomentum + delta ) * 0.5f;
+ protected internal override void OnMouseDown(dfMouseEventArgs args)
+ {
- args.Use();
+ base.OnMouseDown(args);
- }
+ scrollMomentum = Vector2.zero;
+ touchStartPosition = args.Position;
+ isMouseDown = IsInteractive;
- }
+ }
- base.OnMouseMove( args );
+ internal override void OnDragStart(dfDragEventArgs args)
+ {
- }
+ base.OnDragStart(args);
- protected internal override void OnMouseWheel( dfMouseEventArgs args )
- {
+ scrollMomentum = Vector2.zero;
+ if (args.Used)
+ {
+ isMouseDown = false;
+ }
- try
- {
+ }
- if( args.Used )
- return;
-
- var amount = wheelDirection == dfControlOrientation.Horizontal
- ? horzScroll != null ? horzScroll.IncrementAmount : scrollWheelAmount
- : vertScroll != null ? vertScroll.IncrementAmount : scrollWheelAmount;
-
- if( wheelDirection == dfControlOrientation.Horizontal )
- {
- ScrollPosition = new Vector2( scrollPosition.x - amount * args.WheelDelta, scrollPosition.y );
- scrollMomentum = new Vector2( -amount * args.WheelDelta, 0 );
- }
- else
- {
- ScrollPosition = new Vector2( scrollPosition.x, scrollPosition.y - amount * args.WheelDelta );
- scrollMomentum = new Vector2( 0, -amount * args.WheelDelta );
- }
-
- args.Use();
- Signal( "OnMouseWheel", this, args );
-
- }
- finally
- {
- base.OnMouseWheel( args );
- }
+ protected internal override void OnMouseUp(dfMouseEventArgs args)
+ {
+ base.OnMouseUp(args);
+ isMouseDown = false;
+ }
- }
+ protected internal override void OnMouseMove(dfMouseEventArgs args)
+ {
- protected internal override void OnControlAdded( dfControl child )
- {
+ if (args is dfTouchEventArgs || isMouseDown)
+ {
- base.OnControlAdded( child );
+ if (!args.Used && (args.Position - touchStartPosition).magnitude > 5)
+ {
- attachEvents( child );
+ var delta = args.MoveDelta.Scale(-1, 1);
- if( autoLayout )
- {
- AutoArrange();
- }
+ // Calculate the effective screen size
+ var manager = GetManager();
+ var screenSize = manager.GetScreenSize();
- }
+ // Obtain a reference to the camera used to render this control
+ var renderCamera = manager.RenderCamera;
- protected internal override void OnControlRemoved( dfControl child )
- {
+ // Scale the movement amount by the difference between the "virtual"
+ // screen size and the real screen size
+ delta.x = screenSize.x * (delta.x / renderCamera.pixelWidth);
+ delta.y = screenSize.y * (delta.y / renderCamera.pixelHeight);
- base.OnControlRemoved( child );
+ // Set the new scroll position and momentum
+ ScrollPosition += delta;
+ scrollMomentum = (scrollMomentum + delta) * 0.5f;
- if( child != null )
- {
- detachEvents( child );
- }
+ args.Use();
- if( autoLayout )
- {
- AutoArrange();
- }
- else
- {
- updateScrollbars();
- }
+ }
- }
+ }
- protected override void OnRebuildRenderData()
- {
+ base.OnMouseMove(args);
- if( Atlas == null || string.IsNullOrEmpty( backgroundSprite ) )
- return;
+ }
- var spriteInfo = Atlas[ backgroundSprite ];
- if( spriteInfo == null )
- {
- return;
- }
+ protected internal override void OnMouseWheel(dfMouseEventArgs args)
+ {
- renderData.Material = Atlas.Material;
+ try
+ {
- var color = ApplyOpacity( this.BackgroundColor );
- var options = new dfSprite.RenderOptions()
- {
- atlas = atlas,
- color = color,
- fillAmount = 1,
- flip = dfSpriteFlip.None,
- offset = pivot.TransformToUpperLeft( Size ),
- pixelsToUnits = PixelsToUnits(),
- size = Size,
- spriteInfo = spriteInfo
- };
+ if (args.Used)
+ return;
- if( spriteInfo.border.horizontal == 0 && spriteInfo.border.vertical == 0 )
- dfSprite.renderSprite( renderData, options );
- else
- dfSlicedSprite.renderSprite( renderData, options );
+ var amount = wheelDirection == dfControlOrientation.Horizontal
+ ? horzScroll != null ? horzScroll.IncrementAmount : scrollWheelAmount
+ : vertScroll != null ? vertScroll.IncrementAmount : scrollWheelAmount;
- }
+ if (wheelDirection == dfControlOrientation.Horizontal)
+ {
+ ScrollPosition = new Vector2(scrollPosition.x - amount * args.WheelDelta, scrollPosition.y);
+ scrollMomentum = new Vector2(-amount * args.WheelDelta, 0);
+ }
+ else
+ {
+ ScrollPosition = new Vector2(scrollPosition.x, scrollPosition.y - amount * args.WheelDelta);
+ scrollMomentum = new Vector2(0, -amount * args.WheelDelta);
+ }
- protected internal void OnScrollPositionChanged()
- {
+ args.Use();
+ Signal("OnMouseWheel", this, args);
- Invalidate();
+ }
+ finally
+ {
+ base.OnMouseWheel(args);
+ }
- SignalHierarchy( "OnScrollPositionChanged", this, this.ScrollPosition );
+ }
- if( ScrollPositionChanged != null )
- {
- ScrollPositionChanged( this, this.ScrollPosition );
- }
+ protected internal override void OnControlAdded(dfControl child)
+ {
- }
+ base.OnControlAdded(child);
- #endregion
+ attachEvents(child);
- #region Public methods
+ if (autoLayout)
+ {
+ AutoArrange();
+ }
- ///
- /// Resizes the panel to ensure that it encompasses all child controls
- ///
- public void FitToContents()
- {
+ }
- if( controls.Count == 0 )
- return;
+ protected internal override void OnControlRemoved(dfControl child)
+ {
- var max = Vector2.zero;
- for( int i = 0; i < controls.Count; i++ )
- {
+ base.OnControlRemoved(child);
- var child = controls[ i ];
- var childMax = (Vector2)child.RelativePosition + child.Size;
+ if (child != null)
+ {
+ detachEvents(child);
+ }
- max = Vector2.Max( max, childMax );
+ if (autoLayout)
+ {
+ AutoArrange();
+ }
+ else
+ {
+ updateScrollbars();
+ }
- }
+ }
- this.Size = max + new Vector2( scrollPadding.right, scrollPadding.bottom );
+ protected override void OnRebuildRenderData()
+ {
- }
+ if (Atlas == null || string.IsNullOrEmpty(backgroundSprite))
+ return;
- ///
- /// Centers all child controls within the bounds of the panel
- ///
- public void CenterChildControls()
- {
+ var spriteInfo = Atlas[backgroundSprite];
+ if (spriteInfo == null)
+ {
+ return;
+ }
- if( controls.Count == 0 )
- return;
+ renderData.Material = Atlas.Material;
- var min = Vector2.one * float.MaxValue;
- var max = Vector2.one * float.MinValue;
+ var color = ApplyOpacity(this.BackgroundColor);
+ var options = new dfSprite.RenderOptions()
+ {
+ atlas = atlas,
+ color = color,
+ fillAmount = 1,
+ flip = dfSpriteFlip.None,
+ offset = pivot.TransformToUpperLeft(Size),
+ pixelsToUnits = PixelsToUnits(),
+ size = Size,
+ spriteInfo = spriteInfo
+ };
- for( int i = 0; i < controls.Count; i++ )
- {
+ if (spriteInfo.border.horizontal == 0 && spriteInfo.border.vertical == 0)
+ dfSprite.renderSprite(renderData, options);
+ else
+ dfSlicedSprite.renderSprite(renderData, options);
- var child = controls[ i ];
- var childMin = (Vector2)child.RelativePosition;
- var childMax = childMin + child.Size;
+ }
- min = Vector2.Min( min, childMin );
- max = Vector2.Max( max, childMax );
+ protected internal void OnScrollPositionChanged()
+ {
- }
+ Invalidate();
- var contentSize = max - min;
- var contentOffset = ( this.Size - contentSize ) * 0.5f;
+ SignalHierarchy("OnScrollPositionChanged", this, this.ScrollPosition);
- for( int i = 0; i < controls.Count; i++ )
- {
- var child = controls[ i ];
- child.RelativePosition = (Vector2)child.RelativePosition - min + contentOffset;
- }
-
- }
-
- ///
- /// Sets the scrollposition to the top
- ///
- public void ScrollToTop()
- {
- scrollMomentum = Vector2.zero;
- this.ScrollPosition = new Vector2( this.scrollPosition.x, 0 );
- }
-
- ///
- /// Sets the scrollposition to the top
- ///
- public void ScrollToBottom()
- {
- scrollMomentum = Vector2.zero;
- this.ScrollPosition = new Vector2( this.scrollPosition.x, int.MaxValue );
- }
-
- ///
- /// Sets the scrollposition to the top
- ///
- public void ScrollToLeft()
- {
- scrollMomentum = Vector2.zero;
- this.ScrollPosition = new Vector2( 0, this.scrollPosition.y );
- }
-
- ///
- /// Sets the scrollposition to the top
- ///
- public void ScrollToRight()
- {
- scrollMomentum = Vector2.zero;
- this.ScrollPosition = new Vector2( int.MaxValue, this.scrollPosition.y );
- }
-
- ///
- /// Scrolls the specified child control into view
- ///
- /// The child control to scroll into view
- public void ScrollIntoView( dfControl control )
- {
-
- scrollMomentum = Vector2.zero;
-
- var viewRect = new Rect(
- scrollPosition.x + scrollPadding.left,
- scrollPosition.y + scrollPadding.top,
- size.x - scrollPadding.horizontal,
- size.y - scrollPadding.vertical
- ).RoundToInt();
-
- var controlPosition = control.RelativePosition;
- var controlSize = control.Size;
-
- while( !controls.Contains( control ) )
- {
- control = control.Parent;
- controlPosition += control.RelativePosition;
- }
-
- var controlRect = new Rect(
- scrollPosition.x + controlPosition.x,
- scrollPosition.y + controlPosition.y,
- controlSize.x,
- controlSize.y
- ).RoundToInt();
-
- if( viewRect.Contains( controlRect ) )
- {
- return;
- }
+ if (ScrollPositionChanged != null)
+ {
+ ScrollPositionChanged(this, this.ScrollPosition);
+ }
+
+ }
- var newScrollPos = scrollPosition;
+ #endregion
- if( controlRect.xMin < viewRect.xMin )
- {
- newScrollPos.x = controlRect.xMin - scrollPadding.left;
- }
- else if( controlRect.xMax > viewRect.xMax )
- {
- newScrollPos.x = controlRect.xMax - Mathf.Max( size.x, controlSize.x ) + scrollPadding.horizontal;
- }
+ #region Public methods
- if( controlRect.y < viewRect.y )
- {
- newScrollPos.y = controlRect.yMin - scrollPadding.top;
- }
- else if( controlRect.yMax > viewRect.yMax )
- {
- newScrollPos.y = controlRect.yMax - Mathf.Max( size.y, controlSize.y ) + scrollPadding.vertical;
- }
+ ///
+ /// Resizes the panel to ensure that it encompasses all child controls
+ ///
+ public void FitToContents()
+ {
- ScrollPosition = newScrollPos;
- scrollMomentum = Vector2.zero;
+ if (controls.Count == 0)
+ return;
- }
+ var max = Vector2.zero;
+ for (int i = 0; i < controls.Count; i++)
+ {
- ///
- /// Reset the viewport back to the upper left origin of the scrollable area
- ///
- public void Reset()
- {
+ var child = controls[i];
+ var childMax = (Vector2)child.RelativePosition + child.Size;
- try
- {
+ max = Vector2.Max(max, childMax);
+
+ }
+
+ this.Size = max + new Vector2(scrollPadding.right, scrollPadding.bottom);
+
+ }
+
+ ///
+ /// Centers all child controls within the bounds of the panel
+ ///
+ public void CenterChildControls()
+ {
+
+ if (controls.Count == 0)
+ return;
+
+ var min = Vector2.one * float.MaxValue;
+ var max = Vector2.one * float.MinValue;
+
+ for (int i = 0; i < controls.Count; i++)
+ {
+
+ var child = controls[i];
+ var childMin = (Vector2)child.RelativePosition;
+ var childMax = childMin + child.Size;
+
+ min = Vector2.Min(min, childMin);
+ max = Vector2.Max(max, childMax);
+
+ }
+
+ var contentSize = max - min;
+ var contentOffset = (this.Size - contentSize) * 0.5f;
+
+ for (int i = 0; i < controls.Count; i++)
+ {
+ var child = controls[i];
+ child.RelativePosition = (Vector2)child.RelativePosition - min + contentOffset;
+ }
+
+ }
+
+ ///
+ /// Sets the scrollposition to the top
+ ///
+ public void ScrollToTop()
+ {
+ scrollMomentum = Vector2.zero;
+ this.ScrollPosition = new Vector2(this.scrollPosition.x, 0);
+ }
+
+ ///
+ /// Sets the scrollposition to the top
+ ///
+ public void ScrollToBottom()
+ {
+ scrollMomentum = Vector2.zero;
+ this.ScrollPosition = new Vector2(this.scrollPosition.x, int.MaxValue);
+ }
+
+ ///
+ /// Sets the scrollposition to the top
+ ///
+ public void ScrollToLeft()
+ {
+ scrollMomentum = Vector2.zero;
+ this.ScrollPosition = new Vector2(0, this.scrollPosition.y);
+ }
+
+ ///
+ /// Sets the scrollposition to the top
+ ///
+ public void ScrollToRight()
+ {
+ scrollMomentum = Vector2.zero;
+ this.ScrollPosition = new Vector2(int.MaxValue, this.scrollPosition.y);
+ }
+
+ ///
+ /// Scrolls the specified child control into view
+ ///
+ /// The child control to scroll into view
+ public void ScrollIntoView(dfControl control)
+ {
+
+ scrollMomentum = Vector2.zero;
+
+ var viewRect = new Rect(
+ scrollPosition.x + scrollPadding.left,
+ scrollPosition.y + scrollPadding.top,
+ size.x - scrollPadding.horizontal,
+ size.y - scrollPadding.vertical
+ ).RoundToInt();
+
+ var controlPosition = control.RelativePosition;
+ var controlSize = control.Size;
+
+ while (!controls.Contains(control))
+ {
+ control = control.Parent;
+ controlPosition += control.RelativePosition;
+ }
+
+ var controlRect = new Rect(
+ scrollPosition.x + controlPosition.x,
+ scrollPosition.y + controlPosition.y,
+ controlSize.x,
+ controlSize.y
+ ).RoundToInt();
+
+ if (viewRect.Contains(controlRect))
+ {
+ return;
+ }
+
+ var newScrollPos = scrollPosition;
+
+ if (controlRect.xMin < viewRect.xMin)
+ {
+ newScrollPos.x = controlRect.xMin - scrollPadding.left;
+ }
+ else if (controlRect.xMax > viewRect.xMax)
+ {
+ newScrollPos.x = controlRect.xMax - Mathf.Max(size.x, controlSize.x) + scrollPadding.horizontal;
+ }
+
+ if (controlRect.y < viewRect.y)
+ {
+ newScrollPos.y = controlRect.yMin - scrollPadding.top;
+ }
+ else if (controlRect.yMax > viewRect.yMax)
+ {
+ newScrollPos.y = controlRect.yMax - Mathf.Max(size.y, controlSize.y) + scrollPadding.vertical;
+ }
+
+ ScrollPosition = newScrollPos;
+ scrollMomentum = Vector2.zero;
+
+ }
+
+ ///
+ /// Reset the viewport back to the upper left origin of the scrollable area
+ ///
+ public void Reset()
+ {
+
+ try
+ {
+
+ SuspendLayout();
+
+ if (autoLayout)
+ {
+ var savedScrollPosition = this.ScrollPosition;
+ this.ScrollPosition = Vector2.zero;
+ AutoArrange();
+ ScrollPosition = savedScrollPosition;
+ }
+ else
+ {
+
+ scrollPadding = ScrollPadding.ConstrainPadding();
+
+ var offset = (Vector3)calculateMinChildPosition();
+ offset -= new Vector3(scrollPadding.left, scrollPadding.top);
+
+ for (int i = 0; i < controls.Count; i++)
+ {
+ controls[i].RelativePosition -= offset;
+ }
+
+ scrollPosition = Vector2.zero;
+
+ }
+
+ Invalidate();
+
+ updateScrollbars();
+
+ }
+ finally
+ {
+ ResumeLayout();
+ }
+
+ }
+
+ ///
+ /// Instruct the dfScrollPanel to only create as many tiles as needed to fill the dfScrollPanel,
+ /// and recycle the information presented on the tile based on the backing list.
+ ///
+ ///
+ /// An arbitrary list of objects that will seed the tiles as they are recycled / initialized.
+ ///
+ /// Used internally; passed from when ScrollBar paging begins.
+ /// Once I can successfully get it working for initially starting the virtualization at this point
+ /// I will publicly expose this method.
+ ///
+ private void Virtualize(IList backingList, int startIndex)
+ {
+
+ if (!useVirtualScrolling)
+ {
+ Debug.LogError("Virtual scrolling not enabled for this dfScrollPanel: " + name);
+ return;
+ }
+
+ if (virtualScrollingTile == null)
+ {
+ Debug.LogError("Virtual scrolling cannot be started without assigning VirtualScrollingTile: " + name);
+ return;
+ }
+
+ if (backingList.Count == 0)
+ {
+ //# What do you think we should we do here, if anything?
+ }
+
+ var data = GetVirtualScrollData() ?? initVirtualScrollData(backingList);
+ var isVerticalFlow = this.isVerticalFlow();
+
+ //# Used to save the flow padding if they had auto-layout on in the editor.
+ var padding = data.ItemPadding = new RectOffset(
+ FlowPadding.left,
+ FlowPadding.right,
+ FlowPadding.top,
+ FlowPadding.bottom);
+
+ //# Directional padding.
+ var dPadding = (isVerticalFlow) ? padding.vertical : padding.horizontal;
+ //# 'Zero' padding. Couldn't think of a more succinct variable name ;)
+ var zPadding = (isVerticalFlow) ? padding.top : padding.left;
+ //# ScrollPanel measurement for direction.
+ var spLength = (isVerticalFlow) ? Height : Width;
+
+ //# We no longer need these as we handle layouting, and resetting manually.
+ AutoLayout = false;
+ AutoReset = false;
+
+ //# Dummy tile to get measurements. Skip initializing one if we are paging (already have one).
+ var dummy = data.DummyTop ?? (data.DummyTop = initTile(padding));
+ var dp = dummy.GetDfPanel();
+ var tileLength = (isVerticalFlow)
+ ? dummy.GetDfPanel().Height
+ : dummy.GetDfPanel().Width;
+
+ //# Cleanup for the top dummy.
+ dp.IsEnabled = false;
+ dp.Opacity = 0;
+ dp.gameObject.hideFlags = HideFlags.HideInHierarchy;
+ lastWidth = tileLength + dPadding;
+ //# We need to do more "dummy" hacking if user is using scrollbars so the scrollbar sees a "max scroll position".
+ //# We can skip this whole block if we are paging (already know we have scrollbars).
+ dfScrollbar sb;
+
+ if ((sb = VertScrollbar) || (sb = HorzScrollbar))
+ {
+
+ var bottomDummy = data.DummyBottom ?? (data.DummyBottom = initTile(padding));
+ var bdp = bottomDummy.GetDfPanel();
+
+ //# Shoot to bottom so scrollbar sees a maxvalue
+ var dpStart = (isVerticalFlow) ? dp.RelativePosition.y : dp.RelativePosition.x;
+ var bdpPos = dpStart + (((backingList.Count - 1) * (tileLength + dPadding)) + zPadding);
+
+ //# Send all the way to the very bottom where it should meet up with the last reycled tile in our list.
+ bdp.RelativePosition = (isVerticalFlow)
+ ? new Vector3(dp.RelativePosition.x, bdpPos)
+ : new Vector3(bdpPos, dp.RelativePosition.y);
+
+ //# Mirroring other dummy panel so I can quickly debug.
+ bdp.IsEnabled = dp.IsEnabled;
+ bdp.gameObject.hideFlags = dp.hideFlags;
+ bdp.Opacity = dp.Opacity;
+
+ //# This block ensures the virtual scrol position is saved when new items are added / removed from the backing list.
+ if (startIndex == 0 && sb.MaxValue != 0)
+ {
+ var pct = sb.Value / sb.MaxValue;
+ //# I've tried ceil and floor, but the results are kinda unpredictable sometimes. Rounding seems to be the best so far
+ //# without messing about with float tolerance. Mostly annoying after paging, and them touch scrolling back to 0;
+ //# sometimes the 0th tile is a tad off-y/x
+ startIndex = Mathf.RoundToInt(pct * (backingList.Count - 1));
+ }
+
+ //# Give the scrollbar a perfect value so that non-paged scrolling doesn't mess up the 0th tile.
+ sb.Value = startIndex * (tileLength + dPadding);
+ }
+
+ //# Some checks here to determine the optimal number of tiles to generate in order to make virtualization possible.
+ var maxTilesRaw = spLength / (tileLength + dPadding);
+ var maxTilesRounded = Mathf.RoundToInt(maxTilesRaw);
+ var maxTiles = (maxTilesRounded > maxTilesRaw)
+ ? maxTilesRounded + 1
+ : maxTilesRounded + 2;
+
+ //# Initially I was going to use this for paging issues, but no longer neccessary.
+ //# Could use if for "flickering" caused by recycling if needed, but we'll have to see where best to
+ //# make this publicly available.
+ //maxTiles += data.MaxExtraOffscreenTiles;
+
+ //# Begin magic
+ float nextZero = zPadding;
+ //# I'm going to work on getting the ScrollBar and the ScrollPanel to reflect these changes. So far, no luck.
+ float startScrollAtIndex = startIndex;
+
+ //# Loop throug all of our tiles (or potential tiles) and place them evenly on the stage.
+ for (var i = 0; i < maxTiles && i < backingList.Count && startIndex <= backingList.Count; i++)
+ {
+ //# We use a try/catch since we need to be notified, and correct index errors.
+ try
+ {
+ var tile = (data.IsInitialized && data.Tiles.Count >= (i + 1))
+ ? data.Tiles[i]
+ : initTile(padding);
+ var panel = tile.GetDfPanel();
+ var zero = nextZero;
+
+ panel.RelativePosition = (isVerticalFlow)
+ ? new Vector2(padding.left, zero)
+ : new Vector2(zero, padding.top);
+ nextZero = zero + tileLength + dPadding;
+
+ if (!data.Tiles.Contains(tile))
+ {
+ data.Tiles.Add(tile);
+ }
+
+ tile.VirtualScrollItemIndex = startIndex;
+ tile.OnScrollPanelItemVirtualize(backingList[startIndex]);
+
+ startIndex++;
+ }
+ catch
+ {
+ foreach (var tile in data.Tiles)
+ {
+ var index = tile.VirtualScrollItemIndex - 1;
+
+ tile.VirtualScrollItemIndex = index;
+ tile.OnScrollPanelItemVirtualize(backingList[index]);
+ }
+ }
+ }
+
+ //# Remove the old listener so we aren't doubling up...
+ //# Eventually I will also use this block to set the scrollbar position for users who want to start
+ //# at a certian index in their backing list.
+ if (startScrollAtIndex != 0)
+ {
+ if (ScrollPositionChanged != null)
+ {
+ ScrollPositionChanged -= virtualScrollPositionChanged;
+ }
+ }
+
+ data.IsInitialized = true;
+ ScrollPositionChanged += virtualScrollPositionChanged;
+
+ }
+
+ ///
+ /// Instruct the dfScrollPanel to only create as many tiles as needed to fill the dfScrollPanel,
+ /// and recycle the information presented on the tile based on the backing list.
+ ///
+ ///
+ /// An arbitrary list of objects that will seed the tiles as they are recycled / initialized.
+ /// A dfPanel tile.
+ public void Virtualize(IList backingList, dfPanel tile)
+ {
+
+ var inter = tile
+ .GetComponents()
+ .FirstOrDefault(t => t is IDFVirtualScrollingTile);
+
+ if (!inter)
+ {
+ Debug.LogError("The tile you've chosen does not implement IDFVirtualScrollingTile!");
+ return;
+ }
+
+ UseVirtualScrolling = true;
+ VirtualScrollingTile = tile;
+
+ Virtualize(backingList, 0);
+
+ }
+
+ ///
+ /// Instruct the dfScrollPanel to only create as many tiles as needed to fill the dfScrollPanel,
+ /// and recycle the information presented on the tile based on the backing list.
+ ///
+ ///
+ /// An arbitrary list of objects that will seed the tiles as they are recycled / initialized.
+ public void Virtualize(IList backingList)
+ {
+ Virtualize(backingList, 0);
+ }
+
+ public void ResetVirtualScrollingData()
+ {
+
+ virtualScrollData = null;
+
+ var temp = controls.ToArray();
+
+ for (int i = 0; i < temp.Length; i++)
+ {
+ var c = temp[i];
+ RemoveControl(c);
+ c.transform.parent = null;
+ Destroy(c.gameObject, 0.2f * i);
+
+ }
+
+ ScrollPosition = Vector2.zero;
+
+ }
+
+ ///
+ /// Get a reference to the store virtual scrolling information.
+ ///
+ /// Where T is the Type parameter in the backing list seeding the tiles.
+ ///
+ public dfVirtualScrollData GetVirtualScrollData()
+ {
+ return (dfVirtualScrollData)virtualScrollData;
+ }
+
+ #endregion
+
+ #region Private utility methods
+
+ [HideInInspector]
+ private void AutoArrange()
+ {
+
+ SuspendLayout();
+ try
+ {
+
+ scrollPadding = ScrollPadding.ConstrainPadding();
+ flowPadding = FlowPadding.ConstrainPadding();
+
+ var x = (float)scrollPadding.left + (float)flowPadding.left - scrollPosition.x;
+ var y = (float)scrollPadding.top + (float)flowPadding.top - scrollPosition.y;
- SuspendLayout();
+ var maxWidth = 0f;
+ var maxHeight = 0f;
- if( autoLayout )
- {
- var savedScrollPosition = this.ScrollPosition;
- this.ScrollPosition = Vector2.zero;
- AutoArrange();
- ScrollPosition = savedScrollPosition;
- }
- else
- {
+ for (int i = 0; i < controls.Count; i++)
+ {
- scrollPadding = ScrollPadding.ConstrainPadding();
+ var child = controls[i];
- var offset = (Vector3)calculateMinChildPosition();
- offset -= new Vector3( scrollPadding.left, scrollPadding.top );
+ if (!child.GetIsVisibleRaw() || !child.enabled || !child.gameObject.activeSelf)
+ continue;
- for( int i = 0; i < controls.Count; i++ )
- {
- controls[ i ].RelativePosition -= offset;
- }
+ if (child == this.horzScroll || child == this.vertScroll)
+ continue;
- scrollPosition = Vector2.zero;
+ if (this.wrapLayout)
+ {
- }
+ if (flowDirection == LayoutDirection.Horizontal)
+ {
+ if (x + child.Width >= size.x - scrollPadding.right)
+ {
+ x = (float)scrollPadding.left + (float)flowPadding.left;
+ y += maxHeight;
+ maxHeight = 0f;
+ }
+ }
+ else
+ {
+ if (y + child.Height + flowPadding.vertical >= size.y - scrollPadding.bottom)
+ {
+ y = (float)scrollPadding.top + (float)flowPadding.top;
+ x += maxWidth;
+ maxWidth = 0f;
+ }
+ }
- Invalidate();
+ }
- updateScrollbars();
+ var childPosition = new Vector2(x, y);
+ child.RelativePosition = childPosition;
- }
- finally
- {
- ResumeLayout();
- }
-
- }
-
- ///
- /// Instruct the dfScrollPanel to only create as many tiles as needed to fill the dfScrollPanel,
- /// and recycle the information presented on the tile based on the backing list.
- ///
- ///
- /// An arbitrary list of objects that will seed the tiles as they are recycled / initialized.
- ///
- /// Used internally; passed from when ScrollBar paging begins.
- /// Once I can successfully get it working for initially starting the virtualization at this point
- /// I will publicly expose this method.
- ///
- private void Virtualize( IList backingList, int startIndex )
- {
-
- if( !useVirtualScrolling )
- {
- Debug.LogError( "Virtual scrolling not enabled for this dfScrollPanel: " + name );
- return;
- }
-
- if( virtualScrollingTile == null )
- {
- Debug.LogError( "Virtual scrolling cannot be started without assigning VirtualScrollingTile: " + name );
- return;
- }
-
- if( backingList.Count == 0 )
- {
- //# What do you think we should we do here, if anything?
- }
-
- var data = GetVirtualScrollData() ?? initVirtualScrollData( backingList );
- var isVerticalFlow = this.isVerticalFlow();
-
- //# Used to save the flow padding if they had auto-layout on in the editor.
- var padding = data.ItemPadding = new RectOffset(
- FlowPadding.left,
- FlowPadding.right,
- FlowPadding.top,
- FlowPadding.bottom );
-
- //# Directional padding.
- var dPadding = ( isVerticalFlow ) ? padding.vertical : padding.horizontal;
- //# 'Zero' padding. Couldn't think of a more succinct variable name ;)
- var zPadding = ( isVerticalFlow ) ? padding.top : padding.left;
- //# ScrollPanel measurement for direction.
- var spLength = ( isVerticalFlow ) ? Height : Width;
-
- //# We no longer need these as we handle layouting, and resetting manually.
- AutoLayout = false;
- AutoReset = false;
-
- //# Dummy tile to get measurements. Skip initializing one if we are paging (already have one).
- var dummy = data.DummyTop ?? ( data.DummyTop = initTile( padding ) );
- var dp = dummy.GetDfPanel();
- var tileLength = ( isVerticalFlow )
- ? dummy.GetDfPanel().Height
- : dummy.GetDfPanel().Width;
-
- //# Cleanup for the top dummy.
- dp.IsEnabled = false;
- dp.Opacity = 0;
- dp.gameObject.hideFlags = HideFlags.HideInHierarchy;
-
- //# We need to do more "dummy" hacking if user is using scrollbars so the scrollbar sees a "max scroll position".
- //# We can skip this whole block if we are paging (already know we have scrollbars).
- dfScrollbar sb;
-
- if( ( sb = VertScrollbar ) || ( sb = HorzScrollbar ) )
- {
-
- var bottomDummy = data.DummyBottom ?? ( data.DummyBottom = initTile( padding ) );
- var bdp = bottomDummy.GetDfPanel();
-
- //# Shoot to bottom so scrollbar sees a maxvalue
- var dpStart = ( isVerticalFlow ) ? dp.RelativePosition.y : dp.RelativePosition.x;
- var bdpPos = dpStart + ( ( ( backingList.Count - 1 ) * ( tileLength + dPadding ) ) + zPadding );
-
- //# Send all the way to the very bottom where it should meet up with the last reycled tile in our list.
- bdp.RelativePosition = ( isVerticalFlow )
- ? new Vector3( dp.RelativePosition.x, bdpPos )
- : new Vector3( bdpPos, dp.RelativePosition.y );
-
- //# Mirroring other dummy panel so I can quickly debug.
- bdp.IsEnabled = dp.IsEnabled;
- bdp.gameObject.hideFlags = dp.hideFlags;
- bdp.Opacity = dp.Opacity;
-
- //# This block ensures the virtual scrol position is saved when new items are added / removed from the backing list.
- if( startIndex == 0 && sb.MaxValue != 0 )
- {
- var pct = sb.Value / sb.MaxValue;
- //# I've tried ceil and floor, but the results are kinda unpredictable sometimes. Rounding seems to be the best so far
- //# without messing about with float tolerance. Mostly annoying after paging, and them touch scrolling back to 0;
- //# sometimes the 0th tile is a tad off-y/x
- startIndex = Mathf.RoundToInt( pct * ( backingList.Count - 1 ) );
- }
-
- //# Give the scrollbar a perfect value so that non-paged scrolling doesn't mess up the 0th tile.
- sb.Value = startIndex * ( tileLength + dPadding );
- }
-
- //# Some checks here to determine the optimal number of tiles to generate in order to make virtualization possible.
- var maxTilesRaw = spLength / ( tileLength + dPadding );
- var maxTilesRounded = Mathf.RoundToInt( maxTilesRaw );
- var maxTiles = ( maxTilesRounded > maxTilesRaw )
- ? maxTilesRounded + 1
- : maxTilesRounded + 2;
-
- //# Initially I was going to use this for paging issues, but no longer neccessary.
- //# Could use if for "flickering" caused by recycling if needed, but we'll have to see where best to
- //# make this publicly available.
- //maxTiles += data.MaxExtraOffscreenTiles;
-
- //# Begin magic
- float nextZero = zPadding;
- //# I'm going to work on getting the ScrollBar and the ScrollPanel to reflect these changes. So far, no luck.
- float startScrollAtIndex = startIndex;
-
- //# Loop throug all of our tiles (or potential tiles) and place them evenly on the stage.
- for( var i = 0; i < maxTiles && i < backingList.Count && startIndex <= backingList.Count; i++ )
- {
- //# We use a try/catch since we need to be notified, and correct index errors.
- try
- {
- var tile = ( data.IsInitialized && data.Tiles.Count >= ( i + 1 ) )
- ? data.Tiles[ i ]
- : initTile( padding );
- var panel = tile.GetDfPanel();
- var zero = nextZero;
-
- panel.RelativePosition = ( isVerticalFlow )
- ? new Vector2( padding.left, zero )
- : new Vector2( zero, padding.top );
- nextZero = zero + tileLength + dPadding;
-
- if( !data.Tiles.Contains( tile ) )
- {
- data.Tiles.Add( tile );
- }
-
- tile.VirtualScrollItemIndex = startIndex;
- tile.OnScrollPanelItemVirtualize( backingList[ startIndex ] );
-
- startIndex++;
- }
- catch
- {
- foreach( var tile in data.Tiles )
- {
- var index = tile.VirtualScrollItemIndex - 1;
-
- tile.VirtualScrollItemIndex = index;
- tile.OnScrollPanelItemVirtualize( backingList[ index ] );
- }
- }
- }
-
- //# Remove the old listener so we aren't doubling up...
- //# Eventually I will also use this block to set the scrollbar position for users who want to start
- //# at a certian index in their backing list.
- if( startScrollAtIndex != 0 )
- {
- if( ScrollPositionChanged != null )
- {
- ScrollPositionChanged -= virtualScrollPositionChanged;
- }
- }
-
- data.IsInitialized = true;
- ScrollPositionChanged += virtualScrollPositionChanged;
-
- }
-
- ///
- /// Instruct the dfScrollPanel to only create as many tiles as needed to fill the dfScrollPanel,
- /// and recycle the information presented on the tile based on the backing list.
- ///
- ///
- /// An arbitrary list of objects that will seed the tiles as they are recycled / initialized.
- /// A dfPanel tile.
- public void Virtualize( IList backingList, dfPanel tile )
- {
-
- var inter = tile
- .GetComponents()
- .FirstOrDefault( t => t is IDFVirtualScrollingTile );
-
- if( !inter )
- {
- Debug.LogError( "The tile you've chosen does not implement IDFVirtualScrollingTile!" );
- return;
- }
-
- UseVirtualScrolling = true;
- VirtualScrollingTile = tile;
-
- Virtualize( backingList, 0 );
-
- }
-
- ///
- /// Instruct the dfScrollPanel to only create as many tiles as needed to fill the dfScrollPanel,
- /// and recycle the information presented on the tile based on the backing list.
- ///
- ///
- /// An arbitrary list of objects that will seed the tiles as they are recycled / initialized.
- public void Virtualize( IList backingList )
- {
- Virtualize( backingList, 0 );
- }
-
- public void ResetVirtualScrollingData()
- {
-
- virtualScrollData = null;
-
- var temp = controls.ToArray();
-
- for( int i = 0; i < temp.Length; i++ )
- {
- var c = temp[ i ];
- RemoveControl( c );
- Destroy( c.gameObject );
- }
+ var xofs = child.Width + flowPadding.horizontal;
+ var yofs = child.Height + flowPadding.vertical;
- ScrollPosition = Vector2.zero;
+ maxWidth = Mathf.Max(xofs, maxWidth);
+ maxHeight = Mathf.Max(yofs, maxHeight);
- }
+ if (flowDirection == LayoutDirection.Horizontal)
+ x += xofs;
+ else
+ y += yofs;
- ///
- /// Get a reference to the store virtual scrolling information.
- ///
- /// Where T is the Type parameter in the backing list seeding the tiles.
- ///
- public dfVirtualScrollData GetVirtualScrollData()
- {
- return (dfVirtualScrollData) virtualScrollData;
- }
+ }
- #endregion
+ updateScrollbars();
- #region Private utility methods
+ }
+ finally
+ {
+ ResumeLayout();
+ }
- [HideInInspector]
- private void AutoArrange()
- {
+ }
- SuspendLayout();
- try
- {
+ [HideInInspector]
+ private void initialize()
+ {
- scrollPadding = ScrollPadding.ConstrainPadding();
- flowPadding = FlowPadding.ConstrainPadding();
+ if (initialized)
+ return;
- var x = (float)scrollPadding.left + (float)flowPadding.left - scrollPosition.x;
- var y = (float)scrollPadding.top + (float)flowPadding.top - scrollPosition.y;
+ initialized = true;
- var maxWidth = 0f;
- var maxHeight = 0f;
+ if (Application.isPlaying)
+ {
- for( int i = 0; i < controls.Count; i++ )
- {
+ if (horzScroll != null)
+ {
+ horzScroll.ValueChanged += horzScroll_ValueChanged;
+ }
- var child = controls[ i ];
+ if (vertScroll != null)
+ {
+ vertScroll.ValueChanged += vertScroll_ValueChanged;
+ }
- if( !child.GetIsVisibleRaw() || !child.enabled || !child.gameObject.activeSelf )
- continue;
+ }
- if( child == this.horzScroll || child == this.vertScroll )
- continue;
+ if (resetNeeded || autoLayout || autoReset)
+ {
+ Reset();
+ }
- if( this.wrapLayout )
- {
+ Invalidate();
+ ScrollPosition = Vector2.zero;
- if( flowDirection == LayoutDirection.Horizontal )
- {
- if( x + child.Width >= size.x - scrollPadding.right )
- {
- x = (float)scrollPadding.left + (float)flowPadding.left;
- y += maxHeight;
- maxHeight = 0f;
- }
- }
- else
- {
- if( y + child.Height + flowPadding.vertical >= size.y - scrollPadding.bottom )
- {
- y = (float)scrollPadding.top + (float)flowPadding.top;
- x += maxWidth;
- maxWidth = 0f;
- }
- }
+ updateScrollbars();
- }
+ }
- var childPosition = new Vector2( x, y );
- child.RelativePosition = childPosition;
+ private void vertScroll_ValueChanged(dfControl control, float value)
+ {
+ ScrollPosition = new Vector2(scrollPosition.x, value);
+ }
- var xofs = child.Width + flowPadding.horizontal;
- var yofs = child.Height + flowPadding.vertical;
+ private void horzScroll_ValueChanged(dfControl control, float value)
+ {
+ ScrollPosition = new Vector2(value, ScrollPosition.y);
+ }
- maxWidth = Mathf.Max( xofs, maxWidth );
- maxHeight = Mathf.Max( yofs, maxHeight );
+ private void scrollChildControls(Vector3 delta)
+ {
- if( flowDirection == LayoutDirection.Horizontal )
- x += xofs;
- else
- y += yofs;
+ try
+ {
- }
+ scrolling = true;
- updateScrollbars();
+ delta = delta.Scale(1, -1, 1);
- }
- finally
- {
- ResumeLayout();
- }
+ for (int i = 0; i < controls.Count; i++)
+ {
+ var child = controls[i];
+ child.Position = (child.Position - delta).RoundToInt();
+ }
- }
+ }
+ finally
+ {
+ scrolling = false;
+ }
- [HideInInspector]
- private void initialize()
- {
+ }
- if( initialized )
- return;
+ private Vector2 calculateMinChildPosition()
+ {
- initialized = true;
+ var minX = float.MaxValue;
+ var minY = float.MaxValue;
- if( Application.isPlaying )
- {
+ for (int i = 0; i < controls.Count; i++)
+ {
- if( horzScroll != null )
- {
- horzScroll.ValueChanged += horzScroll_ValueChanged;
- }
+ var child = controls[i];
+ if (!child.enabled || !child.gameObject.activeSelf)
+ continue;
- if( vertScroll != null )
- {
- vertScroll.ValueChanged += vertScroll_ValueChanged;
- }
+ var childPos = child.RelativePosition.FloorToInt();
+ minX = Mathf.Min(minX, childPos.x);
+ minY = Mathf.Min(minY, childPos.y);
- }
+ }
- if( resetNeeded || autoLayout || autoReset )
- {
- Reset();
- }
+ return new Vector2(minX, minY);
- Invalidate();
- ScrollPosition = Vector2.zero;
+ }
- updateScrollbars();
+ private Vector2 calculateViewSize()
+ {
- }
+ // Calculate size of client rect
+ var padding = new Vector2(scrollPadding.horizontal, scrollPadding.vertical).RoundToInt();
+ var clientSize = this.Size.RoundToInt() - padding;
- private void vertScroll_ValueChanged( dfControl control, float value )
- {
- ScrollPosition = new Vector2( scrollPosition.x, value );
- }
+ // If not visible or no controls, viewsize is same as client rect
+ if (!this.IsVisible || this.controls.Count == 0)
+ {
+ return clientSize;
+ }
- private void horzScroll_ValueChanged( dfControl control, float value )
- {
- ScrollPosition = new Vector2( value, ScrollPosition.y );
- }
+ var min = Vector2.one * float.MaxValue;
+ var max = Vector2.one * -float.MaxValue;
- private void scrollChildControls( Vector3 delta )
- {
+ for (int i = 0; i < controls.Count; i++)
+ {
- try
- {
+ var child = controls[i];
- scrolling = true;
+ // Skip calculation of child controls that are not visible.
+ // NOTE: Only done during runtime, as this control is "live"
+ // in the editor and we don't want to change the layout
+ // during design time. Everything will be correct when running.
+ if (Application.isPlaying && !child.IsVisible)
+ continue;
- delta = delta.Scale( 1, -1, 1 );
+ var controlMin = (Vector2)child.RelativePosition.CeilToInt();
+ var controlMax = controlMin + child.Size.CeilToInt();
- for( int i = 0; i < controls.Count; i++ )
- {
- var child = controls[ i ];
- child.Position = ( child.Position - delta ).RoundToInt();
- }
+ min = Vector2.Min(controlMin, min);
+ max = Vector2.Max(controlMax, max);
- }
- finally
- {
- scrolling = false;
- }
+ }
- }
+ // If the minimum control position is greater than the origin, we'll need
+ // to compensate for that so that controls don't get moved to the origin
+ // and to allow for scrolling all the way to the far extents
+ var minOffset = Vector2.Max(Vector2.zero, min - new Vector2(scrollPadding.left, scrollPadding.top));
- private Vector2 calculateMinChildPosition()
- {
+ // Regardless of where the current scroll position is, the
+ // max view extent is always the lower-right corner of the
+ // client rect.
+ max = Vector2.Max(max + minOffset, clientSize);
- var minX = float.MaxValue;
- var minY = float.MaxValue;
+ return (max - min) + minOffset;
- for( int i = 0; i < controls.Count; i++ )
- {
+ }
- var child = controls[ i ];
- if( !child.enabled || !child.gameObject.activeSelf )
- continue;
+ [HideInInspector]
+ private void updateScrollbars()
+ {
- var childPos = child.RelativePosition.FloorToInt();
- minX = Mathf.Min( minX, childPos.x );
- minY = Mathf.Min( minY, childPos.y );
+ var viewSize = calculateViewSize();
- }
+ var clientSize = this.Size - new Vector2(scrollPadding.horizontal, scrollPadding.vertical);
- return new Vector2( minX, minY );
+ if (horzScroll != null)
+ {
+ horzScroll.MinValue = 0;
+ horzScroll.MaxValue = viewSize.x;
+ horzScroll.ScrollSize = clientSize.x;
+ horzScroll.Value = Mathf.Max(0, scrollPosition.x);
+ }
- }
+ if (vertScroll != null)
+ {
+ vertScroll.MinValue = 0;
+ vertScroll.MaxValue = viewSize.y;
+ vertScroll.ScrollSize = clientSize.y;
+ vertScroll.Value = Mathf.Max(0, scrollPosition.y);
+ }
- private Vector2 calculateViewSize()
- {
+ }
- // Calculate size of client rect
- var padding = new Vector2( scrollPadding.horizontal, scrollPadding.vertical ).RoundToInt();
- var clientSize = this.Size.RoundToInt() - padding;
+ ///
+ /// This method triggers when the scroll position changes. It checks all of the tiles, and
+ /// performs recycling on them if they are outside the directional bounds.
+ ///
+ ///
+ ///
+ ///
+ private void virtualScrollPositionChanged(dfControl control, Vector2 value)
+ {
- // If not visible or no controls, viewsize is same as client rect
- if( !this.IsVisible || this.controls.Count == 0 )
- {
- return clientSize;
- }
+ var data = GetVirtualScrollData();
+
+ if (data == null)
+ {
+ return;
+ }
+
+ var list = data.BackingList;
+ var padding = data.ItemPadding;
+ var tiles = data.Tiles;
+ var isVerticalFlow = this.isVerticalFlow();
+
+ //# Delta-scroll
+ var d = (isVerticalFlow)
+ ? value.y - data.LastScrollPosition.y
+ : value.x - data.LastScrollPosition.x;
+
+ data.LastScrollPosition = value;
+
+ /**
+ * We perform some checks here to detect if there is extremely fast scrolling happening.
+ * Fast scrolling like that will cause the ScrollPanel to "lose" the tiles in the recycling process.
+ * Instead of performing standard recycling, we check to see if the scroll delta is about the length
+ * of the scroll panel. If so, we simply get an index from our backing list using the value/max Scrollbar percentage,
+ * then restart the virtualization process (as far as placement goes)
+ */
+ var isPaging = Mathf.Abs(d) > Height;
+
+ if (isPaging && (VertScrollbar || HorzScrollbar))
+ {
+
+ var pct = (isVerticalFlow)
+ ? value.y / VertScrollbar.MaxValue
+ : value.x / HorzScrollbar.MaxValue;
+
+ //# I've tried ceil and floor, but the results are kinda unpredictable sometimes. Rounding seems to be the best so far
+ //# without messing about with float tolerance. Mostly annoying after paging, and them touch scrolling back to 0;
+ //# sometimes the 0th tile is a tad off-y/x
+ var indexAtScrollPosition = Mathf.RoundToInt(pct * (list.Count - 1));
+
+ //# Restart the virtualization process.
+ Virtualize(list, indexAtScrollPosition);
+
+ return;
+
+ }
+
+ //# Loop through all tiles seeing if they are out of bounds. If so we shift them to the top, or bottom
+ //# of the stack depending on which vertical bound they pass.
+ foreach (var tileInterface in tiles)
+ {
+
+ int index = 0;
+ float newStart = 0;
+ var hasChangeOccured = false;
+ var panel = tileInterface.GetDfPanel();
+ var start = (isVerticalFlow) ? panel.RelativePosition.y : panel.RelativePosition.x;
+ var panelLength = (isVerticalFlow) ? panel.Height : panel.Width;
+ var posExtreme = (isVerticalFlow) ? Height : Width;
+
+ if (d > 0)
+ {
+
+ if (!((start + panelLength) <= 0))
+ {
+ continue;
+ }
+
+ //# It would be great to just use Linq like this, but iOS throws AOT errors.
+ //# Any suggestions would be great.
+ //index = tiles.Max( x => x.GetListItemIndex() ) + 1;
+ //newStart = tiles.Max( x => x.GetDfPanel().RelativePosition.y ) + panelLength;
+
+ //# Instead, we have to do it this way...
+ data.GetNewLimits(isVerticalFlow, true, out index, out newStart);
+
+ //# Don't try to reposition this tile if there is no valid index in backing list.
+ if (!(index < list.Count))
+ {
+ continue;
+ }
+
+ hasChangeOccured = true;
+ panel.RelativePosition = (isVerticalFlow)
+ ? new Vector3(panel.RelativePosition.x, newStart + panelLength + padding.vertical)
+ : new Vector3(newStart + panelLength + padding.horizontal, panel.RelativePosition.y);
+
+ }
+ else if (d < 0)
+ {
+
+ if (!(start >= (posExtreme)))
+ {
+ continue;
+ }
+
+ data.GetNewLimits(isVerticalFlow, false, out index, out newStart);
+
+ if (index < 0)
+ {
+ continue;
+ }
+
+ hasChangeOccured = true;
+ panel.RelativePosition = (isVerticalFlow)
+ ? new Vector3(panel.RelativePosition.x, newStart - (panelLength + padding.vertical))
+ : new Vector3(newStart - (panelLength + padding.horizontal), panel.RelativePosition.y);
+
+ }
+
+ if (!hasChangeOccured)
+ {
+ continue;
+ }
+
+ tileInterface.VirtualScrollItemIndex = index;
+ tileInterface.OnScrollPanelItemVirtualize(list[index]);
+
+ }
+
+ }
+
+ ///
+ /// Initializes an information storage object that keeps track of various virtual scrolling
+ /// settings.
+ ///
+ ///
+ ///
+ ///
+ private dfVirtualScrollData initVirtualScrollData(IList backingList)
+ {
+
+ var data = new dfVirtualScrollData { BackingList = backingList };
+ virtualScrollData = data;
+
+ return data;
+
+ }
+
+ ///
+ /// Instantiate, and add a tile to the scroll panel to be used for virtual scrolling.
+ ///
+ ///
+ ///
+ private IDFVirtualScrollingTile initTile(RectOffset padding)
+ {
+
+ var inter = virtualScrollingTile.GetComponents()
+ .FirstOrDefault(p => p is IDFVirtualScrollingTile);
+ var tile = (IDFVirtualScrollingTile)Instantiate(inter);
+ var panel = tile.GetDfPanel();
+ var isVerticalFlow = this.isVerticalFlow();
+
+ AddControl(panel);
+
+ if (AutoFitVirtualTiles)
+ {
+ if (isVerticalFlow)
+ {
+ panel.Width = Width - padding.horizontal;
+ }
+ else
+ {
+ panel.Height = Height - padding.vertical;
+ }
+ }
- var min = Vector2.one * float.MaxValue;
- var max = Vector2.one * -float.MaxValue;
+ panel.RelativePosition = new Vector3(padding.left, padding.top);
- for( int i = 0; i < controls.Count; i++ )
- {
+ return tile;
- var child = controls[ i ];
+ }
- // Skip calculation of child controls that are not visible.
- // NOTE: Only done during runtime, as this control is "live"
- // in the editor and we don't want to change the layout
- // during design time. Everything will be correct when running.
- if( Application.isPlaying && !child.IsVisible )
- continue;
+ private bool isVerticalFlow()
+ {
+ return (FlowDirection == LayoutDirection.Vertical);
+ }
- var controlMin = (Vector2)child.RelativePosition.CeilToInt();
- var controlMax = controlMin + child.Size.CeilToInt();
+ #region Child control events
- min = Vector2.Min( controlMin, min );
- max = Vector2.Max( controlMax, max );
+ private void attachEvents(dfControl control)
+ {
+ control.IsVisibleChanged += childIsVisibleChanged;
+ control.PositionChanged += childControlInvalidated;
+ control.SizeChanged += childControlInvalidated;
+ control.ZOrderChanged += childOrderChanged;
+ }
- }
+ private void detachEvents(dfControl control)
+ {
+ control.IsVisibleChanged -= childIsVisibleChanged;
+ control.PositionChanged -= childControlInvalidated;
+ control.SizeChanged -= childControlInvalidated;
+ control.ZOrderChanged -= childOrderChanged;
+ }
- // If the minimum control position is greater than the origin, we'll need
- // to compensate for that so that controls don't get moved to the origin
- // and to allow for scrolling all the way to the far extents
- var minOffset = Vector2.Max( Vector2.zero, min - new Vector2( scrollPadding.left, scrollPadding.top ) );
+ void childOrderChanged(dfControl control, int value)
+ {
+ onChildControlInvalidatedLayout();
+ }
- // Regardless of where the current scroll position is, the
- // max view extent is always the lower-right corner of the
- // client rect.
- max = Vector2.Max( max + minOffset, clientSize );
+ void childIsVisibleChanged(dfControl control, bool value)
+ {
+ onChildControlInvalidatedLayout();
+ }
- return ( max - min ) + minOffset;
+ private void childControlInvalidated(dfControl control, Vector2 value)
+ {
+ onChildControlInvalidatedLayout();
+ }
- }
+ [HideInInspector]
+ private void onChildControlInvalidatedLayout()
+ {
- [HideInInspector]
- private void updateScrollbars()
- {
+ if (scrolling || IsLayoutSuspended)
+ return;
- var viewSize = calculateViewSize();
+ if (autoLayout)
+ {
+ AutoArrange();
+ }
+
+ updateScrollbars();
- var clientSize = this.Size - new Vector2( scrollPadding.horizontal, scrollPadding.vertical );
+ Invalidate();
- if( horzScroll != null )
- {
- horzScroll.MinValue = 0;
- horzScroll.MaxValue = viewSize.x;
- horzScroll.ScrollSize = clientSize.x;
- horzScroll.Value = Mathf.Max( 0, scrollPosition.x );
- }
+ }
- if( vertScroll != null )
- {
- vertScroll.MinValue = 0;
- vertScroll.MaxValue = viewSize.y;
- vertScroll.ScrollSize = clientSize.y;
- vertScroll.Value = Mathf.Max( 0, scrollPosition.y );
- }
-
- }
-
- ///
- /// This method triggers when the scroll position changes. It checks all of the tiles, and
- /// performs recycling on them if they are outside the directional bounds.
- ///
- ///
- ///
- ///
- private void virtualScrollPositionChanged( dfControl control, Vector2 value )
- {
-
- var data = GetVirtualScrollData();
-
- if( data == null )
- {
- return;
- }
-
- var list = data.BackingList;
- var padding = data.ItemPadding;
- var tiles = data.Tiles;
- var isVerticalFlow = this.isVerticalFlow();
-
- //# Delta-scroll
- var d = ( isVerticalFlow )
- ? value.y - data.LastScrollPosition.y
- : value.x - data.LastScrollPosition.x;
-
- data.LastScrollPosition = value;
-
- /**
- * We perform some checks here to detect if there is extremely fast scrolling happening.
- * Fast scrolling like that will cause the ScrollPanel to "lose" the tiles in the recycling process.
- * Instead of performing standard recycling, we check to see if the scroll delta is about the length
- * of the scroll panel. If so, we simply get an index from our backing list using the value/max Scrollbar percentage,
- * then restart the virtualization process (as far as placement goes)
- */
- var isPaging = Mathf.Abs( d ) > Height;
-
- if( isPaging && ( VertScrollbar || HorzScrollbar ) )
- {
+ #endregion
- var pct = ( isVerticalFlow )
- ? value.y / VertScrollbar.MaxValue
- : value.x / HorzScrollbar.MaxValue;
+ #endregion
- //# I've tried ceil and floor, but the results are kinda unpredictable sometimes. Rounding seems to be the best so far
- //# without messing about with float tolerance. Mostly annoying after paging, and them touch scrolling back to 0;
- //# sometimes the 0th tile is a tad off-y/x
- var indexAtScrollPosition = Mathf.RoundToInt( pct * ( list.Count - 1 ) );
+ ///
+ /// 自动跳到指定的index
+ ///
+ ///
+ ///
+ public dfPanel ScrollIntoIndex(int i)
+ {
+ if (isVerticalFlow())
+ {
+ scrollPosition = new Vector2(0, i * lastWidth);
+ }
+ else
+ {
+ scrollPosition = new Vector2(i * lastWidth, 0);
+ }
- //# Restart the virtualization process.
- Virtualize( list, indexAtScrollPosition );
- return;
- }
+ OnScrollPositionChanged();
- //# Loop through all tiles seeing if they are out of bounds. If so we shift them to the top, or bottom
- //# of the stack depending on which vertical bound they pass.
- foreach( var tileInterface in tiles )
- {
- int index = 0;
- float newStart = 0;
- var hasChangeOccured = false;
- var panel = tileInterface.GetDfPanel();
- var start = ( isVerticalFlow ) ? panel.RelativePosition.y : panel.RelativePosition.x;
- var panelLength = ( isVerticalFlow ) ? panel.Height : panel.Width;
- var posExtreme = ( isVerticalFlow ) ? Height : Width;
-
- if( d > 0 )
- {
-
- if( !( ( start + panelLength ) <= 0 ) )
- {
- continue;
- }
-
- //# It would be great to just use Linq like this, but iOS throws AOT errors.
- //# Any suggestions would be great.
- //index = tiles.Max( x => x.GetListItemIndex() ) + 1;
- //newStart = tiles.Max( x => x.GetDfPanel().RelativePosition.y ) + panelLength;
-
- //# Instead, we have to do it this way...
- data.GetNewLimits( isVerticalFlow, true, out index, out newStart );
-
- //# Don't try to reposition this tile if there is no valid index in backing list.
- if( !( index < list.Count ) )
- {
- continue;
- }
-
- hasChangeOccured = true;
- panel.RelativePosition = ( isVerticalFlow )
- ? new Vector3( panel.RelativePosition.x, newStart + panelLength + padding.vertical )
- : new Vector3( newStart + panelLength + padding.horizontal, panel.RelativePosition.y );
-
- }
- else if( d < 0 )
- {
-
- if( !( start >= ( posExtreme ) ) )
- {
- continue;
- }
-
- data.GetNewLimits( isVerticalFlow, false, out index, out newStart );
-
- if( index < 0 )
- {
- continue;
- }
-
- hasChangeOccured = true;
- panel.RelativePosition = ( isVerticalFlow )
- ? new Vector3( panel.RelativePosition.x, newStart - ( panelLength + padding.vertical ) )
- : new Vector3( newStart - ( panelLength + padding.horizontal ), panel.RelativePosition.y );
-
- }
-
- if( !hasChangeOccured )
- {
- continue;
- }
-
- tileInterface.VirtualScrollItemIndex = index;
- tileInterface.OnScrollPanelItemVirtualize( list[ index ] );
-
- }
-
- }
-
- ///
- /// Initializes an information storage object that keeps track of various virtual scrolling
- /// settings.
- ///
- ///
- ///
- ///
- private dfVirtualScrollData initVirtualScrollData( IList backingList )
- {
-
- var data = new dfVirtualScrollData { BackingList = backingList };
- virtualScrollData = data;
-
- return data;
-
- }
-
- ///
- /// Instantiate, and add a tile to the scroll panel to be used for virtual scrolling.
- ///
- ///
- ///
- private IDFVirtualScrollingTile initTile( RectOffset padding )
- {
-
- var inter = virtualScrollingTile.GetComponents()
- .FirstOrDefault( p => p is IDFVirtualScrollingTile );
- var tile = (IDFVirtualScrollingTile)Instantiate( inter );
- var panel = tile.GetDfPanel();
- var isVerticalFlow = this.isVerticalFlow();
-
- AddControl( panel );
-
- if ( AutoFitVirtualTiles )
- {
- if ( isVerticalFlow )
- {
- panel.Width = Width - padding.horizontal;
- } else
- {
- panel.Height = Height - padding.vertical;
- }
- }
-
- panel.RelativePosition = new Vector3( padding.left, padding.top );
-
- return tile;
-
- }
-
- private bool isVerticalFlow()
- {
- return ( FlowDirection == LayoutDirection.Vertical );
- }
-
- #region Child control events
-
- private void attachEvents( dfControl control )
- {
- control.IsVisibleChanged += childIsVisibleChanged;
- control.PositionChanged += childControlInvalidated;
- control.SizeChanged += childControlInvalidated;
- control.ZOrderChanged += childOrderChanged;
- }
-
- private void detachEvents( dfControl control )
- {
- control.IsVisibleChanged -= childIsVisibleChanged;
- control.PositionChanged -= childControlInvalidated;
- control.SizeChanged -= childControlInvalidated;
- control.ZOrderChanged -= childOrderChanged;
- }
-
- void childOrderChanged( dfControl control, int value )
- {
- onChildControlInvalidatedLayout();
- }
-
- void childIsVisibleChanged( dfControl control, bool value )
- {
- onChildControlInvalidatedLayout();
- }
-
- private void childControlInvalidated( dfControl control, Vector2 value )
- {
- onChildControlInvalidatedLayout();
- }
-
- [HideInInspector]
- private void onChildControlInvalidatedLayout()
- {
-
- if( scrolling || IsLayoutSuspended )
- return;
-
- if( autoLayout )
- {
- AutoArrange();
- }
+ List tiels =
+ (List)virtualScrollData.GetType().GetField("Tiles").GetValue(virtualScrollData);
- updateScrollbars();
- Invalidate();
- }
+ foreach (IDFVirtualScrollingTile scrollingTile in tiels)
+ {
+ if (scrollingTile.VirtualScrollItemIndex == i)
+ {
- #endregion
+ var panel = scrollingTile.GetDfPanel();
+ return panel;
+ }
+ }
- #endregion
+ return null;
+ }
}