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