add shadow effect for scrollablebase.
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / ScrollableBase.cs
index d0a008d..50f177d 100755 (executable)
@@ -19,33 +19,32 @@ using System.Collections.Generic;
 using System.ComponentModel;
 using System.Diagnostics;
 using System.Runtime.InteropServices;
+using Tizen.NUI.Accessibility;
 
 namespace Tizen.NUI.Components
 {
     /// <summary>
     /// ScrollEventArgs is a class to record scroll event arguments which will sent to user.
     /// </summary>
-    /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-    [EditorBrowsable(EditorBrowsableState.Never)]
+    /// <since_tizen> 8 </since_tizen>
     public class ScrollEventArgs : EventArgs
     {
-        Position position;
+        private Position position;
 
         /// <summary>
         /// Default constructor.
         /// </summary>
         /// <param name="position">Current scroll position</param>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
+        /// <since_tizen> 8 </since_tizen>
         public ScrollEventArgs(Position position)
         {
             this.position = position;
         }
 
         /// <summary>
-        /// [Draft] Current scroll position.
+        /// Current position of ContentContainer.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public Position Position
         {
             get
@@ -56,16 +55,18 @@ namespace Tizen.NUI.Components
     }
 
     /// <summary>
-    /// [Draft] This class provides a View that can scroll a single View with a layout. This View can be a nest of Views.
+    /// This class provides a View that can scroll a single View with a layout. This View can be a nest of Views.
     /// </summary>
-    /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
-    [EditorBrowsable(EditorBrowsableState.Never)]
+    /// <since_tizen> 8 </since_tizen>
     public class ScrollableBase : Control
     {
         static bool LayoutDebugScrollableBase = false; // Debug flag
         private Direction mScrollingDirection = Direction.Vertical;
         private bool mScrollEnabled = true;
+        private int mScrollDuration = 125;
         private int mPageWidth = 0;
+        private float mPageFlickThreshold = 0.4f;
+        private float mScrollingEventThreshold = 0.00001f;
 
         private class ScrollableBaseCustomLayout : LayoutGroup
         {
@@ -147,6 +148,7 @@ namespace Tizen.NUI.Components
 
                 // Size of ScrollableBase is changed. Change Page width too.
                 scrollableBase.mPageWidth = (int)MeasuredWidth.Size.AsRoundedValue();
+                scrollableBase.OnScrollingChildRelayout(null , null);
             }
 
             protected override void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
@@ -174,30 +176,27 @@ namespace Tizen.NUI.Components
         /// <summary>
         /// The direction axis to scroll.
         /// </summary>
-        /// <since_tizen> 6 </since_tizen>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public enum Direction
         {
             /// <summary>
             /// Horizontal axis.
             /// </summary>
-            /// <since_tizen> 6 </since_tizen>
+            /// <since_tizen> 8 </since_tizen>
             Horizontal,
 
             /// <summary>
             /// Vertical axis.
             /// </summary>
-            /// <since_tizen> 6 </since_tizen>
+            /// <since_tizen> 8 </since_tizen>
             Vertical
         }
 
         /// <summary>
-        /// [Draft] Scrolling direction mode.
+        /// Scrolling direction mode.
         /// Default is Vertical scrolling.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public Direction ScrollingDirection
         {
             get
@@ -223,10 +222,9 @@ namespace Tizen.NUI.Components
         }
 
         /// <summary>
-        /// [Draft] Enable or disable scrolling.
+        /// Enable or disable scrolling.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public bool ScrollEnabled
         {
             get
@@ -251,78 +249,79 @@ namespace Tizen.NUI.Components
         }
 
         /// <summary>
-        /// [Draft] Pages mode, enables moving to the next or return to current page depending on pan displacement.
+        /// Pages mode, enables moving to the next or return to current page depending on pan displacement.
         /// Default is false.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public bool SnapToPage { set; get; } = false;
 
         /// <summary>
-        /// [Draft] Get current page.
-        /// Working propery with SnapToPage property.
+        /// Get current page.
+        /// Working property with SnapToPage property.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public int CurrentPage { get; private set; } = 0;
 
         /// <summary>
-        /// [Draft] Duration of scroll animation.
+        /// Duration of scroll animation.
+        /// Default value is 125ms.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
+        public int ScrollDuration
+        {
+            set
+            {
+                mScrollDuration = value >= 0 ? value : mScrollDuration;
+            }
+            get
+            {
+                return mScrollDuration;
+            }
+        }
 
-        public int ScrollDuration { set; get; } = 125;
         /// <summary>
-        /// [Draft] Scroll Available area.
+        /// Scroll Available area.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public Vector2 ScrollAvailableArea { set; get; }
 
         /// <summary>
         /// An event emitted when user starts dragging ScrollableBase, user can subscribe or unsubscribe to this event handler.<br />
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public event EventHandler<ScrollEventArgs> ScrollDragStarted;
 
         /// <summary>
         /// An event emitted when user stops dragging ScrollableBase, user can subscribe or unsubscribe to this event handler.<br />
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <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>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public event EventHandler<ScrollEventArgs> ScrollAnimationStarted;
 
         /// <summary>
         /// An event emitted when the scrolling slide animation ends, user can subscribe or unsubscribe to this event handler.<br />
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <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>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public event EventHandler<ScrollEventArgs> Scrolling;
 
 
         /// <summary>
-        /// Scrollbar for ScrollableBase.<br />
+        /// Scrollbar for ScrollableBase.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public ScrollbarBase Scrollbar
         {
             get
@@ -335,30 +334,32 @@ namespace Tizen.NUI.Components
                 {
                     scrollBar.Unparent();
                 }
-
                 scrollBar = value;
-                scrollBar.Name = "ScrollBar";
-                base.Add(scrollBar);
 
-                if (hideScrollbar)
-                {
-                    scrollBar.Hide();
-                }
-                else
+                if (scrollBar != null)
                 {
-                    scrollBar.Show();
-                }
+                    scrollBar.Name = "ScrollBar";
+                    base.Add(scrollBar);
 
-                SetScrollbar();
+                    if (hideScrollbar)
+                    {
+                        scrollBar.Hide();
+                    }
+                    else
+                    {
+                        scrollBar.Show();
+                    }
+
+                    SetScrollbar();
+                }
             }
         }
 
         /// <summary>
-        /// [Draft] Always hide Scrollbar.
+        /// Always hide Scrollbar.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public bool HideScrollBar
+        /// <since_tizen> 8 </since_tizen>
+        public bool HideScrollbar
         {
             get
             {
@@ -385,13 +386,13 @@ namespace Tizen.NUI.Components
         /// <summary>
         /// Container which has content of ScrollableBase.
         /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public View ContentContainer { get; private set; }
 
         /// <summary>
         /// Set the layout on this View. Replaces any existing Layout.
         /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public new LayoutItem Layout
         {
             get
@@ -411,7 +412,7 @@ namespace Tizen.NUI.Components
         /// <summary>
         /// List of children of Container.
         /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public new List<View> Children
         {
             get
@@ -422,9 +423,10 @@ namespace Tizen.NUI.Components
 
         /// <summary>
         /// Deceleration rate of scrolling by finger.
-        /// Rate should be 0 < rate < 1.
+        /// Rate should be bigger than 0 and smaller than 1.
+        /// Default value is 0.998f;
         /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public float DecelerationRate
         {
             get
@@ -433,7 +435,7 @@ namespace Tizen.NUI.Components
             }
             set
             {
-                decelerationRate = value;
+                decelerationRate = (value < 1 && value > 0) ? value : decelerationRate;
                 logValueOfDeceleration = (float)Math.Log(value);
             }
         }
@@ -445,10 +447,44 @@ namespace Tizen.NUI.Components
         public float DecelerationThreshold { get; set; } = 0.1f;
 
         /// <summary>
-        /// Page will be changed when velocity of panning is over threshold.
+        /// Scrolling event will be thrown when this amount of scroll positino is changed.
         /// </summary>
         [EditorBrowsable(EditorBrowsableState.Never)]
-        public float PageFlickThreshold { get; set; } = 0.4f;
+        public float ScrollingEventThreshold
+        {
+            get
+            {
+                return mScrollingEventThreshold;
+            }
+            set
+            {
+                if (mScrollingEventThreshold != value && value > 0)
+                {
+                    ContentContainer.RemovePropertyNotification(propertyNotification);
+                    propertyNotification = ContentContainer.AddPropertyNotification("position", PropertyCondition.Step(value));
+                    propertyNotification.Notified += OnPropertyChanged;
+                    mScrollingEventThreshold = value;
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Page will be changed when velocity of panning is over threshold.
+        /// The unit of threshold is pixel per milisec.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public float PageFlickThreshold
+        {
+            get
+            {
+                return mPageFlickThreshold;
+            }
+            set
+            {
+                mPageFlickThreshold = value >= 0f ? value : mPageFlickThreshold;
+            }
+        }
 
         /// <summary>
         /// Alphafunction for scroll animation.
@@ -466,13 +502,18 @@ namespace Tizen.NUI.Components
         private float ratioOfScreenWidthToCompleteScroll = 0.5f;
         private float totalDisplacementForPan = 0.0f;
         private Size previousContainerSize = new Size();
+        private Size previousSize = new Size();
         private PropertyNotification propertyNotification;
         private float noticeAnimationEndBeforePosition = 0.0f;
         private bool readyToNotice = false;
+
+        /// <summary>
+        /// Notice before animation is finished.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
         // Let's consider more whether this needs to be set as protected.
         public float NoticeAnimationEndBeforePosition { get => noticeAnimationEndBeforePosition; set => noticeAnimationEndBeforePosition = value; }
 
-
         // Let's consider more whether this needs to be set as protected.
         private float finalTargetPosition;
 
@@ -487,11 +528,18 @@ 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 bool isVerticalShadowShown = false;
+        private float mStartShowShadowDisplacement;
+
         /// <summary>
-        /// [Draft] Constructor
+        /// Default Constructor
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public ScrollableBase() : base()
         {
             DecelerationRate = 0.998f;
@@ -512,7 +560,7 @@ namespace Tizen.NUI.Components
                 Layout = new AbsoluteLayout() { SetPositionByLayout = false },
             };
             ContentContainer.Relayout += OnScrollingChildRelayout;
-            propertyNotification = ContentContainer.AddPropertyNotification("position", PropertyCondition.Step(1.0f));
+            propertyNotification = ContentContainer.AddPropertyNotification("position", PropertyCondition.Step(mScrollingEventThreshold));
             propertyNotification.Notified += OnPropertyChanged;
             base.Add(ContentContainer);
 
@@ -524,6 +572,28 @@ 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
+            {
+                BackgroundImage = StyleManager.GetFrameworkResourcePath("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
+            {
+                BackgroundImage = StyleManager.GetFrameworkResourcePath("nui_component_default_scroll_over_shooting_bottom.png"),
+                Opacity = 1.0f,
+                SizeHeight = 0.0f,
+                PositionUsesPivotPoint = true,
+                ParentOrigin = NUI.ParentOrigin.BottomCenter,
+                PivotPoint = NUI.PivotPoint.BottomCenter,
+            };
+
+            AccessibilityManager.Instance.SetAccessibilityAttribute(this, AccessibilityManager.AccessibilityAttribute.Trait, "ScrollableBase");
         }
 
         private bool OnIterruptTouchingChildTouched(object source, View.TouchEventArgs args)
@@ -547,8 +617,7 @@ namespace Tizen.NUI.Components
         /// Called after a child has been added to the owning view.
         /// </summary>
         /// <param name="view">The child which has been added.</param>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public override void Add(View view)
         {
             ContentContainer.Add(view);
@@ -558,8 +627,7 @@ namespace Tizen.NUI.Components
         /// Called after a child has been removed from the owning view.
         /// </summary>
         /// <param name="view">The child which has been removed.</param>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public override void Remove(View view)
         {
             if (SnapToPage && CurrentPage == Children.IndexOf(view) && CurrentPage == Children.Count - 1)
@@ -576,7 +644,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;
+            bool isSizeChanged = previousContainerSize.Width != ContentContainer.Size.Width || previousContainerSize.Height != ContentContainer.Size.Height
+                || previousSize.Width != Size.Width || previousSize.Height != Size.Height;
 
             if (isSizeChanged)
             {
@@ -585,13 +654,14 @@ namespace Tizen.NUI.Components
             }
 
             previousContainerSize = ContentContainer.Size;
+            previousSize = Size;
         }
 
         /// <summary>
-        /// The composition of a Scrollbar can vary depending on how you use ScrollableBase. 
+        /// The composition of a Scrollbar can vary depending on how you use ScrollableBase.
         /// Set the composition that will go into the ScrollableBase according to your ScrollableBase.
         /// </summary>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
+        /// <since_tizen> 8 </since_tizen>
         [EditorBrowsable(EditorBrowsableState.Never)]
         protected virtual void SetScrollbar()
         {
@@ -609,8 +679,7 @@ namespace Tizen.NUI.Components
         /// Scrolls to the item at the specified index.
         /// </summary>
         /// <param name="index">Index of item.</param>
-        /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public void ScrollToIndex(int index)
         {
             if (ContentContainer.ChildCount - 1 < index || index < 0)
@@ -663,7 +732,7 @@ namespace Tizen.NUI.Components
             float contentLength = isHorizontal ? ContentContainer.Size.Width : ContentContainer.Size.Height;
             float currentPosition = isHorizontal ? ContentContainer.CurrentPosition.X : ContentContainer.CurrentPosition.Y;
 
-            scrollBar.Update(contentLength, Math.Abs(currentPosition));
+            scrollBar?.Update(contentLength, Math.Abs(currentPosition));
             CheckPreReachedTargetPosition();
         }
 
@@ -731,7 +800,7 @@ namespace Tizen.NUI.Components
         /// </summary>
         /// <param name="position">Destination.</param>
         /// <param name="animate">Scroll with or without animation</param>
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        /// <since_tizen> 8 </since_tizen>
         public void ScrollTo(float position, bool animate)
         {
             float currentPositionX = ContentContainer.CurrentPosition.X != 0 ? ContentContainer.CurrentPosition.X : ContentContainer.Position.X;
@@ -802,7 +871,6 @@ namespace Tizen.NUI.Components
                 {
                     ContentContainer.PositionY = finalTargetPosition;
                 }
-
             }
         }
 
@@ -821,6 +889,7 @@ namespace Tizen.NUI.Components
 
             if (type == DisposeTypes.Explicit)
             {
+                StopVerticalShadowAnimation();
                 StopScroll();
 
                 if (mPanGestureDetector != null)
@@ -829,6 +898,8 @@ namespace Tizen.NUI.Components
                     mPanGestureDetector.Dispose();
                     mPanGestureDetector = null;
                 }
+
+                propertyNotification.Dispose();
             }
             base.Dispose(type);
         }
@@ -893,17 +964,135 @@ namespace Tizen.NUI.Components
             AnimateChildTo(ScrollDuration, destinationX);
         }
 
+        private void AttachShadowView()
+        {
+            // stop animation if necessary.
+            StopVerticalShadowAnimation();
+
+            base.Add(mVerticalTopShadowView);
+            base.Add(mVerticalBottomShadowView);
+
+            mVerticalTopShadowView.Size = new Size(SizeWidth, 0.0f);
+            mVerticalTopShadowView.Opacity = 1.0f;
+
+            mVerticalBottomShadowView.Size = new Size(SizeWidth, 0.0f);
+            mVerticalBottomShadowView.Opacity = 1.0f;
+
+            // at the beginning, height of vertical shadow is 0, so it is invisible.
+            isVerticalShadowShown = false;
+        }
+
+        private void DragVerticalShadow(float displacement)
+        {
+            if ((int)displacement > 0) // downwards
+            {
+                // check if reaching at the top.
+                if ((int)finalTargetPosition != 0)
+                    return;
+
+                // save start displacement, and re-calculate displacement.
+                if (!isVerticalShadowShown)
+                {
+                    mStartShowShadowDisplacement = displacement;
+                }
+                isVerticalShadowShown = true;
+
+                float newDisplacement = displacement < mStartShowShadowDisplacement ? 0 : displacement - mStartShowShadowDisplacement;
+
+                // scale limit of width is 60%.
+                float widthScale = newDisplacement / mVerticalShadowScaleHeightLimit;
+                mVerticalTopShadowView.SizeWidth = widthScale > 0.6f ? SizeWidth * 0.4f : SizeWidth * (1.0f - widthScale);
+
+                // scale limit of height is 300%.
+                mVerticalTopShadowView.SizeHeight = newDisplacement > mVerticalShadowScaleHeightLimit ? mVerticalShadowScaleHeightLimit : newDisplacement;
+            }
+            else if ((int)displacement < 0) // upwards
+            {
+                // check if reaching at the bottom.
+                if (-(int)finalTargetPosition != (int)maxScrollDistance)
+                    return;
+
+                // save start displacement, and re-calculate displacement.
+                if (!isVerticalShadowShown)
+                {
+                    mStartShowShadowDisplacement = displacement;
+                }
+                isVerticalShadowShown = true;
+
+                float newDisplacement = mStartShowShadowDisplacement < displacement ? 0 : mStartShowShadowDisplacement - displacement;
+
+                // scale limit of width is 60%.
+                float widthScale = newDisplacement / mVerticalShadowScaleHeightLimit;
+                mVerticalBottomShadowView.SizeWidth = widthScale > 0.6f ? SizeWidth * 0.4f : SizeWidth * (1.0f - widthScale);
+
+                // scale limit of height is 300%.
+                mVerticalBottomShadowView.SizeHeight = newDisplacement > mVerticalShadowScaleHeightLimit ? mVerticalShadowScaleHeightLimit : newDisplacement;
+            }
+            else
+            {
+                // if total displacement is 0, shadow would become invisible.
+                isVerticalShadowShown = false;
+            }
+        }
+
+        private void PlayVerticalShadowAnimation()
+        {
+            // stop animation if necessary.
+            StopVerticalShadowAnimation();
+
+            if (mVerticalShadowAnimation == null)
+            {
+                mVerticalShadowAnimation = new Animation(mVerticalShadowAnimationDuration);
+                mVerticalShadowAnimation.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();
+        }
+
+        private void StopVerticalShadowAnimation()
+        {
+            if (mVerticalShadowAnimation == null || mVerticalShadowAnimation.State != Animation.States.Playing)
+                return;
+
+            Debug.WriteLineIf(LayoutDebugScrollableBase, "gesture finished. Stop Vertical Shadow Animation Playing.");
+            mVerticalShadowAnimation.Stop(Animation.EndActions.Cancel);
+            OnVerticalShadowAnimationFinished(null, null);
+            mVerticalShadowAnimation.Clear();
+        }
+
+        private void OnVerticalShadowAnimationFinished(object sender, EventArgs e)
+        {
+            base.Remove(mVerticalTopShadowView);
+            base.Remove(mVerticalBottomShadowView);
+
+            mVerticalTopShadowView.Size = new Size(SizeWidth, 0.0f);
+            mVerticalBottomShadowView.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 OnPanGestureDetected(object source, PanGestureDetector.DetectedEventArgs e)
         {
+            OnPanGesture(e.PanGesture);
+        }
+
+        private void OnPanGesture(PanGesture panGesture)
+        {
             if (SnapToPage && scrollAnimation != null && scrollAnimation.State == Animation.States.Playing)
             {
                 return;
             }
 
-            if (e.PanGesture.State == Gesture.StateType.Started)
+            if (panGesture.State == Gesture.StateType.Started)
             {
                 readyToNotice = false;
                 base.Add(mInterruptTouchingChild);
+                AttachShadowView();
                 Debug.WriteLineIf(LayoutDebugScrollableBase, "Gesture Start");
                 if (scrolling && !SnapToPage)
                 {
@@ -912,22 +1101,28 @@ namespace Tizen.NUI.Components
                 totalDisplacementForPan = 0.0f;
                 OnScrollDragStarted();
             }
-            else if (e.PanGesture.State == Gesture.StateType.Continuing)
+            else if (panGesture.State == Gesture.StateType.Continuing)
             {
                 if (ScrollingDirection == Direction.Horizontal)
                 {
-                    ScrollBy(e.PanGesture.Displacement.X, false);
-                    totalDisplacementForPan += e.PanGesture.Displacement.X;
+                    ScrollBy(panGesture.Displacement.X, false);
+                    totalDisplacementForPan += panGesture.Displacement.X;
                 }
                 else
                 {
-                    ScrollBy(e.PanGesture.Displacement.Y, false);
-                    totalDisplacementForPan += e.PanGesture.Displacement.Y;
+                    // if vertical shadow is shown, does not scroll.
+                    if (!isVerticalShadowShown)
+                    {
+                        ScrollBy(panGesture.Displacement.Y, false);
+                    }
+                    totalDisplacementForPan += panGesture.Displacement.Y;
+                    DragVerticalShadow(totalDisplacementForPan);
                 }
                 Debug.WriteLineIf(LayoutDebugScrollableBase, "OnPanGestureDetected Continue totalDisplacementForPan:" + totalDisplacementForPan);
             }
-            else if (e.PanGesture.State == Gesture.StateType.Finished)
+            else if (panGesture.State == Gesture.StateType.Finished || panGesture.State == Gesture.StateType.Cancelled)
             {
+                PlayVerticalShadowAnimation();
                 OnScrollDragEnded();
                 StopScroll(); // Will replace previous animation so will stop existing one.
 
@@ -937,13 +1132,26 @@ namespace Tizen.NUI.Components
                     scrollAnimation.Finished += ScrollAnimationFinished;
                 }
 
+                float panVelocity = (ScrollingDirection == Direction.Horizontal) ? panGesture.Velocity.X : panGesture.Velocity.Y;
+
                 if (SnapToPage)
                 {
-                    PageSnap((ScrollingDirection == Direction.Horizontal) ? e.PanGesture.Velocity.X : e.PanGesture.Velocity.Y);
+                    PageSnap(panVelocity);
                 }
                 else
                 {
-                    Decelerating((ScrollingDirection == Direction.Horizontal) ? e.PanGesture.Velocity.X : e.PanGesture.Velocity.Y);
+                    if (panVelocity == 0)
+                    {
+                        float currentScrollPosition = (ScrollingDirection == Direction.Horizontal ? ContentContainer.CurrentPosition.X : ContentContainer.CurrentPosition.Y);
+                        scrollAnimation.DefaultAlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.Linear);
+                        scrollAnimation.Duration = 0;
+                        scrollAnimation.AnimateTo(ContentContainer, (ScrollingDirection == Direction.Horizontal) ? "PositionX" : "PositionY", currentScrollPosition);
+                        scrollAnimation.Play();
+                    }
+                    else
+                    {
+                        Decelerating(panVelocity, scrollAnimation);
+                    }
                 }
 
                 totalDisplacementForPan = 0;
@@ -953,6 +1161,17 @@ namespace Tizen.NUI.Components
             }
         }
 
+        internal override bool OnAccessibilityPan(PanGesture gestures)
+        {
+            if (SnapToPage && scrollAnimation != null && scrollAnimation.State == Animation.States.Playing)
+            {
+                return false;
+            }
+
+            OnPanGesture(gestures);
+            return true;
+        }
+
         private float CustomScrollAlphaFunction(float progress)
         {
             if (panAnimationDelta == 0)
@@ -971,7 +1190,13 @@ namespace Tizen.NUI.Components
             }
         }
 
-        private void Decelerating(float velocity)
+        /// <summary>
+        /// you can override it to custom your decelerating
+        /// </summary>
+        /// <param name="velocity">Velocity of current pan.</param>
+        /// <param name="animation">Scroll animation.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected virtual void Decelerating(float velocity, Animation animation)
         {
             // Decelerating using deceleration equation ===========
             //
@@ -987,7 +1212,7 @@ namespace Tizen.NUI.Components
             // X(∞) = V0 * d / (1 - d); <-- Result using inifit T can be final position because T is tending to infinity.
             //
             // Because of final T is tending to inifity, we should use threshold value to finish.
-            // Final T = log(-threshold * log d / |V0| ) / log d; 
+            // Final T = log(-threshold * log d / |V0| ) / log d;
 
             velocityOfLastPan = Math.Abs(velocity);
 
@@ -1054,16 +1279,11 @@ namespace Tizen.NUI.Components
             finalTargetPosition = destination;
 
             customScrollAlphaFunction = new UserAlphaFunctionDelegate(CustomScrollAlphaFunction);
-            scrollAnimation.DefaultAlphaFunction = new AlphaFunction(customScrollAlphaFunction);
+            animation.DefaultAlphaFunction = new AlphaFunction(customScrollAlphaFunction);
             GC.KeepAlive(customScrollAlphaFunction);
-            scrollAnimation.Duration = (int)panAnimationDuration;
-            scrollAnimation.AnimateTo(ContentContainer, (ScrollingDirection == Direction.Horizontal) ? "PositionX" : "PositionY", destination);
-            scrollAnimation.Play();
-        }
-
-        protected void OnTapGestureDetected(object source, TapGestureDetector.DetectedEventArgs e)
-        {
-
+            animation.Duration = (int)panAnimationDuration;
+            animation.AnimateTo(ContentContainer, (ScrollingDirection == Direction.Horizontal) ? "PositionX" : "PositionY", destination);
+            animation.Play();
         }
 
         private void ScrollAnimationFinished(object sender, EventArgs e)
@@ -1082,6 +1302,65 @@ namespace Tizen.NUI.Components
             return position;
         }
 
+        /// <summary>
+        /// Scroll position given to ScrollTo.
+        /// This is the position in the opposite direction to the position of ContentContainer.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public Position ScrollPosition
+        {
+            get
+            {
+                return new Position(-ContentContainer.Position);
+            }
+        }
+
+        /// <summary>
+        /// Current scroll position in the middle of ScrollTo animation.
+        /// This is the position in the opposite direction to the current position of ContentContainer.
+        /// </summary>
+        /// <since_tizen> 8 </since_tizen>
+        public Position ScrollCurrentPosition
+        {
+            get
+            {
+                return new Position(-ContentContainer.CurrentPosition);
+            }
+        }
+
+        /// <summary>
+        /// Remove all children in ContentContainer.
+        /// </summary>
+        /// <param name="dispose">If true, removed child is disposed.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void RemoveAllChildren(bool dispose = false)
+        {
+            RecursiveRemoveChildren(ContentContainer, dispose);
+        }
+
+        private void RecursiveRemoveChildren(View parent, bool dispose)
+        {
+            if (parent == null)
+            {
+                return;
+            }
+            int maxChild = (int)parent.GetChildCount();
+            for (int i = maxChild - 1; i >= 0; --i)
+            {
+                View child = parent.GetChildAt((uint)i);
+                if (child == null)
+                {
+                    continue;
+                }
+                RecursiveRemoveChildren(child, dispose);
+                parent.Remove(child);
+                if (dispose)
+                {
+                    child.Dispose();
+                }
+            }
+        }
+
     }
 
 } // namespace