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