add shadow effect for scrollablebase.
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / ScrollableBase.cs
1 /* Copyright (c) 2020 Samsung Electronics Co., Ltd.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  *
15  */
16 using System;
17 using Tizen.NUI.BaseComponents;
18 using System.Collections.Generic;
19 using System.ComponentModel;
20 using System.Diagnostics;
21 using System.Runtime.InteropServices;
22 using Tizen.NUI.Accessibility;
23
24 namespace Tizen.NUI.Components
25 {
26     /// <summary>
27     /// ScrollEventArgs is a class to record scroll event arguments which will sent to user.
28     /// </summary>
29     /// <since_tizen> 8 </since_tizen>
30     public class ScrollEventArgs : EventArgs
31     {
32         private Position position;
33
34         /// <summary>
35         /// Default constructor.
36         /// </summary>
37         /// <param name="position">Current scroll position</param>
38         /// <since_tizen> 8 </since_tizen>
39         public ScrollEventArgs(Position position)
40         {
41             this.position = position;
42         }
43
44         /// <summary>
45         /// Current position of ContentContainer.
46         /// </summary>
47         /// <since_tizen> 8 </since_tizen>
48         public Position Position
49         {
50             get
51             {
52                 return position;
53             }
54         }
55     }
56
57     /// <summary>
58     /// This class provides a View that can scroll a single View with a layout. This View can be a nest of Views.
59     /// </summary>
60     /// <since_tizen> 8 </since_tizen>
61     public class ScrollableBase : Control
62     {
63         static bool LayoutDebugScrollableBase = false; // Debug flag
64         private Direction mScrollingDirection = Direction.Vertical;
65         private bool mScrollEnabled = true;
66         private int mScrollDuration = 125;
67         private int mPageWidth = 0;
68         private float mPageFlickThreshold = 0.4f;
69         private float mScrollingEventThreshold = 0.00001f;
70
71         private class ScrollableBaseCustomLayout : LayoutGroup
72         {
73             protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
74             {
75                 Extents padding = Padding;
76                 float totalHeight = padding.Top + padding.Bottom;
77                 float totalWidth = padding.Start + padding.End;
78
79                 MeasuredSize.StateType childWidthState = MeasuredSize.StateType.MeasuredSizeOK;
80                 MeasuredSize.StateType childHeightState = MeasuredSize.StateType.MeasuredSizeOK;
81
82                 Direction scrollingDirection = Direction.Vertical;
83                 ScrollableBase scrollableBase = this.Owner as ScrollableBase;
84                 if (scrollableBase)
85                 {
86                     scrollingDirection = scrollableBase.ScrollingDirection;
87                 }
88
89                 // measure child, should be a single scrolling child
90                 foreach (LayoutItem childLayout in LayoutChildren)
91                 {
92                     if (childLayout != null)
93                     {
94                         // Get size of child
95                         // Use an Unspecified MeasureSpecification mode so scrolling child is not restricted to it's parents size in Height (for vertical scrolling)
96                         // or Width for horizontal scrolling
97                         MeasureSpecification unrestrictedMeasureSpec = new MeasureSpecification(heightMeasureSpec.Size, MeasureSpecification.ModeType.Unspecified);
98
99                         if (scrollingDirection == Direction.Vertical)
100                         {
101                             MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), unrestrictedMeasureSpec, new LayoutLength(0));  // Height unrestricted by parent
102                         }
103                         else
104                         {
105                             MeasureChildWithMargins(childLayout, unrestrictedMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));  // Width unrestricted by parent
106                         }
107
108                         float childWidth = childLayout.MeasuredWidth.Size.AsDecimal();
109                         float childHeight = childLayout.MeasuredHeight.Size.AsDecimal();
110
111                         // Determine the width and height needed by the children using their given position and size.
112                         // Children could overlap so find the left most and right most child.
113                         Position2D childPosition = childLayout.Owner.Position2D;
114                         float childLeft = childPosition.X;
115                         float childTop = childPosition.Y;
116
117                         // Store current width and height needed to contain all children.
118                         Extents childMargin = childLayout.Margin;
119                         totalWidth = childWidth + childMargin.Start + childMargin.End;
120                         totalHeight = childHeight + childMargin.Top + childMargin.Bottom;
121
122                         if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
123                         {
124                             childWidthState = MeasuredSize.StateType.MeasuredSizeTooSmall;
125                         }
126                         if (childLayout.MeasuredHeight.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
127                         {
128                             childHeightState = MeasuredSize.StateType.MeasuredSizeTooSmall;
129                         }
130                     }
131                 }
132
133
134                 MeasuredSize widthSizeAndState = ResolveSizeAndState(new LayoutLength(totalWidth + Padding.Start + Padding.End), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
135                 MeasuredSize heightSizeAndState = ResolveSizeAndState(new LayoutLength(totalHeight + Padding.Top + Padding.Bottom), heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
136                 totalWidth = widthSizeAndState.Size.AsDecimal();
137                 totalHeight = heightSizeAndState.Size.AsDecimal();
138
139                 // Ensure layout respects it's given minimum size
140                 totalWidth = Math.Max(totalWidth, SuggestedMinimumWidth.AsDecimal());
141                 totalHeight = Math.Max(totalHeight, SuggestedMinimumHeight.AsDecimal());
142
143                 widthSizeAndState.State = childWidthState;
144                 heightSizeAndState.State = childHeightState;
145
146                 SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(totalWidth + Padding.Start + Padding.End), widthMeasureSpec, childWidthState),
147                                        ResolveSizeAndState(new LayoutLength(totalHeight + Padding.Top + Padding.Bottom), heightMeasureSpec, childHeightState));
148
149                 // Size of ScrollableBase is changed. Change Page width too.
150                 scrollableBase.mPageWidth = (int)MeasuredWidth.Size.AsRoundedValue();
151                 scrollableBase.OnScrollingChildRelayout(null , null);
152             }
153
154             protected override void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
155             {
156                 foreach (LayoutItem childLayout in LayoutChildren)
157                 {
158                     if (childLayout != null)
159                     {
160                         LayoutLength childWidth = childLayout.MeasuredWidth.Size;
161                         LayoutLength childHeight = childLayout.MeasuredHeight.Size;
162
163                         Position2D childPosition = childLayout.Owner.Position2D;
164                         Extents padding = Padding;
165                         Extents childMargin = childLayout.Margin;
166
167                         LayoutLength childLeft = new LayoutLength(childPosition.X + childMargin.Start + padding.Start);
168                         LayoutLength childTop = new LayoutLength(childPosition.Y + childMargin.Top + padding.Top);
169
170                         childLayout.Layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
171                     }
172                 }
173             }
174         } //  ScrollableBaseCustomLayout
175
176         /// <summary>
177         /// The direction axis to scroll.
178         /// </summary>
179         /// <since_tizen> 8 </since_tizen>
180         public enum Direction
181         {
182             /// <summary>
183             /// Horizontal axis.
184             /// </summary>
185             /// <since_tizen> 8 </since_tizen>
186             Horizontal,
187
188             /// <summary>
189             /// Vertical axis.
190             /// </summary>
191             /// <since_tizen> 8 </since_tizen>
192             Vertical
193         }
194
195         /// <summary>
196         /// Scrolling direction mode.
197         /// Default is Vertical scrolling.
198         /// </summary>
199         /// <since_tizen> 8 </since_tizen>
200         public Direction ScrollingDirection
201         {
202             get
203             {
204                 return mScrollingDirection;
205             }
206             set
207             {
208                 if (value != mScrollingDirection)
209                 {
210                     mScrollingDirection = value;
211                     mPanGestureDetector.RemoveDirection(value == Direction.Horizontal ?
212                         PanGestureDetector.DirectionVertical : PanGestureDetector.DirectionHorizontal);
213                     mPanGestureDetector.AddDirection(value == Direction.Horizontal ?
214                         PanGestureDetector.DirectionHorizontal : PanGestureDetector.DirectionVertical);
215
216                     ContentContainer.WidthSpecification = mScrollingDirection == Direction.Vertical ?
217                         LayoutParamPolicies.MatchParent : LayoutParamPolicies.WrapContent;
218                     ContentContainer.HeightSpecification = mScrollingDirection == Direction.Vertical ?
219                         LayoutParamPolicies.WrapContent : LayoutParamPolicies.MatchParent;
220                 }
221             }
222         }
223
224         /// <summary>
225         /// Enable or disable scrolling.
226         /// </summary>
227         /// <since_tizen> 8 </since_tizen>
228         public bool ScrollEnabled
229         {
230             get
231             {
232                 return mScrollEnabled;
233             }
234             set
235             {
236                 if (value != mScrollEnabled)
237                 {
238                     mScrollEnabled = value;
239                     if (mScrollEnabled)
240                     {
241                         mPanGestureDetector.Detected += OnPanGestureDetected;
242                     }
243                     else
244                     {
245                         mPanGestureDetector.Detected -= OnPanGestureDetected;
246                     }
247                 }
248             }
249         }
250
251         /// <summary>
252         /// Pages mode, enables moving to the next or return to current page depending on pan displacement.
253         /// Default is false.
254         /// </summary>
255         /// <since_tizen> 8 </since_tizen>
256         public bool SnapToPage { set; get; } = false;
257
258         /// <summary>
259         /// Get current page.
260         /// Working property with SnapToPage property.
261         /// </summary>
262         /// <since_tizen> 8 </since_tizen>
263         public int CurrentPage { get; private set; } = 0;
264
265         /// <summary>
266         /// Duration of scroll animation.
267         /// Default value is 125ms.
268         /// </summary>
269         /// <since_tizen> 8 </since_tizen>
270         public int ScrollDuration
271         {
272             set
273             {
274                 mScrollDuration = value >= 0 ? value : mScrollDuration;
275             }
276             get
277             {
278                 return mScrollDuration;
279             }
280         }
281
282         /// <summary>
283         /// Scroll Available area.
284         /// </summary>
285         /// <since_tizen> 8 </since_tizen>
286         public Vector2 ScrollAvailableArea { set; get; }
287
288         /// <summary>
289         /// An event emitted when user starts dragging ScrollableBase, user can subscribe or unsubscribe to this event handler.<br />
290         /// </summary>
291         /// <since_tizen> 8 </since_tizen>
292         public event EventHandler<ScrollEventArgs> ScrollDragStarted;
293
294         /// <summary>
295         /// An event emitted when user stops dragging ScrollableBase, user can subscribe or unsubscribe to this event handler.<br />
296         /// </summary>
297         /// <since_tizen> 8 </since_tizen>
298         public event EventHandler<ScrollEventArgs> ScrollDragEnded;
299
300
301         /// <summary>
302         /// An event emitted when the scrolling slide animation starts, user can subscribe or unsubscribe to this event handler.<br />
303         /// </summary>
304         /// <since_tizen> 8 </since_tizen>
305         public event EventHandler<ScrollEventArgs> ScrollAnimationStarted;
306
307         /// <summary>
308         /// An event emitted when the scrolling slide animation ends, user can subscribe or unsubscribe to this event handler.<br />
309         /// </summary>
310         /// <since_tizen> 8 </since_tizen>
311         public event EventHandler<ScrollEventArgs> ScrollAnimationEnded;
312
313
314         /// <summary>
315         /// An event emitted when scrolling, user can subscribe or unsubscribe to this event handler.<br />
316         /// </summary>
317         /// <since_tizen> 8 </since_tizen>
318         public event EventHandler<ScrollEventArgs> Scrolling;
319
320
321         /// <summary>
322         /// Scrollbar for ScrollableBase.
323         /// </summary>
324         /// <since_tizen> 8 </since_tizen>
325         public ScrollbarBase Scrollbar
326         {
327             get
328             {
329                 return scrollBar;
330             }
331             set
332             {
333                 if (scrollBar)
334                 {
335                     scrollBar.Unparent();
336                 }
337                 scrollBar = value;
338
339                 if (scrollBar != null)
340                 {
341                     scrollBar.Name = "ScrollBar";
342                     base.Add(scrollBar);
343
344                     if (hideScrollbar)
345                     {
346                         scrollBar.Hide();
347                     }
348                     else
349                     {
350                         scrollBar.Show();
351                     }
352
353                     SetScrollbar();
354                 }
355             }
356         }
357
358         /// <summary>
359         /// Always hide Scrollbar.
360         /// </summary>
361         /// <since_tizen> 8 </since_tizen>
362         public bool HideScrollbar
363         {
364             get
365             {
366                 return hideScrollbar;
367             }
368             set
369             {
370                 hideScrollbar = value;
371
372                 if (scrollBar)
373                 {
374                     if (value)
375                     {
376                         scrollBar.Hide();
377                     }
378                     else
379                     {
380                         scrollBar.Show();
381                     }
382                 }
383             }
384         }
385
386         /// <summary>
387         /// Container which has content of ScrollableBase.
388         /// </summary>
389         /// <since_tizen> 8 </since_tizen>
390         public View ContentContainer { get; private set; }
391
392         /// <summary>
393         /// Set the layout on this View. Replaces any existing Layout.
394         /// </summary>
395         /// <since_tizen> 8 </since_tizen>
396         public new LayoutItem Layout
397         {
398             get
399             {
400                 return ContentContainer.Layout;
401             }
402             set
403             {
404                 ContentContainer.Layout = value;
405                 if (ContentContainer.Layout != null)
406                 {
407                     ContentContainer.Layout.SetPositionByLayout = false;
408                 }
409             }
410         }
411
412         /// <summary>
413         /// List of children of Container.
414         /// </summary>
415         /// <since_tizen> 8 </since_tizen>
416         public new List<View> Children
417         {
418             get
419             {
420                 return ContentContainer.Children;
421             }
422         }
423
424         /// <summary>
425         /// Deceleration rate of scrolling by finger.
426         /// Rate should be bigger than 0 and smaller than 1.
427         /// Default value is 0.998f;
428         /// </summary>
429         /// <since_tizen> 8 </since_tizen>
430         public float DecelerationRate
431         {
432             get
433             {
434                 return decelerationRate;
435             }
436             set
437             {
438                 decelerationRate = (value < 1 && value > 0) ? value : decelerationRate;
439                 logValueOfDeceleration = (float)Math.Log(value);
440             }
441         }
442
443         /// <summary>
444         /// Threashold not to go infinit at the end of scrolling animation.
445         /// </summary>
446         [EditorBrowsable(EditorBrowsableState.Never)]
447         public float DecelerationThreshold { get; set; } = 0.1f;
448
449         /// <summary>
450         /// Scrolling event will be thrown when this amount of scroll positino is changed.
451         /// </summary>
452         [EditorBrowsable(EditorBrowsableState.Never)]
453         public float ScrollingEventThreshold
454         {
455             get
456             {
457                 return mScrollingEventThreshold;
458             }
459             set
460             {
461                 if (mScrollingEventThreshold != value && value > 0)
462                 {
463                     ContentContainer.RemovePropertyNotification(propertyNotification);
464                     propertyNotification = ContentContainer.AddPropertyNotification("position", PropertyCondition.Step(value));
465                     propertyNotification.Notified += OnPropertyChanged;
466                     mScrollingEventThreshold = value;
467                 }
468             }
469         }
470
471
472         /// <summary>
473         /// Page will be changed when velocity of panning is over threshold.
474         /// The unit of threshold is pixel per milisec.
475         /// </summary>
476         /// <since_tizen> 8 </since_tizen>
477         public float PageFlickThreshold
478         {
479             get
480             {
481                 return mPageFlickThreshold;
482             }
483             set
484             {
485                 mPageFlickThreshold = value >= 0f ? value : mPageFlickThreshold;
486             }
487         }
488
489         /// <summary>
490         /// Alphafunction for scroll animation.
491         /// </summary>
492         [EditorBrowsable(EditorBrowsableState.Never)]
493         public AlphaFunction ScrollAlphaFunction { get; set; } = new AlphaFunction(AlphaFunction.BuiltinFunctions.Linear);
494
495         private bool hideScrollbar = true;
496         private float maxScrollDistance;
497         private float childTargetPosition = 0.0f;
498         private PanGestureDetector mPanGestureDetector;
499         private View mInterruptTouchingChild;
500         private ScrollbarBase scrollBar;
501         private bool scrolling = false;
502         private float ratioOfScreenWidthToCompleteScroll = 0.5f;
503         private float totalDisplacementForPan = 0.0f;
504         private Size previousContainerSize = new Size();
505         private Size previousSize = new Size();
506         private PropertyNotification propertyNotification;
507         private float noticeAnimationEndBeforePosition = 0.0f;
508         private bool readyToNotice = false;
509
510         /// <summary>
511         /// Notice before animation is finished.
512         /// </summary>
513         [EditorBrowsable(EditorBrowsableState.Never)]
514         // Let's consider more whether this needs to be set as protected.
515         public float NoticeAnimationEndBeforePosition { get => noticeAnimationEndBeforePosition; set => noticeAnimationEndBeforePosition = value; }
516
517         // Let's consider more whether this needs to be set as protected.
518         private float finalTargetPosition;
519
520         private Animation scrollAnimation;
521         // Declare user alpha function delegate
522         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
523         private delegate float UserAlphaFunctionDelegate(float progress);
524         private UserAlphaFunctionDelegate customScrollAlphaFunction;
525         private float velocityOfLastPan = 0.0f;
526         private float panAnimationDuration = 0.0f;
527         private float panAnimationDelta = 0.0f;
528         private float logValueOfDeceleration = 0.0f;
529         private float decelerationRate = 0.0f;
530
531         private View mVerticalTopShadowView;
532         private View mVerticalBottomShadowView;
533         private const int mVerticalShadowScaleHeightLimit = 64 * 3;
534         private const int mVerticalShadowAnimationDuration = 300;
535         private Animation mVerticalShadowAnimation;
536         private bool isVerticalShadowShown = false;
537         private float mStartShowShadowDisplacement;
538
539         /// <summary>
540         /// Default Constructor
541         /// </summary>
542         /// <since_tizen> 8 </since_tizen>
543         public ScrollableBase() : base()
544         {
545             DecelerationRate = 0.998f;
546
547             base.Layout = new ScrollableBaseCustomLayout();
548             mPanGestureDetector = new PanGestureDetector();
549             mPanGestureDetector.Attach(this);
550             mPanGestureDetector.AddDirection(PanGestureDetector.DirectionVertical);
551             mPanGestureDetector.Detected += OnPanGestureDetected;
552
553             ClippingMode = ClippingModeType.ClipChildren;
554
555             //Default Scrolling child
556             ContentContainer = new View()
557             {
558                 WidthSpecification = ScrollingDirection == Direction.Vertical ? LayoutParamPolicies.MatchParent : LayoutParamPolicies.WrapContent,
559                 HeightSpecification = ScrollingDirection == Direction.Vertical ? LayoutParamPolicies.WrapContent : LayoutParamPolicies.MatchParent,
560                 Layout = new AbsoluteLayout() { SetPositionByLayout = false },
561             };
562             ContentContainer.Relayout += OnScrollingChildRelayout;
563             propertyNotification = ContentContainer.AddPropertyNotification("position", PropertyCondition.Step(mScrollingEventThreshold));
564             propertyNotification.Notified += OnPropertyChanged;
565             base.Add(ContentContainer);
566
567             //Interrupt touching when panning is started
568             mInterruptTouchingChild = new View()
569             {
570                 Size = new Size(Window.Instance.WindowSize),
571                 BackgroundColor = Color.Transparent,
572             };
573             mInterruptTouchingChild.TouchEvent += OnIterruptTouchingChildTouched;
574             Scrollbar = new Scrollbar();
575
576             //Show vertical shadow when panning down (or up) on the scroll top (or end).
577             mVerticalTopShadowView = new View
578             {
579                 BackgroundImage = StyleManager.GetFrameworkResourcePath("nui_component_default_scroll_over_shooting_top.png"),
580                 Opacity = 1.0f,
581                 SizeHeight = 0.0f,
582                 PositionUsesPivotPoint = true,
583                 ParentOrigin = NUI.ParentOrigin.TopCenter,
584                 PivotPoint = NUI.PivotPoint.TopCenter,
585             };
586             mVerticalBottomShadowView = new View
587             {
588                 BackgroundImage = StyleManager.GetFrameworkResourcePath("nui_component_default_scroll_over_shooting_bottom.png"),
589                 Opacity = 1.0f,
590                 SizeHeight = 0.0f,
591                 PositionUsesPivotPoint = true,
592                 ParentOrigin = NUI.ParentOrigin.BottomCenter,
593                 PivotPoint = NUI.PivotPoint.BottomCenter,
594             };
595
596             AccessibilityManager.Instance.SetAccessibilityAttribute(this, AccessibilityManager.AccessibilityAttribute.Trait, "ScrollableBase");
597         }
598
599         private bool OnIterruptTouchingChildTouched(object source, View.TouchEventArgs args)
600         {
601             if (args.Touch.GetState(0) == PointStateType.Down)
602             {
603                 if (scrolling && !SnapToPage)
604                 {
605                     StopScroll();
606                 }
607             }
608             return true;
609         }
610
611         private void OnPropertyChanged(object source, PropertyNotification.NotifyEventArgs args)
612         {
613             OnScroll();
614         }
615
616         /// <summary>
617         /// Called after a child has been added to the owning view.
618         /// </summary>
619         /// <param name="view">The child which has been added.</param>
620         /// <since_tizen> 8 </since_tizen>
621         public override void Add(View view)
622         {
623             ContentContainer.Add(view);
624         }
625
626         /// <summary>
627         /// Called after a child has been removed from the owning view.
628         /// </summary>
629         /// <param name="view">The child which has been removed.</param>
630         /// <since_tizen> 8 </since_tizen>
631         public override void Remove(View view)
632         {
633             if (SnapToPage && CurrentPage == Children.IndexOf(view) && CurrentPage == Children.Count - 1)
634             {
635                 // Target View is current page and also last child.
636                 // CurrentPage should be changed to previous page.
637                 CurrentPage = Math.Max(0, CurrentPage - 1);
638                 ScrollToIndex(CurrentPage);
639             }
640
641             ContentContainer.Remove(view);
642         }
643
644         private void OnScrollingChildRelayout(object source, EventArgs args)
645         {
646             // Size is changed. Calculate maxScrollDistance.
647             bool isSizeChanged = previousContainerSize.Width != ContentContainer.Size.Width || previousContainerSize.Height != ContentContainer.Size.Height
648                 || previousSize.Width != Size.Width || previousSize.Height != Size.Height;
649
650             if (isSizeChanged)
651             {
652                 maxScrollDistance = CalculateMaximumScrollDistance();
653                 SetScrollbar();
654             }
655
656             previousContainerSize = ContentContainer.Size;
657             previousSize = Size;
658         }
659
660         /// <summary>
661         /// The composition of a Scrollbar can vary depending on how you use ScrollableBase.
662         /// Set the composition that will go into the ScrollableBase according to your ScrollableBase.
663         /// </summary>
664         /// <since_tizen> 8 </since_tizen>
665         [EditorBrowsable(EditorBrowsableState.Never)]
666         protected virtual void SetScrollbar()
667         {
668             if (Scrollbar)
669             {
670                 bool isHorizontal = ScrollingDirection == Direction.Horizontal;
671                 float contentLength = isHorizontal ? ContentContainer.Size.Width : ContentContainer.Size.Height;
672                 float viewportLength = isHorizontal ? Size.Width : Size.Height;
673                 float currentPosition = isHorizontal ? ContentContainer.CurrentPosition.X : ContentContainer.CurrentPosition.Y;
674                 Scrollbar.Initialize(contentLength, viewportLength, currentPosition, isHorizontal);
675             }
676         }
677
678         /// <summary>
679         /// Scrolls to the item at the specified index.
680         /// </summary>
681         /// <param name="index">Index of item.</param>
682         /// <since_tizen> 8 </since_tizen>
683         public void ScrollToIndex(int index)
684         {
685             if (ContentContainer.ChildCount - 1 < index || index < 0)
686             {
687                 return;
688             }
689
690             if (SnapToPage)
691             {
692                 CurrentPage = index;
693             }
694
695             float targetPosition = Math.Min(ScrollingDirection == Direction.Vertical ? Children[index].Position.Y : Children[index].Position.X, maxScrollDistance);
696             AnimateChildTo(ScrollDuration, -targetPosition);
697         }
698
699         private void OnScrollDragStarted()
700         {
701             ScrollEventArgs eventArgs = new ScrollEventArgs(ContentContainer.CurrentPosition);
702             ScrollDragStarted?.Invoke(this, eventArgs);
703         }
704
705         private void OnScrollDragEnded()
706         {
707             ScrollEventArgs eventArgs = new ScrollEventArgs(ContentContainer.CurrentPosition);
708             ScrollDragEnded?.Invoke(this, eventArgs);
709         }
710
711         private void OnScrollAnimationStarted()
712         {
713             ScrollEventArgs eventArgs = new ScrollEventArgs(ContentContainer.CurrentPosition);
714             ScrollAnimationStarted?.Invoke(this, eventArgs);
715         }
716
717         private void OnScrollAnimationEnded()
718         {
719             scrolling = false;
720             base.Remove(mInterruptTouchingChild);
721
722             ScrollEventArgs eventArgs = new ScrollEventArgs(ContentContainer.CurrentPosition);
723             ScrollAnimationEnded?.Invoke(this, eventArgs);
724         }
725
726         private void OnScroll()
727         {
728             ScrollEventArgs eventArgs = new ScrollEventArgs(ContentContainer.CurrentPosition);
729             Scrolling?.Invoke(this, eventArgs);
730
731             bool isHorizontal = ScrollingDirection == Direction.Horizontal;
732             float contentLength = isHorizontal ? ContentContainer.Size.Width : ContentContainer.Size.Height;
733             float currentPosition = isHorizontal ? ContentContainer.CurrentPosition.X : ContentContainer.CurrentPosition.Y;
734
735             scrollBar?.Update(contentLength, Math.Abs(currentPosition));
736             CheckPreReachedTargetPosition();
737         }
738
739         private void CheckPreReachedTargetPosition()
740         {
741             // Check whether we reached pre-reached target position
742             if (readyToNotice &&
743                 ContentContainer.CurrentPosition.Y <= finalTargetPosition + NoticeAnimationEndBeforePosition &&
744                 ContentContainer.CurrentPosition.Y >= finalTargetPosition - NoticeAnimationEndBeforePosition)
745             {
746                 //Notice first
747                 readyToNotice = false;
748                 OnPreReachedTargetPosition(finalTargetPosition);
749             }
750         }
751
752         /// <summary>
753         /// This helps developer who wants to know before scroll is reaching target position.
754         /// </summary>
755         /// <param name="targetPosition">Index of item.</param>
756         /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
757         [EditorBrowsable(EditorBrowsableState.Never)]
758         protected virtual void OnPreReachedTargetPosition(float targetPosition)
759         {
760
761         }
762
763         private void StopScroll()
764         {
765             if (scrollAnimation != null)
766             {
767                 if (scrollAnimation.State == Animation.States.Playing)
768                 {
769                     Debug.WriteLineIf(LayoutDebugScrollableBase, "StopScroll Animation Playing");
770                     scrollAnimation.Stop(Animation.EndActions.Cancel);
771                     OnScrollAnimationEnded();
772                 }
773                 scrollAnimation.Clear();
774             }
775         }
776
777         private void AnimateChildTo(int duration, float axisPosition)
778         {
779             Debug.WriteLineIf(LayoutDebugScrollableBase, "AnimationTo Animation Duration:" + duration + " Destination:" + axisPosition);
780             finalTargetPosition = axisPosition;
781
782             StopScroll(); // Will replace previous animation so will stop existing one.
783
784             if (scrollAnimation == null)
785             {
786                 scrollAnimation = new Animation();
787                 scrollAnimation.Finished += ScrollAnimationFinished;
788             }
789
790             scrollAnimation.Duration = duration;
791             scrollAnimation.DefaultAlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.EaseOutSquare);
792             scrollAnimation.AnimateTo(ContentContainer, (ScrollingDirection == Direction.Horizontal) ? "PositionX" : "PositionY", axisPosition, ScrollAlphaFunction);
793             scrolling = true;
794             OnScrollAnimationStarted();
795             scrollAnimation.Play();
796         }
797
798         /// <summary>
799         /// Scroll to specific position with or without animation.
800         /// </summary>
801         /// <param name="position">Destination.</param>
802         /// <param name="animate">Scroll with or without animation</param>
803         /// <since_tizen> 8 </since_tizen>
804         public void ScrollTo(float position, bool animate)
805         {
806             float currentPositionX = ContentContainer.CurrentPosition.X != 0 ? ContentContainer.CurrentPosition.X : ContentContainer.Position.X;
807             float currentPositionY = ContentContainer.CurrentPosition.Y != 0 ? ContentContainer.CurrentPosition.Y : ContentContainer.Position.Y;
808             float delta = ScrollingDirection == Direction.Horizontal ? currentPositionX : currentPositionY;
809             // The argument position is the new pan position. So the new position of ScrollableBase becomes (-position).
810             // To move ScrollableBase's position to (-position), it moves by (-position - currentPosition).
811             delta = -position - delta;
812
813             ScrollBy(delta, animate);
814         }
815
816         private float BoundScrollPosition(float targetPosition)
817         {
818             if (ScrollAvailableArea != null)
819             {
820                 float minScrollPosition = ScrollAvailableArea.X;
821                 float maxScrollPosition = ScrollAvailableArea.Y;
822
823                 targetPosition = Math.Min(-minScrollPosition, targetPosition);
824                 targetPosition = Math.Max(-maxScrollPosition, targetPosition);
825             }
826             else
827             {
828                 targetPosition = Math.Min(0, targetPosition);
829                 targetPosition = Math.Max(-maxScrollDistance, targetPosition);
830             }
831
832             return targetPosition;
833         }
834
835         private void ScrollBy(float displacement, bool animate)
836         {
837             if (GetChildCount() == 0 || maxScrollDistance < 0)
838             {
839                 return;
840             }
841
842             float childCurrentPosition = (ScrollingDirection == Direction.Horizontal) ? ContentContainer.PositionX : ContentContainer.PositionY;
843
844             Debug.WriteLineIf(LayoutDebugScrollableBase, "ScrollBy childCurrentPosition:" + childCurrentPosition +
845                                                    " displacement:" + displacement,
846                                                    " maxScrollDistance:" + maxScrollDistance);
847
848             childTargetPosition = childCurrentPosition + displacement; // child current position + gesture displacement
849
850
851             Debug.WriteLineIf(LayoutDebugScrollableBase, "ScrollBy currentAxisPosition:" + childCurrentPosition + "childTargetPosition:" + childTargetPosition);
852
853             if (animate)
854             {
855                 // Calculate scroll animaton duration
856                 float scrollDistance = Math.Abs(displacement);
857                 readyToNotice = true;
858
859                 AnimateChildTo(ScrollDuration, BoundScrollPosition(AdjustTargetPositionOfScrollAnimation(BoundScrollPosition(childTargetPosition))));
860             }
861             else
862             {
863                 finalTargetPosition = BoundScrollPosition(childTargetPosition);
864
865                 // Set position of scrolling child without an animation
866                 if (ScrollingDirection == Direction.Horizontal)
867                 {
868                     ContentContainer.PositionX = finalTargetPosition;
869                 }
870                 else
871                 {
872                     ContentContainer.PositionY = finalTargetPosition;
873                 }
874             }
875         }
876
877         /// <summary>
878         /// you can override it to clean-up your own resources.
879         /// </summary>
880         /// <param name="type">DisposeTypes</param>
881         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
882         [EditorBrowsable(EditorBrowsableState.Never)]
883         protected override void Dispose(DisposeTypes type)
884         {
885             if (disposed)
886             {
887                 return;
888             }
889
890             if (type == DisposeTypes.Explicit)
891             {
892                 StopVerticalShadowAnimation();
893                 StopScroll();
894
895                 if (mPanGestureDetector != null)
896                 {
897                     mPanGestureDetector.Detected -= OnPanGestureDetected;
898                     mPanGestureDetector.Dispose();
899                     mPanGestureDetector = null;
900                 }
901
902                 propertyNotification.Dispose();
903             }
904             base.Dispose(type);
905         }
906
907         private float CalculateMaximumScrollDistance()
908         {
909             float scrollingChildLength = 0;
910             float scrollerLength = 0;
911             if (ScrollingDirection == Direction.Horizontal)
912             {
913                 Debug.WriteLineIf(LayoutDebugScrollableBase, "Horizontal");
914
915                 scrollingChildLength = ContentContainer.Size.Width;
916                 scrollerLength = Size.Width;
917             }
918             else
919             {
920                 Debug.WriteLineIf(LayoutDebugScrollableBase, "Vertical");
921                 scrollingChildLength = ContentContainer.Size.Height;
922                 scrollerLength = Size.Height;
923             }
924
925             Debug.WriteLineIf(LayoutDebugScrollableBase, "ScrollBy maxScrollDistance:" + (scrollingChildLength - scrollerLength) +
926                                                    " parent length:" + scrollerLength +
927                                                    " scrolling child length:" + scrollingChildLength);
928
929             return Math.Max(scrollingChildLength - scrollerLength, 0);
930         }
931
932         private void PageSnap(float velocity)
933         {
934             Debug.WriteLineIf(LayoutDebugScrollableBase, "PageSnap with pan candidate totalDisplacement:" + totalDisplacementForPan +
935                                                                 " currentPage[" + CurrentPage + "]");
936
937             //Increment current page if total displacement enough to warrant a page change.
938             if (Math.Abs(totalDisplacementForPan) > (mPageWidth * ratioOfScreenWidthToCompleteScroll))
939             {
940                 if (totalDisplacementForPan < 0)
941                 {
942                     CurrentPage = Math.Min(Math.Max(Children.Count - 1, 0), ++CurrentPage);
943                 }
944                 else
945                 {
946                     CurrentPage = Math.Max(0, --CurrentPage);
947                 }
948             }
949             else if (Math.Abs(velocity) > PageFlickThreshold)
950             {
951                 if (velocity < 0)
952                 {
953                     CurrentPage = Math.Min(Math.Max(Children.Count - 1, 0), ++CurrentPage);
954                 }
955                 else
956                 {
957                     CurrentPage = Math.Max(0, --CurrentPage);
958                 }
959             }
960
961             // Animate to new page or reposition to current page
962             float destinationX = -(Children[CurrentPage].Position.X + Children[CurrentPage].CurrentSize.Width / 2 - CurrentSize.Width / 2); // set to middle of current page
963             Debug.WriteLineIf(LayoutDebugScrollableBase, "Snapping to page[" + CurrentPage + "] to:" + destinationX + " from:" + ContentContainer.PositionX);
964             AnimateChildTo(ScrollDuration, destinationX);
965         }
966
967         private void AttachShadowView()
968         {
969             // stop animation if necessary.
970             StopVerticalShadowAnimation();
971
972             base.Add(mVerticalTopShadowView);
973             base.Add(mVerticalBottomShadowView);
974
975             mVerticalTopShadowView.Size = new Size(SizeWidth, 0.0f);
976             mVerticalTopShadowView.Opacity = 1.0f;
977
978             mVerticalBottomShadowView.Size = new Size(SizeWidth, 0.0f);
979             mVerticalBottomShadowView.Opacity = 1.0f;
980
981             // at the beginning, height of vertical shadow is 0, so it is invisible.
982             isVerticalShadowShown = false;
983         }
984
985         private void DragVerticalShadow(float displacement)
986         {
987             if ((int)displacement > 0) // downwards
988             {
989                 // check if reaching at the top.
990                 if ((int)finalTargetPosition != 0)
991                     return;
992
993                 // save start displacement, and re-calculate displacement.
994                 if (!isVerticalShadowShown)
995                 {
996                     mStartShowShadowDisplacement = displacement;
997                 }
998                 isVerticalShadowShown = true;
999
1000                 float newDisplacement = displacement < mStartShowShadowDisplacement ? 0 : displacement - mStartShowShadowDisplacement;
1001
1002                 // scale limit of width is 60%.
1003                 float widthScale = newDisplacement / mVerticalShadowScaleHeightLimit;
1004                 mVerticalTopShadowView.SizeWidth = widthScale > 0.6f ? SizeWidth * 0.4f : SizeWidth * (1.0f - widthScale);
1005
1006                 // scale limit of height is 300%.
1007                 mVerticalTopShadowView.SizeHeight = newDisplacement > mVerticalShadowScaleHeightLimit ? mVerticalShadowScaleHeightLimit : newDisplacement;
1008             }
1009             else if ((int)displacement < 0) // upwards
1010             {
1011                 // check if reaching at the bottom.
1012                 if (-(int)finalTargetPosition != (int)maxScrollDistance)
1013                     return;
1014
1015                 // save start displacement, and re-calculate displacement.
1016                 if (!isVerticalShadowShown)
1017                 {
1018                     mStartShowShadowDisplacement = displacement;
1019                 }
1020                 isVerticalShadowShown = true;
1021
1022                 float newDisplacement = mStartShowShadowDisplacement < displacement ? 0 : mStartShowShadowDisplacement - displacement;
1023
1024                 // scale limit of width is 60%.
1025                 float widthScale = newDisplacement / mVerticalShadowScaleHeightLimit;
1026                 mVerticalBottomShadowView.SizeWidth = widthScale > 0.6f ? SizeWidth * 0.4f : SizeWidth * (1.0f - widthScale);
1027
1028                 // scale limit of height is 300%.
1029                 mVerticalBottomShadowView.SizeHeight = newDisplacement > mVerticalShadowScaleHeightLimit ? mVerticalShadowScaleHeightLimit : newDisplacement;
1030             }
1031             else
1032             {
1033                 // if total displacement is 0, shadow would become invisible.
1034                 isVerticalShadowShown = false;
1035             }
1036         }
1037
1038         private void PlayVerticalShadowAnimation()
1039         {
1040             // stop animation if necessary.
1041             StopVerticalShadowAnimation();
1042
1043             if (mVerticalShadowAnimation == null)
1044             {
1045                 mVerticalShadowAnimation = new Animation(mVerticalShadowAnimationDuration);
1046                 mVerticalShadowAnimation.Finished += OnVerticalShadowAnimationFinished;
1047             }
1048
1049             View targetView = totalDisplacementForPan < 0 ? mVerticalBottomShadowView : mVerticalTopShadowView;
1050             mVerticalShadowAnimation.AnimateTo(targetView, "SizeWidth", SizeWidth);
1051             mVerticalShadowAnimation.AnimateTo(targetView, "SizeHeight", 0.0f);
1052             mVerticalShadowAnimation.AnimateTo(targetView, "Opacity", 0.0f);
1053             mVerticalShadowAnimation.Play();
1054         }
1055
1056         private void StopVerticalShadowAnimation()
1057         {
1058             if (mVerticalShadowAnimation == null || mVerticalShadowAnimation.State != Animation.States.Playing)
1059                 return;
1060
1061             Debug.WriteLineIf(LayoutDebugScrollableBase, "gesture finished. Stop Vertical Shadow Animation Playing.");
1062             mVerticalShadowAnimation.Stop(Animation.EndActions.Cancel);
1063             OnVerticalShadowAnimationFinished(null, null);
1064             mVerticalShadowAnimation.Clear();
1065         }
1066
1067         private void OnVerticalShadowAnimationFinished(object sender, EventArgs e)
1068         {
1069             base.Remove(mVerticalTopShadowView);
1070             base.Remove(mVerticalBottomShadowView);
1071
1072             mVerticalTopShadowView.Size = new Size(SizeWidth, 0.0f);
1073             mVerticalBottomShadowView.Size = new Size(SizeWidth, 0.0f);
1074
1075             // after animation finished, height & opacity of vertical shadow both are 0, so it is invisible.
1076             isVerticalShadowShown = false;
1077         }
1078
1079         private void OnPanGestureDetected(object source, PanGestureDetector.DetectedEventArgs e)
1080         {
1081             OnPanGesture(e.PanGesture);
1082         }
1083
1084         private void OnPanGesture(PanGesture panGesture)
1085         {
1086             if (SnapToPage && scrollAnimation != null && scrollAnimation.State == Animation.States.Playing)
1087             {
1088                 return;
1089             }
1090
1091             if (panGesture.State == Gesture.StateType.Started)
1092             {
1093                 readyToNotice = false;
1094                 base.Add(mInterruptTouchingChild);
1095                 AttachShadowView();
1096                 Debug.WriteLineIf(LayoutDebugScrollableBase, "Gesture Start");
1097                 if (scrolling && !SnapToPage)
1098                 {
1099                     StopScroll();
1100                 }
1101                 totalDisplacementForPan = 0.0f;
1102                 OnScrollDragStarted();
1103             }
1104             else if (panGesture.State == Gesture.StateType.Continuing)
1105             {
1106                 if (ScrollingDirection == Direction.Horizontal)
1107                 {
1108                     ScrollBy(panGesture.Displacement.X, false);
1109                     totalDisplacementForPan += panGesture.Displacement.X;
1110                 }
1111                 else
1112                 {
1113                     // if vertical shadow is shown, does not scroll.
1114                     if (!isVerticalShadowShown)
1115                     {
1116                         ScrollBy(panGesture.Displacement.Y, false);
1117                     }
1118                     totalDisplacementForPan += panGesture.Displacement.Y;
1119                     DragVerticalShadow(totalDisplacementForPan);
1120                 }
1121                 Debug.WriteLineIf(LayoutDebugScrollableBase, "OnPanGestureDetected Continue totalDisplacementForPan:" + totalDisplacementForPan);
1122             }
1123             else if (panGesture.State == Gesture.StateType.Finished || panGesture.State == Gesture.StateType.Cancelled)
1124             {
1125                 PlayVerticalShadowAnimation();
1126                 OnScrollDragEnded();
1127                 StopScroll(); // Will replace previous animation so will stop existing one.
1128
1129                 if (scrollAnimation == null)
1130                 {
1131                     scrollAnimation = new Animation();
1132                     scrollAnimation.Finished += ScrollAnimationFinished;
1133                 }
1134
1135                 float panVelocity = (ScrollingDirection == Direction.Horizontal) ? panGesture.Velocity.X : panGesture.Velocity.Y;
1136
1137                 if (SnapToPage)
1138                 {
1139                     PageSnap(panVelocity);
1140                 }
1141                 else
1142                 {
1143                     if (panVelocity == 0)
1144                     {
1145                         float currentScrollPosition = (ScrollingDirection == Direction.Horizontal ? ContentContainer.CurrentPosition.X : ContentContainer.CurrentPosition.Y);
1146                         scrollAnimation.DefaultAlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.Linear);
1147                         scrollAnimation.Duration = 0;
1148                         scrollAnimation.AnimateTo(ContentContainer, (ScrollingDirection == Direction.Horizontal) ? "PositionX" : "PositionY", currentScrollPosition);
1149                         scrollAnimation.Play();
1150                     }
1151                     else
1152                     {
1153                         Decelerating(panVelocity, scrollAnimation);
1154                     }
1155                 }
1156
1157                 totalDisplacementForPan = 0;
1158                 scrolling = true;
1159                 readyToNotice = true;
1160                 OnScrollAnimationStarted();
1161             }
1162         }
1163
1164         internal override bool OnAccessibilityPan(PanGesture gestures)
1165         {
1166             if (SnapToPage && scrollAnimation != null && scrollAnimation.State == Animation.States.Playing)
1167             {
1168                 return false;
1169             }
1170
1171             OnPanGesture(gestures);
1172             return true;
1173         }
1174
1175         private float CustomScrollAlphaFunction(float progress)
1176         {
1177             if (panAnimationDelta == 0)
1178             {
1179                 return 1.0f;
1180             }
1181             else
1182             {
1183                 // Parameter "progress" is normalized value. We need to multiply target duration to calculate distance.
1184                 // Can get real distance using equation of deceleration (check Decelerating function)
1185                 // After get real distance, normalize it
1186                 float realDuration = progress * panAnimationDuration;
1187                 float realDistance = velocityOfLastPan * ((float)Math.Pow(decelerationRate, realDuration) - 1) / logValueOfDeceleration;
1188                 float result = Math.Min(realDistance / Math.Abs(panAnimationDelta), 1.0f);
1189                 return result;
1190             }
1191         }
1192
1193         /// <summary>
1194         /// you can override it to custom your decelerating
1195         /// </summary>
1196         /// <param name="velocity">Velocity of current pan.</param>
1197         /// <param name="animation">Scroll animation.</param>
1198         [EditorBrowsable(EditorBrowsableState.Never)]
1199         protected virtual void Decelerating(float velocity, Animation animation)
1200         {
1201             // Decelerating using deceleration equation ===========
1202             //
1203             // V   : velocity (pixel per milisecond)
1204             // V0  : initial velocity
1205             // d   : deceleration rate,
1206             // t   : time
1207             // X   : final position after decelerating
1208             // log : natural logarithm
1209             //
1210             // V(t) = V0 * d pow t;
1211             // X(t) = V0 * (d pow t - 1) / log d;  <-- Integrate the velocity function
1212             // X(∞) = V0 * d / (1 - d); <-- Result using inifit T can be final position because T is tending to infinity.
1213             //
1214             // Because of final T is tending to inifity, we should use threshold value to finish.
1215             // Final T = log(-threshold * log d / |V0| ) / log d;
1216
1217             velocityOfLastPan = Math.Abs(velocity);
1218
1219             float currentScrollPosition = -(ScrollingDirection == Direction.Horizontal ? ContentContainer.CurrentPosition.X : ContentContainer.CurrentPosition.Y);
1220             panAnimationDelta = (velocityOfLastPan * decelerationRate) / (1 - decelerationRate);
1221             panAnimationDelta = velocity > 0 ? -panAnimationDelta : panAnimationDelta;
1222
1223             float destination = -(panAnimationDelta + currentScrollPosition);
1224             float adjustDestination = AdjustTargetPositionOfScrollAnimation(destination);
1225             float maxPosition = ScrollAvailableArea != null ? ScrollAvailableArea.Y : maxScrollDistance;
1226             float minPosition = ScrollAvailableArea != null ? ScrollAvailableArea.X : 0;
1227
1228             if (destination < -maxPosition || destination > minPosition)
1229             {
1230                 panAnimationDelta = velocity > 0 ? (currentScrollPosition - minPosition) : (maxPosition - currentScrollPosition);
1231                 destination = velocity > 0 ? minPosition : -maxPosition;
1232
1233                 if (panAnimationDelta == 0)
1234                 {
1235                     panAnimationDuration = 0.0f;
1236                 }
1237                 else
1238                 {
1239                     panAnimationDuration = (float)Math.Log((panAnimationDelta * logValueOfDeceleration / velocityOfLastPan + 1), decelerationRate);
1240                 }
1241
1242                 Debug.WriteLineIf(LayoutDebugScrollableBase, "\n" +
1243                     "OverRange======================= \n" +
1244                     "[decelerationRate] " + decelerationRate + "\n" +
1245                     "[logValueOfDeceleration] " + logValueOfDeceleration + "\n" +
1246                     "[Velocity] " + velocityOfLastPan + "\n" +
1247                     "[CurrentPosition] " + currentScrollPosition + "\n" +
1248                     "[CandidateDelta] " + panAnimationDelta + "\n" +
1249                     "[Destination] " + destination + "\n" +
1250                     "[Duration] " + panAnimationDuration + "\n" +
1251                     "================================ \n"
1252                 );
1253             }
1254             else
1255             {
1256                 panAnimationDuration = (float)Math.Log(-DecelerationThreshold * logValueOfDeceleration / velocityOfLastPan) / logValueOfDeceleration;
1257
1258                 if (adjustDestination != destination)
1259                 {
1260                     destination = adjustDestination;
1261                     panAnimationDelta = destination + currentScrollPosition;
1262                     velocityOfLastPan = Math.Abs(panAnimationDelta * logValueOfDeceleration / ((float)Math.Pow(decelerationRate, panAnimationDuration) - 1));
1263                     panAnimationDuration = (float)Math.Log(-DecelerationThreshold * logValueOfDeceleration / velocityOfLastPan) / logValueOfDeceleration;
1264                 }
1265
1266                 Debug.WriteLineIf(LayoutDebugScrollableBase, "\n" +
1267                     "================================ \n" +
1268                     "[decelerationRate] " + decelerationRate + "\n" +
1269                     "[logValueOfDeceleration] " + logValueOfDeceleration + "\n" +
1270                     "[Velocity] " + velocityOfLastPan + "\n" +
1271                     "[CurrentPosition] " + currentScrollPosition + "\n" +
1272                     "[CandidateDelta] " + panAnimationDelta + "\n" +
1273                     "[Destination] " + destination + "\n" +
1274                     "[Duration] " + panAnimationDuration + "\n" +
1275                     "================================ \n"
1276                 );
1277             }
1278
1279             finalTargetPosition = destination;
1280
1281             customScrollAlphaFunction = new UserAlphaFunctionDelegate(CustomScrollAlphaFunction);
1282             animation.DefaultAlphaFunction = new AlphaFunction(customScrollAlphaFunction);
1283             GC.KeepAlive(customScrollAlphaFunction);
1284             animation.Duration = (int)panAnimationDuration;
1285             animation.AnimateTo(ContentContainer, (ScrollingDirection == Direction.Horizontal) ? "PositionX" : "PositionY", destination);
1286             animation.Play();
1287         }
1288
1289         private void ScrollAnimationFinished(object sender, EventArgs e)
1290         {
1291             OnScrollAnimationEnded();
1292         }
1293
1294         /// <summary>
1295         /// Adjust scrolling position by own scrolling rules.
1296         /// Override this function when developer wants to change destination of flicking.(e.g. always snap to center of item)
1297         /// </summary>
1298         /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
1299         [EditorBrowsable(EditorBrowsableState.Never)]
1300         protected virtual float AdjustTargetPositionOfScrollAnimation(float position)
1301         {
1302             return position;
1303         }
1304
1305         /// <summary>
1306         /// Scroll position given to ScrollTo.
1307         /// This is the position in the opposite direction to the position of ContentContainer.
1308         /// </summary>
1309         /// <since_tizen> 8 </since_tizen>
1310         public Position ScrollPosition
1311         {
1312             get
1313             {
1314                 return new Position(-ContentContainer.Position);
1315             }
1316         }
1317
1318         /// <summary>
1319         /// Current scroll position in the middle of ScrollTo animation.
1320         /// This is the position in the opposite direction to the current position of ContentContainer.
1321         /// </summary>
1322         /// <since_tizen> 8 </since_tizen>
1323         public Position ScrollCurrentPosition
1324         {
1325             get
1326             {
1327                 return new Position(-ContentContainer.CurrentPosition);
1328             }
1329         }
1330
1331         /// <summary>
1332         /// Remove all children in ContentContainer.
1333         /// </summary>
1334         /// <param name="dispose">If true, removed child is disposed.</param>
1335         [EditorBrowsable(EditorBrowsableState.Never)]
1336         public void RemoveAllChildren(bool dispose = false)
1337         {
1338             RecursiveRemoveChildren(ContentContainer, dispose);
1339         }
1340
1341         private void RecursiveRemoveChildren(View parent, bool dispose)
1342         {
1343             if (parent == null)
1344             {
1345                 return;
1346             }
1347             int maxChild = (int)parent.GetChildCount();
1348             for (int i = maxChild - 1; i >= 0; --i)
1349             {
1350                 View child = parent.GetChildAt((uint)i);
1351                 if (child == null)
1352                 {
1353                     continue;
1354                 }
1355                 RecursiveRemoveChildren(child, dispose);
1356                 parent.Remove(child);
1357                 if (dispose)
1358                 {
1359                     child.Dispose();
1360                 }
1361             }
1362         }
1363
1364     }
1365
1366 } // namespace