[NUI] Fix ScrollableBase Size Issue
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / ScrollableBase.cs
index 50f177d..0fa7250 100755 (executable)
@@ -14,6 +14,7 @@
  *
  */
 using System;
+using Tizen.NUI;
 using Tizen.NUI.BaseComponents;
 using System.Collections.Generic;
 using System.ComponentModel;
@@ -55,6 +56,51 @@ namespace Tizen.NUI.Components
     }
 
     /// <summary>
+    /// ScrollOutofBoundEventArgs is to record scroll out-of-bound event arguments which will be sent to user.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class ScrollOutOfBoundEventArgs : EventArgs
+    {
+        /// <summary>
+        /// The bound to be scrolled out of.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public enum Bound
+        {
+            /// <summary>
+            /// Top bound.
+            /// </summary>
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            Top,
+
+            /// <summary>
+            /// Bottom bound.
+            /// </summary>
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            Bottom
+        }
+
+        /// <summary>
+        /// Default constructor.
+        /// </summary>
+        /// <param name="bound">Current scrollable bound</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public ScrollOutOfBoundEventArgs(Bound bound)
+        {
+            ScrollableBound = bound;
+        }
+
+        /// <summary>
+        /// Current position of ContentContainer.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Bound ScrollableBound
+        {
+            get;
+        }
+    }
+
+    /// <summary>
     /// This class provides a View that can scroll a single View with a layout. This View can be a nest of Views.
     /// </summary>
     /// <since_tizen> 8 </since_tizen>
@@ -72,10 +118,6 @@ namespace Tizen.NUI.Components
         {
             protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
             {
-                Extents padding = Padding;
-                float totalHeight = padding.Top + padding.Bottom;
-                float totalWidth = padding.Start + padding.End;
-
                 MeasuredSize.StateType childWidthState = MeasuredSize.StateType.MeasuredSizeOK;
                 MeasuredSize.StateType childHeightState = MeasuredSize.StateType.MeasuredSizeOK;
 
@@ -86,38 +128,30 @@ namespace Tizen.NUI.Components
                     scrollingDirection = scrollableBase.ScrollingDirection;
                 }
 
+                float totalWidth = 0.0f;
+                float totalHeight = 0.0f;
+
                 // measure child, should be a single scrolling child
                 foreach (LayoutItem childLayout in LayoutChildren)
                 {
-                    if (childLayout != null)
+                    if (childLayout != null && childLayout.Owner.Name == "ContentContainer")
                     {
                         // Get size of child
                         // Use an Unspecified MeasureSpecification mode so scrolling child is not restricted to it's parents size in Height (for vertical scrolling)
                         // or Width for horizontal scrolling
-                        MeasureSpecification unrestrictedMeasureSpec = new MeasureSpecification(heightMeasureSpec.Size, MeasureSpecification.ModeType.Unspecified);
-
                         if (scrollingDirection == Direction.Vertical)
                         {
-                            MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), unrestrictedMeasureSpec, new LayoutLength(0));  // Height unrestricted by parent
+                            MeasureSpecification unrestrictedMeasureSpec = new MeasureSpecification(heightMeasureSpec.Size, MeasureSpecification.ModeType.Unspecified);
+                            MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), unrestrictedMeasureSpec, new LayoutLength(0)); // Height unrestricted by parent
                         }
                         else
                         {
-                            MeasureChildWithMargins(childLayout, unrestrictedMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));  // Width unrestricted by parent
+                            MeasureSpecification unrestrictedMeasureSpec = new MeasureSpecification(widthMeasureSpec.Size, MeasureSpecification.ModeType.Unspecified);
+                            MeasureChildWithMargins(childLayout, unrestrictedMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0)); // Width unrestricted by parent
                         }
 
-                        float childWidth = childLayout.MeasuredWidth.Size.AsDecimal();
-                        float childHeight = childLayout.MeasuredHeight.Size.AsDecimal();
-
-                        // Determine the width and height needed by the children using their given position and size.
-                        // Children could overlap so find the left most and right most child.
-                        Position2D childPosition = childLayout.Owner.Position2D;
-                        float childLeft = childPosition.X;
-                        float childTop = childPosition.Y;
-
-                        // Store current width and height needed to contain all children.
-                        Extents childMargin = childLayout.Margin;
-                        totalWidth = childWidth + childMargin.Start + childMargin.End;
-                        totalHeight = childHeight + childMargin.Top + childMargin.Bottom;
+                        totalWidth = childLayout.MeasuredWidth.Size.AsDecimal();
+                        totalHeight = childLayout.MeasuredHeight.Size.AsDecimal();
 
                         if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
                         {
@@ -130,9 +164,8 @@ namespace Tizen.NUI.Components
                     }
                 }
 
-
-                MeasuredSize widthSizeAndState = ResolveSizeAndState(new LayoutLength(totalWidth + Padding.Start + Padding.End), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
-                MeasuredSize heightSizeAndState = ResolveSizeAndState(new LayoutLength(totalHeight + Padding.Top + Padding.Bottom), heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
+                MeasuredSize widthSizeAndState = ResolveSizeAndState(new LayoutLength(totalWidth), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
+                MeasuredSize heightSizeAndState = ResolveSizeAndState(new LayoutLength(totalHeight), heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
                 totalWidth = widthSizeAndState.Size.AsDecimal();
                 totalHeight = heightSizeAndState.Size.AsDecimal();
 
@@ -143,29 +176,27 @@ namespace Tizen.NUI.Components
                 widthSizeAndState.State = childWidthState;
                 heightSizeAndState.State = childHeightState;
 
-                SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(totalWidth + Padding.Start + Padding.End), widthMeasureSpec, childWidthState),
-                                       ResolveSizeAndState(new LayoutLength(totalHeight + Padding.Top + Padding.Bottom), heightMeasureSpec, childHeightState));
+                SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(totalWidth), widthMeasureSpec, childWidthState),
+                    ResolveSizeAndState(new LayoutLength(totalHeight), heightMeasureSpec, childHeightState));
 
                 // Size of ScrollableBase is changed. Change Page width too.
                 scrollableBase.mPageWidth = (int)MeasuredWidth.Size.AsRoundedValue();
-                scrollableBase.OnScrollingChildRelayout(null , null);
+                scrollableBase.OnScrollingChildRelayout(null, null);
             }
 
             protected override void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
             {
                 foreach (LayoutItem childLayout in LayoutChildren)
                 {
-                    if (childLayout != null)
+                    if (childLayout != null && childLayout.Owner.Name == "ContentContainer")
                     {
                         LayoutLength childWidth = childLayout.MeasuredWidth.Size;
                         LayoutLength childHeight = childLayout.MeasuredHeight.Size;
 
                         Position2D childPosition = childLayout.Owner.Position2D;
-                        Extents padding = Padding;
-                        Extents childMargin = childLayout.Margin;
 
-                        LayoutLength childLeft = new LayoutLength(childPosition.X + childMargin.Start + padding.Start);
-                        LayoutLength childTop = new LayoutLength(childPosition.Y + childMargin.Top + padding.Top);
+                        LayoutLength childLeft = new LayoutLength(childPosition.X);
+                        LayoutLength childTop = new LayoutLength(childPosition.Y);
 
                         childLayout.Layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
                     }
@@ -208,15 +239,12 @@ namespace Tizen.NUI.Components
                 if (value != mScrollingDirection)
                 {
                     mScrollingDirection = value;
-                    mPanGestureDetector.RemoveDirection(value == Direction.Horizontal ?
-                        PanGestureDetector.DirectionVertical : PanGestureDetector.DirectionHorizontal);
+                    mPanGestureDetector.ClearAngles();
                     mPanGestureDetector.AddDirection(value == Direction.Horizontal ?
                         PanGestureDetector.DirectionHorizontal : PanGestureDetector.DirectionVertical);
 
-                    ContentContainer.WidthSpecification = mScrollingDirection == Direction.Vertical ?
-                        LayoutParamPolicies.MatchParent : LayoutParamPolicies.WrapContent;
-                    ContentContainer.HeightSpecification = mScrollingDirection == Direction.Vertical ?
-                        LayoutParamPolicies.WrapContent : LayoutParamPolicies.MatchParent;
+                    ContentContainer.WidthSpecification = LayoutParamPolicies.WrapContent;
+                    ContentContainer.HeightSpecification = LayoutParamPolicies.WrapContent;
                 }
             }
         }
@@ -297,7 +325,6 @@ namespace Tizen.NUI.Components
         /// <since_tizen> 8 </since_tizen>
         public event EventHandler<ScrollEventArgs> ScrollDragEnded;
 
-
         /// <summary>
         /// An event emitted when the scrolling slide animation starts, user can subscribe or unsubscribe to this event handler.<br />
         /// </summary>
@@ -310,13 +337,17 @@ namespace Tizen.NUI.Components
         /// <since_tizen> 8 </since_tizen>
         public event EventHandler<ScrollEventArgs> ScrollAnimationEnded;
 
-
         /// <summary>
         /// An event emitted when scrolling, user can subscribe or unsubscribe to this event handler.<br />
         /// </summary>
         /// <since_tizen> 8 </since_tizen>
         public event EventHandler<ScrollEventArgs> Scrolling;
 
+        /// <summary>
+        /// An event emitted when scrolling out of bound, user can subscribe or unsubscribe to this event handler.<br />
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public event EventHandler<ScrollOutOfBoundEventArgs> ScrollOutOfBound;
 
         /// <summary>
         /// Scrollbar for ScrollableBase.
@@ -404,7 +435,7 @@ namespace Tizen.NUI.Components
                 ContentContainer.Layout = value;
                 if (ContentContainer.Layout != null)
                 {
-                    ContentContainer.Layout.SetPositionByLayout = false;
+                    ContentContainer.Layout.SetPositionByLayout = true;
                 }
             }
         }
@@ -468,7 +499,6 @@ namespace Tizen.NUI.Components
             }
         }
 
-
         /// <summary>
         /// Page will be changed when velocity of panning is over threshold.
         /// The unit of threshold is pixel per milisec.
@@ -487,6 +517,22 @@ namespace Tizen.NUI.Components
         }
 
         /// <summary>
+        /// Padding for the ScrollableBase
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Extents Padding
+        {
+            get
+            {
+                return ContentContainer.Padding;
+            }
+            set
+            {
+                ContentContainer.Padding = value;
+            }
+        }
+
+        /// <summary>
         /// Alphafunction for scroll animation.
         /// </summary>
         [EditorBrowsable(EditorBrowsableState.Never)]
@@ -512,7 +558,11 @@ namespace Tizen.NUI.Components
         /// </summary>
         [EditorBrowsable(EditorBrowsableState.Never)]
         // Let's consider more whether this needs to be set as protected.
-        public float NoticeAnimationEndBeforePosition { get => noticeAnimationEndBeforePosition; set => noticeAnimationEndBeforePosition = value; }
+        public float NoticeAnimationEndBeforePosition
+        {
+            get => noticeAnimationEndBeforePosition;
+            set => noticeAnimationEndBeforePosition = value;
+        }
 
         // Let's consider more whether this needs to be set as protected.
         private float finalTargetPosition;
@@ -528,13 +578,13 @@ namespace Tizen.NUI.Components
         private float logValueOfDeceleration = 0.0f;
         private float decelerationRate = 0.0f;
 
-        private View mVerticalTopShadowView;
-        private View mVerticalBottomShadowView;
-        private const int mVerticalShadowScaleHeightLimit = 64 * 3;
-        private const int mVerticalShadowAnimationDuration = 300;
-        private Animation mVerticalShadowAnimation;
+        private View verticalTopShadowView;
+        private View verticalBottomShadowView;
+        private const int verticalShadowScaleHeightLimit = 64 * 3;
+        private const int verticalShadowAnimationDuration = 300;
+        private Animation verticalShadowAnimation;
         private bool isVerticalShadowShown = false;
-        private float mStartShowShadowDisplacement;
+        private float startShowShadowDisplacement;
 
         /// <summary>
         /// Default Constructor
@@ -555,9 +605,13 @@ namespace Tizen.NUI.Components
             //Default Scrolling child
             ContentContainer = new View()
             {
-                WidthSpecification = ScrollingDirection == Direction.Vertical ? LayoutParamPolicies.MatchParent : LayoutParamPolicies.WrapContent,
-                HeightSpecification = ScrollingDirection == Direction.Vertical ? LayoutParamPolicies.WrapContent : LayoutParamPolicies.MatchParent,
-                Layout = new AbsoluteLayout() { SetPositionByLayout = false },
+                Name = "ContentContainer",
+                WidthSpecification = LayoutParamPolicies.WrapContent,
+                HeightSpecification = LayoutParamPolicies.WrapContent,
+                Layout = new LinearLayout()
+                {
+                    SetPositionByLayout = true
+                },
             };
             ContentContainer.Relayout += OnScrollingChildRelayout;
             propertyNotification = ContentContainer.AddPropertyNotification("position", PropertyCondition.Step(mScrollingEventThreshold));
@@ -573,19 +627,19 @@ namespace Tizen.NUI.Components
             mInterruptTouchingChild.TouchEvent += OnIterruptTouchingChildTouched;
             Scrollbar = new Scrollbar();
 
-            //Show vertical shadow when panning down (or up) on the scroll top (or end).
-            mVerticalTopShadowView = new View
+            //Show vertical shadow on the top (or bottom) of the scrollable when panning down (or up).
+            verticalTopShadowView = new View
             {
-                BackgroundImage = StyleManager.GetFrameworkResourcePath("nui_component_default_scroll_over_shooting_top.png"),
+                BackgroundImage = Tizen.NUI.StyleManager.FrameworkResourcePath + "nui_component_default_scroll_over_shooting_top.png",
                 Opacity = 1.0f,
                 SizeHeight = 0.0f,
                 PositionUsesPivotPoint = true,
                 ParentOrigin = NUI.ParentOrigin.TopCenter,
                 PivotPoint = NUI.PivotPoint.TopCenter,
             };
-            mVerticalBottomShadowView = new View
+            verticalBottomShadowView = new View
             {
-                BackgroundImage = StyleManager.GetFrameworkResourcePath("nui_component_default_scroll_over_shooting_bottom.png"),
+                BackgroundImage = Tizen.NUI.StyleManager.FrameworkResourcePath + "nui_component_default_scroll_over_shooting_bottom.png",
                 Opacity = 1.0f,
                 SizeHeight = 0.0f,
                 PositionUsesPivotPoint = true,
@@ -644,8 +698,8 @@ namespace Tizen.NUI.Components
         private void OnScrollingChildRelayout(object source, EventArgs args)
         {
             // Size is changed. Calculate maxScrollDistance.
-            bool isSizeChanged = previousContainerSize.Width != ContentContainer.Size.Width || previousContainerSize.Height != ContentContainer.Size.Height
-                || previousSize.Width != Size.Width || previousSize.Height != Size.Height;
+            bool isSizeChanged = previousContainerSize.Width != ContentContainer.Size.Width || previousContainerSize.Height != ContentContainer.Size.Height ||
+                previousSize.Width != Size.Width || previousSize.Height != Size.Height;
 
             if (isSizeChanged)
             {
@@ -842,12 +896,11 @@ namespace Tizen.NUI.Components
             float childCurrentPosition = (ScrollingDirection == Direction.Horizontal) ? ContentContainer.PositionX : ContentContainer.PositionY;
 
             Debug.WriteLineIf(LayoutDebugScrollableBase, "ScrollBy childCurrentPosition:" + childCurrentPosition +
-                                                   " displacement:" + displacement,
-                                                   " maxScrollDistance:" + maxScrollDistance);
+                " displacement:" + displacement,
+                " maxScrollDistance:" + maxScrollDistance);
 
             childTargetPosition = childCurrentPosition + displacement; // child current position + gesture displacement
 
-
             Debug.WriteLineIf(LayoutDebugScrollableBase, "ScrollBy currentAxisPosition:" + childCurrentPosition + "childTargetPosition:" + childTargetPosition);
 
             if (animate)
@@ -923,8 +976,8 @@ namespace Tizen.NUI.Components
             }
 
             Debug.WriteLineIf(LayoutDebugScrollableBase, "ScrollBy maxScrollDistance:" + (scrollingChildLength - scrollerLength) +
-                                                   " parent length:" + scrollerLength +
-                                                   " scrolling child length:" + scrollingChildLength);
+                " parent length:" + scrollerLength +
+                " scrolling child length:" + scrollingChildLength);
 
             return Math.Max(scrollingChildLength - scrollerLength, 0);
         }
@@ -932,7 +985,7 @@ namespace Tizen.NUI.Components
         private void PageSnap(float velocity)
         {
             Debug.WriteLineIf(LayoutDebugScrollableBase, "PageSnap with pan candidate totalDisplacement:" + totalDisplacementForPan +
-                                                                " currentPage[" + CurrentPage + "]");
+                " currentPage[" + CurrentPage + "]");
 
             //Increment current page if total displacement enough to warrant a page change.
             if (Math.Abs(totalDisplacementForPan) > (mPageWidth * ratioOfScreenWidthToCompleteScroll))
@@ -969,14 +1022,14 @@ namespace Tizen.NUI.Components
             // stop animation if necessary.
             StopVerticalShadowAnimation();
 
-            base.Add(mVerticalTopShadowView);
-            base.Add(mVerticalBottomShadowView);
+            base.Add(verticalTopShadowView);
+            base.Add(verticalBottomShadowView);
 
-            mVerticalTopShadowView.Size = new Size(SizeWidth, 0.0f);
-            mVerticalTopShadowView.Opacity = 1.0f;
+            verticalTopShadowView.Size = new Size(SizeWidth, 0.0f);
+            verticalTopShadowView.Opacity = 1.0f;
 
-            mVerticalBottomShadowView.Size = new Size(SizeWidth, 0.0f);
-            mVerticalBottomShadowView.Opacity = 1.0f;
+            verticalBottomShadowView.Size = new Size(SizeWidth, 0.0f);
+            verticalBottomShadowView.Opacity = 1.0f;
 
             // at the beginning, height of vertical shadow is 0, so it is invisible.
             isVerticalShadowShown = false;
@@ -993,18 +1046,19 @@ namespace Tizen.NUI.Components
                 // save start displacement, and re-calculate displacement.
                 if (!isVerticalShadowShown)
                 {
-                    mStartShowShadowDisplacement = displacement;
+                    startShowShadowDisplacement = displacement;
+                    OnScrollOutOfBound(ScrollOutOfBoundEventArgs.Bound.Top);
                 }
                 isVerticalShadowShown = true;
 
-                float newDisplacement = displacement < mStartShowShadowDisplacement ? 0 : displacement - mStartShowShadowDisplacement;
+                float newDisplacement = (int)displacement < (int)startShowShadowDisplacement ? 0 : displacement - startShowShadowDisplacement;
 
                 // scale limit of width is 60%.
-                float widthScale = newDisplacement / mVerticalShadowScaleHeightLimit;
-                mVerticalTopShadowView.SizeWidth = widthScale > 0.6f ? SizeWidth * 0.4f : SizeWidth * (1.0f - widthScale);
+                float widthScale = newDisplacement / verticalShadowScaleHeightLimit;
+                verticalTopShadowView.SizeWidth = widthScale > 0.6f ? SizeWidth * 0.4f : SizeWidth * (1.0f - widthScale);
 
                 // scale limit of height is 300%.
-                mVerticalTopShadowView.SizeHeight = newDisplacement > mVerticalShadowScaleHeightLimit ? mVerticalShadowScaleHeightLimit : newDisplacement;
+                verticalTopShadowView.SizeHeight = newDisplacement > verticalShadowScaleHeightLimit ? verticalShadowScaleHeightLimit : newDisplacement;
             }
             else if ((int)displacement < 0) // upwards
             {
@@ -1015,18 +1069,19 @@ namespace Tizen.NUI.Components
                 // save start displacement, and re-calculate displacement.
                 if (!isVerticalShadowShown)
                 {
-                    mStartShowShadowDisplacement = displacement;
+                    startShowShadowDisplacement = displacement;
+                    OnScrollOutOfBound(ScrollOutOfBoundEventArgs.Bound.Bottom);
                 }
                 isVerticalShadowShown = true;
 
-                float newDisplacement = mStartShowShadowDisplacement < displacement ? 0 : mStartShowShadowDisplacement - displacement;
+                float newDisplacement = (int)startShowShadowDisplacement < (int)displacement ? 0 : startShowShadowDisplacement - displacement;
 
                 // scale limit of width is 60%.
-                float widthScale = newDisplacement / mVerticalShadowScaleHeightLimit;
-                mVerticalBottomShadowView.SizeWidth = widthScale > 0.6f ? SizeWidth * 0.4f : SizeWidth * (1.0f - widthScale);
+                float widthScale = newDisplacement / verticalShadowScaleHeightLimit;
+                verticalBottomShadowView.SizeWidth = widthScale > 0.6f ? SizeWidth * 0.4f : SizeWidth * (1.0f - widthScale);
 
                 // scale limit of height is 300%.
-                mVerticalBottomShadowView.SizeHeight = newDisplacement > mVerticalShadowScaleHeightLimit ? mVerticalShadowScaleHeightLimit : newDisplacement;
+                verticalBottomShadowView.SizeHeight = newDisplacement > verticalShadowScaleHeightLimit ? verticalShadowScaleHeightLimit : newDisplacement;
             }
             else
             {
@@ -1040,42 +1095,47 @@ namespace Tizen.NUI.Components
             // stop animation if necessary.
             StopVerticalShadowAnimation();
 
-            if (mVerticalShadowAnimation == null)
+            if (verticalShadowAnimation == null)
             {
-                mVerticalShadowAnimation = new Animation(mVerticalShadowAnimationDuration);
-                mVerticalShadowAnimation.Finished += OnVerticalShadowAnimationFinished;
+                verticalShadowAnimation = new Animation(verticalShadowAnimationDuration);
+                verticalShadowAnimation.Finished += OnVerticalShadowAnimationFinished;
             }
 
-            View targetView = totalDisplacementForPan < 0 ? mVerticalBottomShadowView : mVerticalTopShadowView;
-            mVerticalShadowAnimation.AnimateTo(targetView, "SizeWidth", SizeWidth);
-            mVerticalShadowAnimation.AnimateTo(targetView, "SizeHeight", 0.0f);
-            mVerticalShadowAnimation.AnimateTo(targetView, "Opacity", 0.0f);
-            mVerticalShadowAnimation.Play();
+            View targetView = totalDisplacementForPan < 0 ? verticalBottomShadowView : verticalTopShadowView;
+            verticalShadowAnimation.AnimateTo(targetView, "SizeWidth", SizeWidth);
+            verticalShadowAnimation.AnimateTo(targetView, "SizeHeight", 0.0f);
+            verticalShadowAnimation.AnimateTo(targetView, "Opacity", 0.0f);
+            verticalShadowAnimation.Play();
         }
 
         private void StopVerticalShadowAnimation()
         {
-            if (mVerticalShadowAnimation == null || mVerticalShadowAnimation.State != Animation.States.Playing)
+            if (verticalShadowAnimation == null || verticalShadowAnimation.State != Animation.States.Playing)
                 return;
 
-            Debug.WriteLineIf(LayoutDebugScrollableBase, "gesture finished. Stop Vertical Shadow Animation Playing.");
-            mVerticalShadowAnimation.Stop(Animation.EndActions.Cancel);
+            verticalShadowAnimation.Stop(Animation.EndActions.Cancel);
             OnVerticalShadowAnimationFinished(null, null);
-            mVerticalShadowAnimation.Clear();
+            verticalShadowAnimation.Clear();
         }
 
         private void OnVerticalShadowAnimationFinished(object sender, EventArgs e)
         {
-            base.Remove(mVerticalTopShadowView);
-            base.Remove(mVerticalBottomShadowView);
+            base.Remove(verticalTopShadowView);
+            base.Remove(verticalBottomShadowView);
 
-            mVerticalTopShadowView.Size = new Size(SizeWidth, 0.0f);
-            mVerticalBottomShadowView.Size = new Size(SizeWidth, 0.0f);
+            verticalTopShadowView.Size = new Size(SizeWidth, 0.0f);
+            verticalBottomShadowView.Size = new Size(SizeWidth, 0.0f);
 
             // after animation finished, height & opacity of vertical shadow both are 0, so it is invisible.
             isVerticalShadowShown = false;
         }
 
+        private void OnScrollOutOfBound(ScrollOutOfBoundEventArgs.Bound bound)
+        {
+            ScrollOutOfBoundEventArgs args = new ScrollOutOfBoundEventArgs(bound);
+            ScrollOutOfBound?.Invoke(this, args);
+        }
+
         private void OnPanGestureDetected(object source, PanGestureDetector.DetectedEventArgs e)
         {
             OnPanGesture(e.PanGesture);