ba6eba00104b71ce47ad003cc12dc38912ed86a9
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / FlexibleView / LinearLayoutManager.cs
1 /*
2  * Copyright(c) 2019 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 using System;
18 using System.ComponentModel;
19
20 namespace Tizen.NUI.Components
21 {
22     /// <summary>
23     /// Layout collection of views horizontally/vertically.
24     /// </summary>
25     /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
26     [EditorBrowsable(EditorBrowsableState.Never)]
27     public partial class LinearLayoutManager : FlexibleViewLayoutManager
28     {
29         /// <summary>
30         /// Constant value: 0.
31         /// </summary>
32         /// <since_tizen> 6 </since_tizen>
33         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
34         [EditorBrowsable(EditorBrowsableState.Never)]
35         public static readonly int HORIZONTAL = OrientationHelper.HORIZONTAL;
36         /// <summary>
37         /// Constant value: 1.
38         /// </summary>
39         /// <since_tizen> 6 </since_tizen>
40         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
41         [EditorBrowsable(EditorBrowsableState.Never)]
42         public static readonly int VERTICAL = OrientationHelper.VERTICAL;
43         /// <summary>
44         /// Constant value: -1.
45         /// </summary>
46         /// <since_tizen> 6 </since_tizen>
47         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
48         [EditorBrowsable(EditorBrowsableState.Never)]
49         public static readonly int NO_POSITION = FlexibleView.NO_POSITION;
50         /// <summary>
51         /// Constant value: -2^31.
52         /// </summary>
53         /// <since_tizen> 6 </since_tizen>
54         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
55         [EditorBrowsable(EditorBrowsableState.Never)]
56         public static readonly int INVALID_OFFSET = -2147483648;
57
58         private const float MAX_SCROLL_FACTOR = 1 / 3f;
59
60         /// <summary>
61         /// Current orientation.
62         /// </summary>
63         /// <since_tizen> 6 </since_tizen>
64         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
65         [EditorBrowsable(EditorBrowsableState.Never)]
66         protected int mOrientation;
67
68         internal OrientationHelper mOrientationHelper;
69
70         private LayoutState mLayoutState;
71         private AnchorInfo mAnchorInfo = new AnchorInfo();
72
73         // Stashed to avoid allocation, currently only used in #fill()
74         private LayoutChunkResult mLayoutChunkResult = new LayoutChunkResult();
75
76         private bool mShouldReverseLayout = false;
77
78         // When LayoutManager needs to scroll to a position, it sets this variable and requests a
79         // layout which will check this variable and re-layout accordingly.
80         private int mPendingScrollPosition = NO_POSITION;
81
82         // Used to keep the offset value when {@link #scrollToPositionWithOffset(int, int)} is
83         // called.
84         private int mPendingScrollPositionOffset = INVALID_OFFSET;
85
86         /// <summary>
87         /// Creates a LinearLayoutManager with orientation.
88         /// </summary>
89         /// <param name="orientation">Layout orientation.Should be HORIZONTAL or VERTICAL</param>
90         /// <since_tizen> 6 </since_tizen>
91         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
92         [EditorBrowsable(EditorBrowsableState.Never)]
93         public LinearLayoutManager(int orientation)
94         {
95             mOrientation = orientation;
96             mOrientationHelper = OrientationHelper.CreateOrientationHelper(this, mOrientation);
97
98             mLayoutState = new LayoutState();
99             mLayoutState.Offset = mOrientationHelper.GetStartAfterPadding();
100         }
101
102         /// <summary>
103         /// Retrieves the first visible item position.
104         /// </summary>
105         /// <since_tizen> 6 </since_tizen>
106         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
107         [EditorBrowsable(EditorBrowsableState.Never)]
108         public int FirstVisibleItemPosition
109         {
110             get
111             {
112                 FlexibleViewViewHolder child = FindFirstVisibleItemView();
113                 return child == null ? NO_POSITION : child.LayoutPosition;
114             }
115         }
116
117         /// <summary>
118         /// Retrieves the first complete visible item position.
119         /// </summary>
120         /// <since_tizen> 6 </since_tizen>
121         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
122         [EditorBrowsable(EditorBrowsableState.Never)]
123         public int FirstCompleteVisibleItemPosition
124         {
125             get
126             {
127                 FlexibleViewViewHolder child = FindFirstCompleteVisibleItemView();
128                 return child == null ? NO_POSITION : child.LayoutPosition;
129             }
130         }
131
132         /// <summary>
133         /// Retrieves the last visible item position.
134         /// </summary>
135         /// <since_tizen> 6 </since_tizen>
136         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
137         [EditorBrowsable(EditorBrowsableState.Never)]
138         public int LastVisibleItemPosition
139         {
140             get
141             {
142                 FlexibleViewViewHolder child = FindLastVisibleItemView();
143                 return child == null ? NO_POSITION : child.LayoutPosition;
144             }
145         }
146
147         /// <summary>
148         /// Retrieves the last complete visible item position.
149         /// </summary>
150         /// <since_tizen> 6 </since_tizen>
151         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
152         [EditorBrowsable(EditorBrowsableState.Never)]
153         public int LastCompleteVisibleItemPosition
154         {
155             get
156             {
157                 FlexibleViewViewHolder child = FindLastCompleteVisibleItemView();
158                 return child == null ? NO_POSITION : child.LayoutPosition;
159             }
160         }
161
162         /// <summary>
163         /// Query if horizontal scrolling is currently supported. The default implementation returns false.
164         /// </summary>
165         /// <since_tizen> 6 </since_tizen>
166         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
167         [EditorBrowsable(EditorBrowsableState.Never)]
168         public override bool CanScrollHorizontally()
169         {
170             return mOrientation == HORIZONTAL;
171         }
172
173         /// <summary>
174         /// Query if vertical scrolling is currently supported. The default implementation returns false.
175         /// </summary>
176         /// <since_tizen> 6 </since_tizen>
177         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
178         [EditorBrowsable(EditorBrowsableState.Never)]
179         public override bool CanScrollVertically()
180         {
181             return mOrientation == VERTICAL;
182         }
183
184         /// <summary>
185         /// Lay out all relevant child views from the given adapter.
186         /// </summary>
187         /// <param name="recycler">Recycler to use for fetching potentially cached views for a position</param>
188         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
189         [EditorBrowsable(EditorBrowsableState.Never)]
190         public override void OnLayoutChildren(FlexibleViewRecycler recycler)
191         {
192             mLayoutState.Recycle = false;
193             if (!mAnchorInfo.Valid || mPendingScrollPosition != NO_POSITION)
194             {
195                 mAnchorInfo.Reset();
196                 mAnchorInfo.LayoutFromEnd = mShouldReverseLayout;
197                 // calculate anchor position and coordinate
198                 UpdateAnchorInfoForLayout(recycler, mAnchorInfo);
199                 mAnchorInfo.Valid = true;
200             }
201
202             int firstLayoutDirection;
203             if (mAnchorInfo.LayoutFromEnd)
204             {
205                 firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL
206                         : LayoutState.ITEM_DIRECTION_HEAD;
207             }
208             else
209             {
210                 firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD
211                         : LayoutState.ITEM_DIRECTION_TAIL;
212             }
213             EnsureAnchorReady(recycler, mAnchorInfo, firstLayoutDirection);
214             ScrapAttachedViews(recycler);
215
216             if (mAnchorInfo.LayoutFromEnd == true)
217             {
218                 UpdateLayoutStateToFillStart(mAnchorInfo.Position, mAnchorInfo.Coordinate);
219                 Fill(recycler, mLayoutState, false, true);
220                 Cache(recycler, mLayoutState, true);
221
222                 UpdateLayoutStateToFillEnd(mAnchorInfo.Position, mAnchorInfo.Coordinate);
223                 mLayoutState.CurrentPosition += mLayoutState.ItemDirection;
224                 Fill(recycler, mLayoutState, false, true);
225                 Cache(recycler, mLayoutState, true);
226             }
227             else
228             {
229                 UpdateLayoutStateToFillEnd(mAnchorInfo.Position, mAnchorInfo.Coordinate);
230                 Fill(recycler, mLayoutState, false, true);
231                 Cache(recycler, mLayoutState, true);
232
233                 UpdateLayoutStateToFillStart(mAnchorInfo.Position, mAnchorInfo.Coordinate);
234                 mLayoutState.CurrentPosition += mLayoutState.ItemDirection;
235                 Fill(recycler, mLayoutState, false, true);
236                 Cache(recycler, mLayoutState, true);
237             }
238
239             OnLayoutCompleted();
240         }
241
242         /// <summary>
243         /// Scroll horizontally by dy pixels in screen coordinates.
244         /// </summary>
245         /// <param name="dx">distance to scroll in pixels. Y increases as scroll position approaches the top.</param>
246         /// <param name="recycler">Recycler to use for fetching potentially cached views for a position</param>
247         /// <param name="immediate">Specify if the scroll need animation</param>
248         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
249         [EditorBrowsable(EditorBrowsableState.Never)]
250         public override float ScrollHorizontallyBy(float dx, FlexibleViewRecycler recycler, bool immediate)
251         {
252             if (mOrientation == VERTICAL)
253             {
254                 return 0;
255             }
256             return ScrollBy(dx, recycler, immediate);
257         }
258
259         /// <summary>
260         /// Scroll vertically by dy pixels in screen coordinates.
261         /// </summary>
262         /// <param name="dy">distance to scroll in pixels. Y increases as scroll position approaches the top.</param>
263         /// <param name="recycler">Recycler to use for fetching potentially cached views for a position</param>
264         /// <param name="immediate">Specify if the scroll need animation</param>
265         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
266         [EditorBrowsable(EditorBrowsableState.Never)]
267         public override float ScrollVerticallyBy(float dy, FlexibleViewRecycler recycler, bool immediate)
268         {
269             if (mOrientation == HORIZONTAL)
270             {
271                 return 0;
272             }
273             return ScrollBy(dy, recycler, immediate);
274         }
275
276         /// <summary>
277         /// Compute the offset of the scrollbar's thumb within the range.
278         /// </summary>
279         /// <since_tizen> 6 </since_tizen>
280         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
281         [EditorBrowsable(EditorBrowsableState.Never)]
282         public override float ComputeScrollOffset()
283         {
284             FlexibleViewViewHolder startChild = FindFirstVisibleItemView();
285             FlexibleViewViewHolder endChild = FindLastVisibleItemView();
286             if (ChildCount == 0 || startChild == null || endChild == null)
287             {
288                 return 0;
289             }
290             int minPosition = Math.Min(startChild.LayoutPosition, endChild.LayoutPosition);
291             int maxPosition = Math.Max(startChild.LayoutPosition, endChild.LayoutPosition);
292             int itemsBefore = mShouldReverseLayout
293                     ? Math.Max(0, ItemCount - maxPosition - 1)
294                     : Math.Max(0, minPosition);
295
296             float laidOutArea = Math.Abs(mOrientationHelper.GetViewHolderEnd(endChild)
297                    - mOrientationHelper.GetViewHolderStart(startChild));
298             int itemRange = Math.Abs(startChild.LayoutPosition - endChild.LayoutPosition) + 1;
299             float avgSizePerRow = laidOutArea / itemRange;
300
301             return (float)Math.Round(itemsBefore * avgSizePerRow + (mOrientationHelper.GetStartAfterPadding()
302                     - mOrientationHelper.GetViewHolderStart(startChild)));
303         }
304
305         /// <summary>
306         /// Compute the extent of the scrollbar's thumb within the range.
307         /// </summary>
308         /// <since_tizen> 6 </since_tizen>
309         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
310         [EditorBrowsable(EditorBrowsableState.Never)]
311         public override float ComputeScrollExtent()
312         {
313             FlexibleViewViewHolder startChild = FindFirstVisibleItemView();
314             FlexibleViewViewHolder endChild = FindLastVisibleItemView();
315             if (ChildCount == 0 || startChild == null || endChild == null)
316             {
317                 return 0;
318             }
319             float extend = mOrientationHelper.GetViewHolderEnd(endChild)
320                 - mOrientationHelper.GetViewHolderStart(startChild);
321             return Math.Min(mOrientationHelper.GetTotalSpace(), extend);
322         }
323
324         /// <summary>
325         /// Compute the range that the scrollbar represents.
326         /// </summary>
327         /// <since_tizen> 6 </since_tizen>
328         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
329         [EditorBrowsable(EditorBrowsableState.Never)]
330         public override float ComputeScrollRange()
331         {
332             FlexibleViewViewHolder startChild = FindFirstVisibleItemView();
333             FlexibleViewViewHolder endChild = FindLastVisibleItemView();
334             if (ChildCount == 0 || startChild == null || endChild == null)
335             {
336                 return 0;
337             }
338             float laidOutArea = mOrientationHelper.GetViewHolderEnd(endChild)
339                     - mOrientationHelper.GetViewHolderStart(startChild);
340             int laidOutRange = Math.Abs(startChild.LayoutPosition - endChild.LayoutPosition) + 1;
341             // estimate a size for full list.
342             return laidOutArea / laidOutRange * ItemCount;
343         }
344
345         /// <summary>
346         /// Scroll the FlexibleView to make the position visible.
347         /// </summary>
348         /// <param name="position">Scroll to this adapter position</param>
349         /// <since_tizen> 6 </since_tizen>
350         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
351         [EditorBrowsable(EditorBrowsableState.Never)]
352         public override void ScrollToPosition(int position)
353         {
354             mPendingScrollPosition = position;
355             mPendingScrollPositionOffset = INVALID_OFFSET;
356
357             RelayoutRequest();
358         }
359
360         /// <summary>
361         /// Scroll to the specified adapter position with the given offset from resolved layout start.
362         /// </summary>
363         /// <param name="position">Scroll to this adapter position</param>
364         /// <param name="offset">The distance (in pixels) between the start edge of the item view and start edge of the FlexibleView.</param>
365         /// <since_tizen> 6 </since_tizen>
366         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
367         [EditorBrowsable(EditorBrowsableState.Never)]
368         public override void ScrollToPositionWithOffset(int position, int offset)
369         {
370             mPendingScrollPosition = position;
371             mPendingScrollPositionOffset = offset;
372
373             RelayoutRequest();
374         }
375
376         /// <summary>
377         /// Called after a full layout calculation is finished.
378         /// </summary>
379         /// <since_tizen> 6 </since_tizen>
380         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
381         [EditorBrowsable(EditorBrowsableState.Never)]
382         public override void OnLayoutCompleted()
383         {
384             if (mPendingScrollPosition != NO_POSITION)
385             {
386                 ChangeFocus(mPendingScrollPosition);
387             }
388             mPendingScrollPosition = NO_POSITION;
389             mPendingScrollPositionOffset = INVALID_OFFSET;
390
391             mAnchorInfo.Reset();
392         }
393
394         internal virtual void EnsureAnchorReady(FlexibleViewRecycler recycler, AnchorInfo anchorInfo, int itemDirection)
395         {
396
397         }
398
399
400         /// <summary>
401         /// Retrieves a position that neighbor to current position by direction.
402         /// </summary>
403         /// <param name="position">The anchor adapter position</param>
404         /// <param name="direction">The direction.</param>
405         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
406         [EditorBrowsable(EditorBrowsableState.Never)]
407         protected override int GetNextPosition(int position, FlexibleViewLayoutManager.Direction direction)
408         {
409             if (mOrientation == HORIZONTAL)
410             {
411                 switch (direction)
412                 {
413                     case FlexibleViewLayoutManager.Direction.Left:
414                         if (position > 0)
415                         {
416                             return position - 1;
417                         }
418                         break;
419                     case FlexibleViewLayoutManager.Direction.Right:
420                         if (position < ItemCount - 1)
421                         {
422                             return position + 1;
423                         }
424                         break;
425                 }
426             }
427             else
428             {
429                 switch (direction)
430                 {
431                     case FlexibleViewLayoutManager.Direction.Up:
432                         if (position > 0)
433                         {
434                             return position - 1;
435                         }
436                         break;
437                     case FlexibleViewLayoutManager.Direction.Down:
438                         if (position < ItemCount - 1)
439                         {
440                             return position + 1;
441                         }
442                         break;
443                 }
444             }
445
446             return NO_POSITION;
447         }
448
449         /// <summary>
450         /// Retrieves the first visible item view.
451         /// </summary>
452         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
453         [EditorBrowsable(EditorBrowsableState.Never)]
454         protected override FlexibleViewViewHolder FindFirstVisibleItemView()
455         {
456             int childCount = ChildCount;
457             if (mShouldReverseLayout == false)
458             {
459                 for (int i = 0; i < childCount; i++)
460                 {
461                     FlexibleViewViewHolder child = GetChildAt(i);
462                     int end = (int)mOrientationHelper.GetViewHolderEnd(child);
463                     if (end >= 0 && end < (int)mOrientationHelper.GetEnd())
464                     {
465                         return child;
466                     }
467                 }
468             }
469             else
470             {
471                 for (int i = childCount - 1; i >= 0; i--)
472                 {
473                     FlexibleViewViewHolder child = GetChildAt(i);
474                     int end = (int)mOrientationHelper.GetViewHolderEnd(child);
475                     if (end >= 0 && end < (int)mOrientationHelper.GetEnd())
476                     {
477                         return child;
478                     }
479                 }
480             }
481             return null;
482         }
483
484         /// <summary>
485         /// Retrieves the last visible item view.
486         /// </summary>
487         /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
488         [EditorBrowsable(EditorBrowsableState.Never)]
489         protected override FlexibleViewViewHolder FindLastVisibleItemView()
490         {
491             int childCount = ChildCount;
492             if (mShouldReverseLayout == false)
493             {
494                 for (int i = childCount - 1; i >= 0; i--)
495                 {
496                     FlexibleViewViewHolder child = GetChildAt(i);
497                     int start = (int)mOrientationHelper.GetViewHolderStart(child);
498                     if (start > 0 && start < (int)mOrientationHelper.GetEnd())
499                     {
500                         return child;
501                     }
502                 }
503             }
504             else
505             {
506                 for (int i = 0; i < childCount; i++)
507                 {
508                     FlexibleViewViewHolder child = GetChildAt(i);
509                     int start = (int)mOrientationHelper.GetViewHolderStart(child);
510                     if (start > 0 && start < (int)mOrientationHelper.GetEnd())
511                     {
512                         return child;
513                     }
514                 }
515             }
516             return null;
517         }
518     }
519 }