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