3bf32823a92e8018043a858118383b55ce9abee0
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / RecyclerView / Layouter / LinearLayouter.cs
1 /* Copyright (c) 2021 Samsung Electronics Co., Ltd.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  *
15  */
16 using System;
17 using Tizen.NUI.BaseComponents;
18 using System.Collections.Generic;
19 using System.ComponentModel;
20
21 namespace Tizen.NUI.Components
22 {
23     /// <summary>
24     /// layouter for CollectionView to display items in linear layout.
25     /// </summary>
26     /// <since_tizen> 9 </since_tizen>
27     public class LinearLayouter : ItemsLayouter
28     {
29         private readonly List<float> ItemPosition = new List<float>();
30         private readonly List<float> ItemSize = new List<float>();
31         private int ItemSizeChanged = -1;
32         private CollectionView colView;
33         private bool hasHeader;
34         private float headerSize;
35         private Extents headerMargin;
36         private bool hasFooter;
37         private float footerSize;
38         private Extents footerMargin;
39         private bool isGrouped;
40         private readonly List<GroupInfo> groups = new List<GroupInfo>();
41         private float groupHeaderSize;
42         private Extents groupHeaderMargin;
43         private float groupFooterSize;
44         private Extents groupFooterMargin;
45         private GroupInfo Visited;
46         private Timer requestLayoutTimer = null;
47         private bool isSourceEmpty;
48
49         /// <summary>
50         /// Clean up ItemsLayouter.
51         /// </summary>
52         /// <param name="view"> CollectionView of layouter.</param>
53         /// <remarks>please note that, view must be type of CollectionView</remarks>
54         /// <since_tizen> 9 </since_tizen>
55         public override void Initialize(RecyclerView view)
56         {
57             colView = view as CollectionView;
58             if (colView == null)
59             {
60                 throw new ArgumentException("LinearLayouter only can be applied CollectionView.", nameof(view));
61             }
62             // 1. Clean Up
63             Clear();
64
65             FirstVisible = 0;
66             LastVisible = 0;
67
68             IsHorizontal = (colView.ScrollingDirection == ScrollableBase.Direction.Horizontal);
69
70             RecyclerViewItem header = colView?.Header;
71             RecyclerViewItem footer = colView?.Footer;
72             float width, height;
73             int count = colView.InternalItemSource.Count;
74
75             if (header != null)
76             {
77                 MeasureChild(colView, header);
78
79                 width = header.Layout != null? header.Layout.MeasuredWidth.Size.AsRoundedValue() : 0;
80                 height = header.Layout != null? header.Layout.MeasuredHeight.Size.AsRoundedValue() : 0;
81
82                 Extents itemMargin = header.Margin;
83                 headerSize = IsHorizontal?
84                                 width + itemMargin.Start + itemMargin.End:
85                                 height + itemMargin.Top + itemMargin.Bottom;
86                 headerMargin = new Extents(itemMargin);
87                 hasHeader = true;
88
89                 colView.UnrealizeItem(header);
90             }
91             else hasHeader = false;
92
93             if (footer != null)
94             {
95                 MeasureChild(colView, footer);
96
97                 width = footer.Layout != null? footer.Layout.MeasuredWidth.Size.AsRoundedValue() : 0;
98                 height = footer.Layout != null? footer.Layout.MeasuredHeight.Size.AsRoundedValue() : 0;
99
100                 Extents itemMargin = footer.Margin;
101                 footerSize = IsHorizontal?
102                                 width + itemMargin.Start + itemMargin.End:
103                                 height + itemMargin.Top + itemMargin.Bottom;
104                 footerMargin = new Extents(itemMargin);
105                 footer.Index = count - 1;
106                 hasFooter = true;
107
108                 colView.UnrealizeItem(footer);
109             }
110             else hasFooter = false;
111
112             //No Internal Source exist.
113             if (count == (hasHeader? (hasFooter? 2 : 1) : 0))
114             {
115                 isSourceEmpty = true;
116                 base.Initialize(view);
117                 return;
118             }
119             isSourceEmpty = false;
120
121             int firstIndex = hasHeader? 1 : 0;
122
123             if (colView.IsGrouped)
124             {
125                 isGrouped = true;
126
127                 if (colView.GroupHeaderTemplate != null)
128                 {
129                     while (!colView.InternalItemSource.IsGroupHeader(firstIndex)) firstIndex++;
130                     //must be always true
131                     if (colView.InternalItemSource.IsGroupHeader(firstIndex))
132                     {
133                         RecyclerViewItem groupHeader = colView.RealizeItem(firstIndex);
134                         firstIndex++;
135
136                         if (groupHeader == null) throw new Exception("[" + firstIndex + "] Group Header failed to realize!");
137
138                         // Need to Set proper height or width on scroll direction.
139                         if (groupHeader.Layout == null)
140                         {
141                             width = groupHeader.WidthSpecification;
142                             height = groupHeader.HeightSpecification;
143                         }
144                         else
145                         {
146                             MeasureChild(colView, groupHeader);
147
148                             width = groupHeader.Layout.MeasuredWidth.Size.AsRoundedValue();
149                             height = groupHeader.Layout.MeasuredHeight.Size.AsRoundedValue();
150                         }
151                         // pick the StepCandidate.
152                         Extents itemMargin = groupHeader.Margin;
153                         groupHeaderSize = IsHorizontal?
154                                             width + itemMargin.Start + itemMargin.End:
155                                             height + itemMargin.Top + itemMargin.Bottom;
156                         groupHeaderMargin = new Extents(itemMargin);
157                         colView.UnrealizeItem(groupHeader);
158                     }
159                 }
160                 else
161                 {
162                     groupHeaderSize = 0F;
163                 }
164
165                 if (colView.GroupFooterTemplate != null)
166                 {
167                     int firstFooter = firstIndex;
168                     while (!colView.InternalItemSource.IsGroupFooter(firstFooter)) firstFooter++;
169                     //must be always true
170                     if (colView.InternalItemSource.IsGroupFooter(firstFooter))
171                     {
172                         RecyclerViewItem groupFooter = colView.RealizeItem(firstFooter);
173
174                         if (groupFooter == null) throw new Exception("[" + firstFooter + "] Group Footer failed to realize!");
175                         // Need to Set proper height or width on scroll direction.
176                         if (groupFooter.Layout == null)
177                         {
178                             width = groupFooter.WidthSpecification;
179                             height = groupFooter.HeightSpecification;
180                         }
181                         else
182                         {
183                             MeasureChild(colView, groupFooter);
184
185                             width = groupFooter.Layout.MeasuredWidth.Size.AsRoundedValue();
186                             height = groupFooter.Layout.MeasuredHeight.Size.AsRoundedValue();
187                         }
188                         // pick the StepCandidate.
189                         Extents itemMargin = groupFooter.Margin;
190                         groupFooterSize = IsHorizontal?
191                                             width + itemMargin.Start + itemMargin.End:
192                                             height + itemMargin.Top + itemMargin.Bottom;
193                         groupFooterMargin = new Extents(itemMargin);  
194                         colView.UnrealizeItem(groupFooter);
195                     }
196                 }
197                 else
198                 {
199                     groupFooterSize = 0F;
200                 }
201             }
202             else isGrouped = false;
203
204             bool failed = false;
205             //Final Check of FirstIndex
206             while (colView.InternalItemSource.IsHeader(firstIndex) ||
207                     colView.InternalItemSource.IsGroupHeader(firstIndex) ||
208                     colView.InternalItemSource.IsGroupFooter(firstIndex))
209             {
210                 if (colView.InternalItemSource.IsFooter(firstIndex))
211                 {
212                     StepCandidate = 0F;
213                     failed = true;
214                     break;
215                 }
216                 firstIndex++;
217             }
218
219             if (!failed)
220             {
221                 RecyclerViewItem sizeDeligate = colView.RealizeItem(firstIndex);
222                 if (sizeDeligate == null)
223                 {
224                     // error !
225                     throw new Exception("Cannot create content from DatTemplate.");
226                 }
227
228                 sizeDeligate.BindingContext = colView.InternalItemSource.GetItem(firstIndex);
229
230                 // Need to Set proper height or width on scroll direction.
231                 if (sizeDeligate.Layout == null)
232                 {
233                     width = sizeDeligate.WidthSpecification;
234                     height = sizeDeligate.HeightSpecification;
235                 }
236                 else
237                 {
238                     MeasureChild(colView, sizeDeligate);
239
240                     width = sizeDeligate.Layout.MeasuredWidth.Size.AsRoundedValue();
241                     height = sizeDeligate.Layout.MeasuredHeight.Size.AsRoundedValue();
242                 }
243                 // pick the StepCandidate.
244                 Extents itemMargin = sizeDeligate.Margin;
245                 StepCandidate = IsHorizontal?
246                                 width + itemMargin.Start + itemMargin.End:
247                                 height + itemMargin.Top + itemMargin.Bottom;
248                 CandidateMargin = new Extents(itemMargin);  
249                 if (StepCandidate == 0) StepCandidate = 1; //????
250
251                 colView.UnrealizeItem(sizeDeligate);
252             }
253
254             float Current = IsHorizontal? Padding.Start : Padding.Top;
255             IGroupableItemSource source = colView.InternalItemSource;
256             GroupInfo currentGroup = null;
257             object currentParent = null;
258             for (int i = 0; i < count; i++)
259             {
260                 if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
261                 {
262                     if (i == 0 && hasHeader)
263                         ItemSize.Add(headerSize);
264                     else if (i == count - 1 && hasFooter)
265                         ItemSize.Add(footerSize);
266                     else if (source.IsGroupHeader(i))
267                         ItemSize.Add(groupHeaderSize);
268                     else if (source.IsGroupFooter(i))
269                         ItemSize.Add(groupFooterSize);
270                     else ItemSize.Add(StepCandidate);
271                 }
272                 if (isGrouped)
273                 {
274                     if (source.IsHeader(i))
275                     {
276                         //ItemPosition.Add(Current);
277                         Current += headerSize;
278                     }
279                     else if (source.IsFooter(i))
280                     {
281                         //ItemPosition.Add(Current);
282                         Current += footerSize;
283                     }
284                     else
285                     {
286                         //GroupHeader must always exist in group usage.
287                         //if (source.IsGroupHeader(i))
288                         if (source.GetGroupParent(i) != currentParent)
289                         {
290                             currentParent = source.GetGroupParent(i);
291                             float currentSize = (source.IsGroupHeader(i)? groupHeaderSize :
292                                                     (source.IsGroupFooter(i)? groupFooterSize: StepCandidate));
293                             currentGroup = new GroupInfo()
294                             {
295                                 GroupParent = currentParent,
296                                 //hasHeader = true,
297                                 //hasFooter = false,
298                                 StartIndex = i,
299                                 Count = 1,
300                                 GroupSize = currentSize,
301                                 GroupPosition = Current
302                             };
303                             if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
304                                 currentGroup.ItemPosition.Add(0);
305                             groups.Add(currentGroup);
306                             if (source.IsGroupHeader(i)) Current += currentSize;
307                         }
308                         //optional
309                         else if (source.IsGroupFooter(i))
310                         {
311                             //currentGroup.hasFooter = true;
312                             if (currentGroup != null)
313                             {
314                                 currentGroup.Count++;
315                                 currentGroup.GroupSize += groupFooterSize;
316                                 if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
317                                     currentGroup.ItemPosition.Add(Current - currentGroup.GroupPosition);
318                                 Current += groupFooterSize;
319                             }
320                         }
321                         else
322                         {
323                             if (currentGroup != null)
324                             {
325                                 currentGroup.Count++;
326                                 currentGroup.GroupSize += StepCandidate;
327                                 if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
328                                     currentGroup.ItemPosition.Add(Current - currentGroup.GroupPosition);
329                                 Current += StepCandidate;
330                             }
331                         }
332                     }
333                 }
334                 else
335                 {
336                     if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
337                         ItemPosition.Add(Current);
338
339                     if (i == 0 && hasHeader) Current += headerSize;
340                     else if (i == count - 1 && hasFooter) Current += footerSize;
341                     else Current += StepCandidate;
342                 }
343             }
344
345             ScrollContentSize = Current + (IsHorizontal? Padding.End : Padding.Bottom);
346             if (IsHorizontal) colView.ContentContainer.SizeWidth = ScrollContentSize;
347             else colView.ContentContainer.SizeHeight = ScrollContentSize;
348
349             base.Initialize(view);
350             //Console.WriteLine("[NUI] Init Done, StepCnadidate{0}, Scroll{1}", StepCandidate, ScrollContentSize);
351         }
352
353         /// <summary>
354         /// This is called to find out where items are lain out according to current scroll position.
355         /// </summary>
356         /// <param name="scrollPosition">Scroll position which is calculated by ScrollableBase</param>
357         /// <param name="force">boolean force flag to layouting forcely.</param>
358         /// <since_tizen> 9 </since_tizen>
359         public override void RequestLayout(float scrollPosition, bool force = false)
360         {
361             // Layouting is only possible after once it initialized.
362             if (!IsInitialized) return;
363
364             if (requestLayoutTimer != null)
365             {
366                 requestLayoutTimer.Dispose();
367                 requestLayoutTimer = null;
368                 force = true;
369             }
370
371             int LastIndex = colView.InternalItemSource.Count - 1;
372
373             if (!force && PrevScrollPosition == Math.Abs(scrollPosition)) return;
374             PrevScrollPosition = Math.Abs(scrollPosition);
375
376             if (ItemSizeChanged >= 0)
377             {
378                 for (int i = ItemSizeChanged; i <= LastIndex; i++)
379                     UpdatePosition(i);
380                 (float updateX, float updateY) = GetItemPosition(LastIndex);
381                 ScrollContentSize = GetItemStepSize(LastIndex) + (IsHorizontal? updateX + Padding.End : updateY + Padding.Bottom);
382             }
383
384             int prevFirstVisible = FirstVisible;
385             int prevLastVisible = LastVisible;
386
387             (float X, float Y) visibleArea = (PrevScrollPosition,
388                 PrevScrollPosition + (IsHorizontal? colView.Size.Width : colView.Size.Height)
389             );
390
391             // 1. Set First/Last Visible Item Index. 
392             (int start, int end) = FindVisibleItems(visibleArea);
393             FirstVisible = start;
394             LastVisible = end;
395
396             // 2. Unrealize invisible items.
397             List<RecyclerViewItem> unrealizedItems = new List<RecyclerViewItem>();
398             foreach (RecyclerViewItem item in VisibleItems)
399             {
400                 if (item.Index < FirstVisible || item.Index > LastVisible)
401                 {
402                     //Console.WriteLine("[NUI] Unrealize{0}!", item.Index);
403                     unrealizedItems.Add(item);
404                     colView.UnrealizeItem(item);
405                 }
406             }
407             VisibleItems.RemoveAll(unrealizedItems.Contains);
408             unrealizedItems.Clear();
409
410             // 3. Realize and placing visible items.
411             for (int i = FirstVisible; i <= LastVisible; i++)
412             {
413                 RecyclerViewItem item = null;
414                 // 4. Get item if visible or realize new.
415                 if (i >= prevFirstVisible && i <= prevLastVisible)
416                 {
417                     item = GetVisibleItem(i);
418                     if (item != null && !force) continue;
419                 }
420
421                 if (item == null)
422                 {
423                     item = colView.RealizeItem(i);
424                     if (item != null) VisibleItems.Add(item);
425                     else throw new Exception("Failed to create RecycerViewItem index of ["+ i + "]");
426                 }
427                 // 5. Placing item.
428                 (float posX, float posY) = GetItemPosition(i);
429                 item.Position = new Position(posX, posY);
430
431                 var size = (IsHorizontal? item.SizeWidth: item.SizeHeight);
432
433                 if (colView.SizingStrategy == ItemSizingStrategy.MeasureFirst)
434                 {
435                     if (item.IsHeader || item.IsFooter || item.isGroupHeader || item.isGroupFooter)
436                     {
437                         if (item.IsHeader) size = headerSize;
438                         else if (item.IsFooter) size = footerSize;
439                         else if (item.isGroupHeader) size = groupHeaderSize;
440                         else if (item.isGroupFooter) size = groupFooterSize;
441                     }
442                     else size = StepCandidate;
443                 }
444                 if (IsHorizontal && item.HeightSpecification == LayoutParamPolicies.MatchParent)
445                 {
446                     item.Size = new Size(size, Container.Size.Height - Padding.Top - Padding.Bottom - item.Margin.Top - item.Margin.Bottom);
447                 }
448                 else if (!IsHorizontal && item.WidthSpecification == LayoutParamPolicies.MatchParent)
449                 {
450                     item.Size = new Size(Container.Size.Width - Padding.Start - Padding.End - item.Margin.Start - item.Margin.End, size);
451                 }
452             }
453             return;
454         }
455
456         /// <summary>
457         /// Clear the current screen and all properties.
458         /// </summary>
459         [EditorBrowsable(EditorBrowsableState.Never)]
460         public override void Clear()
461         {
462             // Clean Up
463             if (requestLayoutTimer != null)
464             {
465                 requestLayoutTimer.Dispose();
466             }
467             if (groups != null)
468             {
469                  /*
470                 foreach (GroupInfo group in groups)
471                 {
472                     //group.ItemPosition?.Clear();
473                     // if Disposable?
474                     //group.Dispose();
475                 }
476                 */
477                 groups.Clear();
478             }
479             if (ItemPosition != null)
480             {
481                 ItemPosition.Clear();
482             }
483             if (ItemSize != null)
484             {
485                 ItemSize.Clear();
486             }
487             if (headerMargin != null)
488             {
489                 headerMargin.Dispose();
490                 headerMargin = null;
491             }
492             if (footerMargin != null)
493             {
494                 footerMargin.Dispose();
495                 footerMargin = null;
496             }
497             if (groupHeaderMargin != null)
498             {
499                 groupHeaderMargin.Dispose();
500                 groupHeaderMargin = null;
501             }
502             if (groupFooterMargin != null)
503             {
504                 groupFooterMargin.Dispose();
505                 groupFooterMargin = null;
506             }
507
508             base.Clear();
509         }
510
511
512         /// <inheritdoc/>
513         [EditorBrowsable(EditorBrowsableState.Never)]
514         public override void NotifyItemSizeChanged(RecyclerViewItem item)
515         {
516             if (item == null)
517                 throw new ArgumentNullException(nameof(item));
518
519             if (!IsInitialized ||
520                 (colView.SizingStrategy == ItemSizingStrategy.MeasureFirst &&
521                 item.Index != 0) ||
522                 (item.Index < 0))
523                 return;
524
525             float PrevSize, CurrentSize;
526             if (item.Index == (colView.InternalItemSource.Count - 1))
527             {
528                 PrevSize = ScrollContentSize - ItemPosition[item.Index];
529             }
530             else
531             {
532                 PrevSize = ItemPosition[item.Index + 1] - ItemPosition[item.Index];
533             }
534
535             CurrentSize = (IsHorizontal? item.Size.Width : item.Size.Height);
536
537             if (CurrentSize != PrevSize)
538             {
539                 if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
540                     ItemSize[item.Index] = CurrentSize;
541                 else
542                     StepCandidate = CurrentSize;
543             }
544             if (ItemSizeChanged == -1) ItemSizeChanged = item.Index;
545             else ItemSizeChanged = Math.Min(ItemSizeChanged, item.Index);
546
547             //ScrollContentSize += Diff; UpdateOnce?
548         }
549
550         /// <Inheritdoc/>
551         [EditorBrowsable(EditorBrowsableState.Never)]
552         public override void NotifyItemInserted(IItemSource source, int startIndex)
553         {
554             // Insert Single item.
555             if (source == null) throw new ArgumentNullException(nameof(source));
556
557             if (isSourceEmpty)
558             {
559                 Initialize(colView);
560             }
561
562             // Will be null if not a group.
563             float currentSize = StepCandidate;
564             IGroupableItemSource gSource = source as IGroupableItemSource;
565
566             // Get the first Visible Position to adjust.
567             /*
568             int topInScreenIndex = 0;
569             float offset = 0F;
570             (topInScreenIndex, offset) = FindTopItemInScreen();
571             */
572         
573             // 1. Handle MeasureAll
574             /*
575             if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
576             {
577                 //Need To Implement
578             }
579             */
580
581             //2. Handle Group Case.
582             if (isGrouped && gSource != null)
583             {
584                 GroupInfo groupInfo = null;
585                 object groupParent = gSource.GetGroupParent(startIndex);
586                 int parentIndex = gSource.GetPosition(groupParent);
587                 if (gSource.HasHeader) parentIndex--;
588
589                 // Check item is group parent or not
590                 // if group parent, add new gorupinfo
591                 if (gSource.IsHeader(startIndex))
592                 {
593                     // This is childless group.
594                     // create new groupInfo!
595                     groupInfo = new GroupInfo()
596                     {
597                         GroupParent = groupParent,
598                         StartIndex = startIndex,
599                         Count = 1,
600                         GroupSize = groupHeaderSize,
601                     };
602
603                     if (parentIndex >= groups.Count)
604                     {
605                         groupInfo.GroupPosition = ScrollContentSize;
606                         groups.Add(groupInfo);
607                     }
608                     else
609                     {
610                         groupInfo.GroupPosition = groups[parentIndex].GroupPosition;
611                         groups.Insert(parentIndex, groupInfo);
612                     }
613
614                     currentSize = groupHeaderSize;
615                 }
616                 else
617                 {
618                     // If not group parent, add item into the groupinfo.
619                     if (parentIndex >= groups.Count) throw new Exception("group parent is bigger than group counts.");
620                     groupInfo = groups[parentIndex];//GetGroupInfo(groupParent);
621                     if (groupInfo == null) throw new Exception("Cannot find group information!");
622                     groupInfo.Count++;
623
624                     if (gSource.IsGroupFooter(startIndex))
625                     {
626                         // It doesn't make sence to adding footer by notify...
627                         // if GroupFooterTemplate is added,
628                         // need to implement on here.
629                     }
630                     else
631                     {
632                         if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
633                         {
634                             float curPos = groupInfo.ItemPosition[startIndex - groupInfo.StartIndex];
635                             groupInfo.ItemPosition.Insert(startIndex - groupInfo.StartIndex, curPos);
636                             for (int i = startIndex - groupInfo.StartIndex; i < groupInfo.Count; i++)
637                             {
638                                 groupInfo.ItemPosition[i] = curPos;
639                                 curPos += GetItemStepSize(parentIndex + i);
640                             }
641                             groupInfo.GroupSize = curPos;
642                         }
643                         else
644                         {
645                             groupInfo.GroupSize += currentSize;
646                         }
647                     }
648                 }
649
650                 if (parentIndex + 1 < groups.Count)
651                 {
652                     for(int i = parentIndex + 1; i < groups.Count; i++)
653                     {
654                         groups[i].GroupPosition += currentSize;
655                         groups[i].StartIndex++;
656                     }
657                 }
658             }
659             else
660             {
661                 if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
662                 {
663                     // Need to Implements
664                 }
665
666             }
667
668             // 3. Update Scroll Content Size
669             ScrollContentSize += currentSize;
670
671             if (IsHorizontal) colView.ContentContainer.SizeWidth = ScrollContentSize;
672             else colView.ContentContainer.SizeHeight = ScrollContentSize;
673
674             // 4. Update Visible Items.
675             foreach (RecyclerViewItem item in VisibleItems)
676             {
677                 if (item.Index >= startIndex)
678                 {
679                     item.Index++;
680                 }
681             }
682
683
684             float scrollPosition = PrevScrollPosition;
685
686             /*
687             // Position Adjust
688             // Insertion above Top Visible!
689             if (startIndex <= topInScreenIndex)
690             {
691                 scrollPosition = GetItemPosition(topInScreenIndex);
692                 scrollPosition -= offset;
693
694                 colView.ScrollTo(scrollPosition);
695             }
696             */
697
698             // Update Viewport in delay.
699             // FIMXE: original we only need to process RequestLayout once before layout calculation in main loop.
700             // but currently we do not have any accessor to pre-calculation so instead of this,
701             // using Timer temporarily.
702             DelayedRequestLayout(scrollPosition);
703         }
704
705         /// <Inheritdoc/>
706         [EditorBrowsable(EditorBrowsableState.Never)]
707         public override void NotifyItemRangeInserted(IItemSource source, int startIndex, int count)
708         {
709              // Insert Group
710             if (source == null) throw new ArgumentNullException(nameof(source));
711
712             if (isSourceEmpty)
713             {
714                 Initialize(colView);
715             }
716
717             float currentSize = StepCandidate;
718             // Will be null if not a group.
719             IGroupableItemSource gSource = source as IGroupableItemSource;
720
721             // Get the first Visible Position to adjust.
722             /*
723             int topInScreenIndex = 0;
724             float offset = 0F;
725             (topInScreenIndex, offset) = FindTopItemInScreen();
726             */
727
728             // 1. Handle MeasureAll
729             /*
730             if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
731             {
732                 //Need To Implement
733             }
734             */
735
736             // 2. Handle Group Case
737             // Adding ranged items should all same new groups.
738             if (isGrouped && gSource != null)
739             {
740                 GroupInfo groupInfo = null;
741                 object groupParent = gSource.GetGroupParent(startIndex);
742                 int parentIndex = gSource.GetPosition(groupParent);
743                 if (gSource.HasHeader) parentIndex--;
744
745                 // We guess here that range inserted from GroupStartIndex.
746                 int groupStartIndex = startIndex;
747
748                 for (int current = startIndex; current - startIndex < count; current++)
749                 {
750                     // Check item is group parent or not
751                     // if group parent, add new gorupinfo
752                     if (groupStartIndex == current)
753                     {
754                         currentSize = (gSource.IsGroupHeader(current)? groupHeaderSize :
755                                             (gSource.IsGroupFooter(current)? groupFooterSize: currentSize));
756                         //create new groupInfo!
757                         groupInfo = new GroupInfo()
758                         {
759                             GroupParent = groupParent,
760                             StartIndex = current,
761                             Count = 1,
762                             GroupSize = currentSize,
763                         };
764
765                     }
766                     else
767                     {
768                         //if not group parent, add item into the groupinfo.
769                         //groupInfo = GetGroupInfo(groupStartIndex);
770                         if (groupInfo == null) throw new Exception("Cannot find group information!");
771                         groupInfo.Count++;
772
773                         if (gSource.IsGroupFooter(current))
774                         {
775                                 groupInfo.GroupSize += groupFooterSize;
776                         }
777                         else
778                         {
779                             if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
780                             {
781                                //Need To Implement
782                                /*
783                                 float curPos = groupInfo.ItemPosition[current - groupStartIndex];
784                                 groupInfo.ItemPosition.Insert(current - groupStartIndex, curPos);
785                                 for (int i = current - groupStartIndex; i < groupInfo.Count; i++)
786                                 {
787                                     groupInfo.ItemPosition[i] = curPos;
788                                     curPos += GetItemSize(parentIndex + i);
789                                 }
790                                 groupInfo.GroupSize = curPos;
791                                 */
792                             }
793                             else
794                             {
795                                 groupInfo.GroupSize += StepCandidate;
796                             }
797                         }
798                     }
799                 }
800
801                 if (parentIndex >= groups.Count)
802                 {
803                     groupInfo.GroupPosition = ScrollContentSize;
804                     groups.Add(groupInfo);
805                 }
806                 else
807                 {
808                     groupInfo.GroupPosition = groups[parentIndex].GroupPosition;
809                     groups.Insert(parentIndex, groupInfo);
810                 }
811
812                 // Update other below group's position
813                 if (parentIndex + 1 < groups.Count)
814                 {
815                     for(int i = parentIndex + 1; i < groups.Count; i++)
816                     {
817                         groups[i].GroupPosition += groupInfo.GroupSize;
818                         groups[i].StartIndex += count;
819                     }
820                 }
821
822                 ScrollContentSize += groupInfo.GroupSize;
823             }
824             else
825             {
826                 Tizen.Log.Error("NUI", "Not support insert ungrouped range items currently!");
827             }
828
829             // 3. Update Scroll Content Size
830             if (IsHorizontal) colView.ContentContainer.SizeWidth = ScrollContentSize;
831             else colView.ContentContainer.SizeHeight = ScrollContentSize;
832
833             // 4. Update Visible Items.
834             foreach (RecyclerViewItem item in VisibleItems)
835             {
836                 if (item.Index >= startIndex)
837                 {
838                     item.Index += count;
839                 }
840             }
841
842             // Position Adjust
843             float scrollPosition = PrevScrollPosition;
844             /*
845             // Insertion above Top Visible!
846             if (startIndex + count <= topInScreenIndex)
847             {
848                 scrollPosition = GetItemPosition(topInScreenIndex);
849                 scrollPosition -= offset;
850
851                 colView.ScrollTo(scrollPosition);
852             }
853             */
854
855             // Update Viewport in delay.
856             // FIMXE: original we only need to process RequestLayout once before layout calculation in main loop.
857             // but currently we do not have any accessor to pre-calculation so instead of this,
858             // using Timer temporarily.
859             DelayedRequestLayout(scrollPosition);
860         }
861
862         /// <Inheritdoc/>
863         [EditorBrowsable(EditorBrowsableState.Never)]
864         public override void NotifyItemRemoved(IItemSource source, int startIndex)
865         {
866             // Remove Single
867             if (source == null) throw new ArgumentNullException(nameof(source));
868
869             // Will be null if not a group.
870             float currentSize = StepCandidate;
871             IGroupableItemSource gSource = source as IGroupableItemSource;
872
873             // Get the first Visible Position to adjust.
874             /*
875             int topInScreenIndex = 0;
876             float offset = 0F;
877             (topInScreenIndex, offset) = FindTopItemInScreen();
878             */
879
880             // 1. Handle MeasureAll
881             /*
882             if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
883             {
884                 //Need To Implement
885             }
886             */
887
888             // 2. Handle Group Case
889             if (isGrouped && gSource != null)
890             {
891                 int parentIndex = 0;
892                 GroupInfo groupInfo = null;
893                 foreach(GroupInfo cur in groups)
894                 {
895                     if ((cur.StartIndex <= startIndex) && (cur.StartIndex + cur.Count - 1 >= startIndex))
896                     {
897                         groupInfo = cur;
898                         break;
899                     }
900                     parentIndex++;
901                 }
902                 if (groupInfo == null) throw new Exception("Cannot find group information!");
903                 // Check item is group parent or not
904                 // if group parent, add new gorupinfo
905                 if (groupInfo.StartIndex == startIndex)
906                 {
907                     // This is empty group!
908                     // check group is empty.
909                     if (groupInfo.Count != 1)
910                     {
911                         throw new Exception("Cannot remove group parent");
912                     }
913                     currentSize = groupInfo.GroupSize;
914
915                     // Remove Group
916                     // groupInfo.Dispose();
917                     groups.Remove(groupInfo);
918                 }
919                 else
920                 {
921                     groupInfo.Count--;
922
923                     if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
924                     {
925                         //Need to Implement this.
926                     }
927                     else
928                     {
929                         groupInfo.GroupSize -= currentSize;
930                     }
931                 }
932
933                 for (int i = parentIndex + 1; i < groups.Count; i++)
934                 {
935                     groups[i].GroupPosition -= currentSize;
936                     groups[i].StartIndex--;
937                 }
938             }
939             else
940             {
941                 if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
942                 {
943                     // Need to Implements
944                 }
945                 // else Nothing to Do
946             }
947
948             ScrollContentSize -= currentSize;
949
950             // 3. Update Scroll Content Size
951             if (IsHorizontal) colView.ContentContainer.SizeWidth = ScrollContentSize;
952             else colView.ContentContainer.SizeHeight = ScrollContentSize;
953
954             // 4. Update Visible Items.
955             RecyclerViewItem targetItem = null;
956             foreach (RecyclerViewItem item in VisibleItems)
957             {
958                 if (item.Index == startIndex)
959                 {
960                     targetItem = item;
961                     colView.UnrealizeItem(item);
962                 }
963                 else if (item.Index > startIndex)
964                 {
965                     item.Index--;
966                 }
967             }
968             VisibleItems.Remove(targetItem);
969
970             // Position Adjust
971             float scrollPosition = PrevScrollPosition;
972             /*
973             // Insertion above Top Visible!
974             if (startIndex <= topInScreenIndex)
975             {
976                 scrollPosition = GetItemPosition(topInScreenIndex);
977                 scrollPosition -= offset;
978
979                 colView.ScrollTo(scrollPosition);
980             }
981             */
982
983             // Update Viewport in delay.
984             // FIMXE: original we only need to process RequestLayout once before layout calculation in main loop.
985             // but currently we do not have any accessor to pre-calculation so instead of this,
986             // using Timer temporarily.
987             DelayedRequestLayout(scrollPosition);
988         }
989
990         /// <Inheritdoc/>
991         [EditorBrowsable(EditorBrowsableState.Never)]
992         public override void NotifyItemRangeRemoved(IItemSource source, int startIndex, int count)
993         {
994             // Remove Group
995             if (source == null) throw new ArgumentNullException(nameof(source));
996
997             // Will be null if not a group.
998             float currentSize = StepCandidate;
999             IGroupableItemSource gSource = source as IGroupableItemSource;
1000
1001             // Get the first Visible Position to adjust.
1002             /*
1003             int topInScreenIndex = 0;
1004             float offset = 0F;
1005             (topInScreenIndex, offset) = FindTopItemInScreen();
1006             */
1007
1008             // 1. Handle MeasureAll
1009             /*
1010             if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
1011             {
1012                 //Need To Implement
1013             }
1014             */
1015
1016             // 2. Handle Group Case
1017             if (isGrouped && gSource != null)
1018             {
1019                 int parentIndex = 0;
1020                 GroupInfo groupInfo = null;
1021                 foreach(GroupInfo cur in groups)
1022                 {
1023                     if ((cur.StartIndex == startIndex) && (cur.Count == count))
1024                     {
1025                         groupInfo = cur;
1026                         break;
1027                     }
1028                     parentIndex++;
1029                 }
1030                 if (groupInfo == null) throw new Exception("Cannot find group information!");
1031                 // Check item is group parent or not
1032                 // if group parent, add new gorupinfo
1033                 currentSize = groupInfo.GroupSize;
1034                 if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
1035                 {
1036                     // Update ItemSize and ItemPosition
1037                 }
1038                 // Remove Group
1039                 // groupInfo.Dispose();
1040                 groups.Remove(groupInfo);
1041
1042                 for (int i = parentIndex; i < groups.Count; i++)
1043                 {
1044                     groups[i].GroupPosition -= currentSize;
1045                     groups[i].StartIndex -= count;
1046                 }
1047             }
1048             else
1049             {
1050                 //It must group case! throw exception!
1051                 Tizen.Log.Error("NUI", "Not support remove ungrouped range items currently!");
1052             }
1053
1054             ScrollContentSize -= currentSize;
1055
1056             // 3. Update Scroll Content Size
1057             if (IsHorizontal) colView.ContentContainer.SizeWidth = ScrollContentSize;
1058             else colView.ContentContainer.SizeHeight = ScrollContentSize;
1059
1060             // 4. Update Visible Items.
1061             List<RecyclerViewItem> unrealizedItems = new List<RecyclerViewItem>();
1062             foreach (RecyclerViewItem item in VisibleItems)
1063             {
1064                 if ((item.Index >= startIndex)
1065                     && (item.Index < startIndex + count))
1066                 {
1067                     unrealizedItems.Add(item);
1068                     colView.UnrealizeItem(item);
1069                 }
1070                 else if (item.Index >= startIndex + count)
1071                 {
1072                     item.Index -= count;
1073                 }
1074             }
1075             VisibleItems.RemoveAll(unrealizedItems.Contains);
1076             unrealizedItems.Clear();
1077
1078             // Position Adjust
1079             float scrollPosition = PrevScrollPosition;
1080             /*
1081             // Insertion above Top Visible!
1082             if (startIndex <= topInScreenIndex)
1083             {
1084                 scrollPosition = GetItemPosition(topInScreenIndex);
1085                 scrollPosition -= offset;
1086
1087                 colView.ScrollTo(scrollPosition);
1088             }
1089             */
1090
1091             // Update Viewport in delay.
1092             // FIMXE: original we only need to process RequestLayout once before layout calculation in main loop.
1093             // but currently we do not have any accessor to pre-calculation so instead of this,
1094             // using Timer temporarily.
1095             DelayedRequestLayout(scrollPosition);
1096         }
1097
1098         /// <Inheritdoc/>
1099         [EditorBrowsable(EditorBrowsableState.Never)]
1100         public override void NotifyItemMoved(IItemSource source, int fromPosition, int toPosition)
1101         {
1102             // Reorder Single
1103             if (source == null) throw new ArgumentNullException(nameof(source));
1104
1105             // Will be null if not a group.
1106             float currentSize = StepCandidate;
1107             int diff = toPosition - fromPosition;
1108
1109             // Get the first Visible Position to adjust.
1110             /*
1111             int topInScreenIndex = 0;
1112             float offset = 0F;
1113             (topInScreenIndex, offset) = FindTopItemInScreen();
1114             */
1115
1116             // 1. Handle MeasureAll
1117             /*
1118             if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
1119             {
1120                 //Need To Implement
1121             }
1122             */
1123             
1124             // Move can only happen in it's own groups.
1125             // so there will be no changes in position, startIndex in ohter groups.
1126             // check visible item and update indexs.
1127             int startIndex = ( diff > 0 ? fromPosition: toPosition);
1128             int endIndex = (diff > 0 ? toPosition: fromPosition);
1129
1130             if ((endIndex >= FirstVisible) && (startIndex <= LastVisible))
1131             {
1132                 foreach (RecyclerViewItem item in VisibleItems)
1133                 {
1134                     if ((item.Index >= startIndex)
1135                         && (item.Index <= endIndex))
1136                     {
1137                         if (item.Index == fromPosition) item.Index = toPosition;
1138                         else
1139                         {
1140                             if (diff > 0) item.Index--;
1141                             else item.Index++;
1142                         }
1143                     }
1144                 }
1145             }
1146
1147             if (IsHorizontal) colView.ContentContainer.SizeWidth = ScrollContentSize;
1148             else colView.ContentContainer.SizeHeight = ScrollContentSize;
1149
1150             // Position Adjust
1151             float scrollPosition = PrevScrollPosition;
1152             /*
1153             // Insertion above Top Visible!
1154             if (((fromPosition > topInScreenIndex) && (toPosition < topInScreenIndex) ||
1155                 ((fromPosition < topInScreenIndex) && (toPosition > topInScreenIndex)))
1156             {
1157                 scrollPosition = GetItemPosition(topInScreenIndex);
1158                 scrollPosition -= offset;
1159
1160                 colView.ScrollTo(scrollPosition);
1161             }
1162             */
1163
1164             // Update Viewport in delay.
1165             // FIMXE: original we only need to process RequestLayout once before layout calculation in main loop.
1166             // but currently we do not have any accessor to pre-calculation so instead of this,
1167             // using Timer temporarily.
1168             DelayedRequestLayout(scrollPosition);
1169         }
1170
1171         /// <Inheritdoc/>
1172         [EditorBrowsable(EditorBrowsableState.Never)]
1173         public override void NotifyItemRangeMoved(IItemSource source, int fromPosition, int toPosition, int count)
1174         {
1175             // Reorder Groups
1176             if (source == null) throw new ArgumentNullException(nameof(source));
1177
1178             // Will be null if not a group.
1179             float currentSize = StepCandidate;
1180             int diff = toPosition - fromPosition;
1181
1182             int startIndex = ( diff > 0 ? fromPosition: toPosition);
1183             int endIndex = (diff > 0 ? toPosition + count - 1: fromPosition + count - 1);
1184
1185             // 2. Handle Group Case
1186             if (isGrouped)
1187             {
1188                 int fromParentIndex = 0;
1189                 int toParentIndex = 0;
1190                 bool findFrom = false;
1191                 bool findTo = false;
1192                 GroupInfo fromGroup = null;
1193                 GroupInfo toGroup = null;
1194
1195                 foreach(GroupInfo cur in groups)
1196                 {
1197                     if ((cur.StartIndex == fromPosition) && (cur.Count == count))
1198                     {
1199                         fromGroup = cur;
1200                         findFrom = true;
1201                         if (findFrom && findTo) break;
1202                     }
1203                     else if (cur.StartIndex == toPosition)
1204                     {
1205                         toGroup = cur;
1206                         findTo = true;
1207                         if (findFrom && findTo) break;
1208                     }
1209                     if (!findFrom) fromParentIndex++;
1210                     if (!findTo) toParentIndex++;
1211                 }
1212                 if (toGroup == null || fromGroup == null) throw new Exception("Cannot find group information!");
1213
1214                 fromGroup.StartIndex = toGroup.StartIndex;
1215                 fromGroup.GroupPosition = toGroup.GroupPosition;
1216
1217                 endIndex = (diff > 0 ? toPosition + toGroup.Count - 1: fromPosition + count - 1);
1218
1219                 groups.Remove(fromGroup);
1220                 groups.Insert(toParentIndex, fromGroup);
1221
1222                 int startGroup = (diff > 0? fromParentIndex: toParentIndex);
1223                 int endGroup =  (diff > 0? toParentIndex: fromParentIndex);
1224                 
1225                 for (int i = startGroup; i <= endGroup; i++)
1226                 {
1227                     if (i == toParentIndex) continue;
1228                     float prevPos = groups[i].GroupPosition;
1229                     int prevIdx = groups[i].StartIndex;
1230                     groups[i].GroupPosition = groups[i].GroupPosition + (diff > 0? -1 : 1) * fromGroup.GroupSize;
1231                     groups[i].StartIndex = groups[i].StartIndex + (diff > 0? -1 : 1) * fromGroup.Count;
1232                 }
1233             }
1234             else
1235             {
1236                 //It must group case! throw exception!
1237                 Tizen.Log.Error("NUI", "Not support move ungrouped range items currently!");
1238             }
1239
1240             // Move can only happen in it's own groups.
1241             // so there will be no changes in position, startIndex in ohter groups.
1242             // check visible item and update indexs.
1243             if ((endIndex >= FirstVisible) && (startIndex <= LastVisible))
1244             {
1245                 foreach (RecyclerViewItem item in VisibleItems)
1246                 {
1247                     if ((item.Index >= startIndex)
1248                         && (item.Index <= endIndex))
1249                     {
1250                         if ((item.Index >= fromPosition) && (item.Index < fromPosition + count))
1251                         {
1252                             item.Index = fromPosition - item.Index + toPosition;
1253                         }
1254                         else
1255                         {
1256                             if (diff > 0) item.Index -= count;
1257                             else item.Index += count;
1258                         }
1259                     }
1260                 }
1261             }
1262
1263             // Position Adjust
1264             float scrollPosition = PrevScrollPosition;
1265             /*
1266             // Insertion above Top Visible!
1267             if (((fromPosition > topInScreenIndex) && (toPosition < topInScreenIndex) ||
1268                 ((fromPosition < topInScreenIndex) && (toPosition > topInScreenIndex)))
1269             {
1270                 scrollPosition = GetItemPosition(topInScreenIndex);
1271                 scrollPosition -= offset;
1272
1273                 colView.ScrollTo(scrollPosition);
1274             }
1275             */
1276
1277             // Update Viewport in delay.
1278             // FIMXE: original we only need to process RequestLayout once before layout calculation in main loop.
1279             // but currently we do not have any accessor to pre-calculation so instead of this,
1280             // using Timer temporarily.
1281             DelayedRequestLayout(scrollPosition);
1282         }
1283
1284         /// <Inheritdoc/>
1285         [EditorBrowsable(EditorBrowsableState.Never)]
1286         public override void NotifyItemRangeChanged(IItemSource source, int startRange, int endRange)
1287         {
1288             // Reorder Group
1289             if (source == null) throw new ArgumentNullException(nameof(source));
1290             IGroupableItemSource gSource = source as IGroupableItemSource;
1291             if (gSource == null)throw new Exception("Source is not group!");
1292
1293             // Get the first Visible Position to adjust.
1294             /*
1295             int topInScreenIndex = 0;
1296             float offset = 0F;
1297             (topInScreenIndex, offset) = FindTopItemInScreen();
1298             */
1299
1300
1301             // Unrealize, initialized all items in the Range
1302             // and receate all.
1303
1304             // Update Viewport in delay.
1305             // FIMXE: original we only need to process RequestLayout once before layout calculation in main loop.
1306             // but currently we do not have any accessor to pre-calculation so instead of this,
1307             // using Timer temporarily.
1308             DelayedRequestLayout(PrevScrollPosition);
1309         }
1310
1311         /// <Inheritdoc/>
1312         [EditorBrowsable(EditorBrowsableState.Never)]
1313         public override float CalculateLayoutOrientationSize()
1314         {
1315             //Console.WriteLine("[NUI] Calculate Layout ScrollContentSize {0}", ScrollContentSize);
1316             return ScrollContentSize;
1317         }
1318
1319         /// <Inheritdoc/>
1320         [EditorBrowsable(EditorBrowsableState.Never)]
1321         public override float CalculateCandidateScrollPosition(float scrollPosition)
1322         {
1323             //Console.WriteLine("[NUI] Calculate Candidate ScrollContentSize {0}", ScrollContentSize);
1324             return scrollPosition;
1325         }
1326
1327         /// <Inheritdoc/>
1328         [EditorBrowsable(EditorBrowsableState.Never)]
1329         public override View RequestNextFocusableView(View currentFocusedView, View.FocusDirection direction, bool loopEnabled)
1330         {
1331             if (currentFocusedView == null)
1332                 throw new ArgumentNullException(nameof(currentFocusedView));
1333
1334             View nextFocusedView = null;
1335             int targetSibling = -1;
1336
1337             switch (direction)
1338             {
1339                 case View.FocusDirection.Left:
1340                     {
1341                         targetSibling = IsHorizontal? currentFocusedView.SiblingOrder - 1 : targetSibling;
1342                         break;
1343                     }
1344                 case View.FocusDirection.Right:
1345                     {
1346                         targetSibling = IsHorizontal? currentFocusedView.SiblingOrder + 1 : targetSibling;
1347                         break;
1348                     }
1349                 case View.FocusDirection.Up:
1350                     {
1351                         targetSibling = IsHorizontal? targetSibling : currentFocusedView.SiblingOrder - 1;
1352                         break;
1353                     }
1354                 case View.FocusDirection.Down:
1355                     {
1356                         targetSibling = IsHorizontal? targetSibling : currentFocusedView.SiblingOrder + 1;
1357                         break;
1358                     }
1359             }
1360
1361             if (targetSibling > -1 && targetSibling < Container.Children.Count)
1362             {
1363                 RecyclerViewItem candidate = Container.Children[targetSibling] as RecyclerViewItem;
1364                 if (candidate != null && candidate.Index >= 0 && candidate.Index < colView.InternalItemSource.Count)
1365                 {
1366                     nextFocusedView = candidate;
1367                 }
1368             }
1369
1370             return nextFocusedView;
1371         }
1372
1373         /// <inheritdoc/>
1374         [EditorBrowsable(EditorBrowsableState.Never)]
1375         protected override (int start, int end) FindVisibleItems((float X, float Y) visibleArea)
1376         {
1377             int MaxIndex = colView.InternalItemSource.Count - 1 - (hasFooter? 1 : 0);
1378             int adds = 5;
1379             int skipGroup = -2;
1380             (int start, int end) found = (0, 0);
1381
1382             // 1. Find the start index.
1383             // Header is Showing
1384             if (hasHeader && visibleArea.X <= headerSize + (IsHorizontal? Padding.Start: Padding.Top))
1385             {
1386                 found.start = 0;
1387             }
1388             else
1389             {
1390                 if (isGrouped)
1391                 {
1392                     bool failed = true;
1393                     foreach (GroupInfo gInfo in groups)
1394                     {
1395                         skipGroup++;
1396                         // in the Group
1397                         if (gInfo.GroupPosition <= visibleArea.X &&
1398                             gInfo.GroupPosition + gInfo.GroupSize >= visibleArea.X)
1399                         {
1400                             for (int i = 0; i < gInfo.Count; i++)
1401                             {
1402                                 // Reach last index of group.
1403                                 if (i == (gInfo.Count - 1))
1404                                 {
1405                                     found.start = gInfo.StartIndex + i - adds;
1406                                     failed = false;
1407                                     break;
1408
1409                                 }
1410                                 else if (GetGroupPosition(gInfo, gInfo.StartIndex + i) <= visibleArea.X &&
1411                                         GetGroupPosition(gInfo, gInfo.StartIndex + i + 1) >= visibleArea.X)
1412                                 {
1413                                     found.start = gInfo.StartIndex + i - adds;
1414                                     failed = false;
1415                                     break;
1416                                 }
1417                             }
1418                         }
1419                     }
1420                     //footer only shows?
1421                     if (failed)
1422                     {
1423                         found.start = MaxIndex;
1424                     }
1425                 }
1426                 else
1427                 {
1428                     float visibleAreaX = visibleArea.X - (hasHeader? headerSize : 0);
1429                     // Prevent zero division.
1430                     var itemSize = (StepCandidate != 0)? StepCandidate : 1f;
1431                     found.start = (Convert.ToInt32(Math.Abs(visibleAreaX / itemSize)) - adds);
1432                 }
1433
1434                 if (found.start < 0) found.start = 0;
1435             }
1436
1437             if (hasFooter && visibleArea.Y > ScrollContentSize - footerSize)
1438             {
1439                 found.end = MaxIndex + 1;
1440             }
1441             else
1442             {
1443                 if (isGrouped)
1444                 {
1445                     bool failed = true;
1446                     // can it be start from founded group...?
1447                     //foreach(GroupInfo gInfo in groups.Skip(skipGroup))
1448                     foreach (GroupInfo gInfo in groups)
1449                     {
1450                         // in the Group
1451                         if (gInfo.GroupPosition <= visibleArea.Y &&
1452                             gInfo.GroupPosition + gInfo.GroupSize >= visibleArea.Y)
1453                         {
1454                             for (int i = 0; i < gInfo.Count; i++)
1455                             {
1456                                 if (i == (gInfo.Count - 1))
1457                                 {
1458                                     //Should be groupFooter!
1459                                     found.end = gInfo.StartIndex + i + adds;
1460                                     failed = false;
1461                                     break;
1462
1463                                 }
1464                                 else if (GetGroupPosition(gInfo, gInfo.StartIndex + i) <= visibleArea.Y &&
1465                                         GetGroupPosition(gInfo, gInfo.StartIndex + i + 1) >= visibleArea.Y)
1466                                 {
1467                                     found.end = gInfo.StartIndex + i + adds;
1468                                     failed = false;
1469                                     break;
1470                                 }
1471                             }
1472                         }
1473                     }
1474                     if (failed) found.end = MaxIndex;
1475                 }
1476                 else
1477                 {
1478                     float visibleAreaY = visibleArea.Y - (hasHeader? headerSize : 0);
1479                     // Prevent zero division.
1480                     var itemSize = (StepCandidate != 0)? StepCandidate : 1f;
1481                     found.end = (Convert.ToInt32(Math.Abs(visibleAreaY / itemSize)) + adds);
1482                     if (hasHeader) found.end += 1;
1483                 }
1484                 if (found.end > (MaxIndex)) found.end = MaxIndex;
1485             }
1486             return found;
1487         }
1488
1489         // Item position excluding margins.
1490         internal override (float X, float Y) GetItemPosition(int index)
1491         {
1492             int spaceStartX = Padding.Start;
1493             int spaceStartY = Padding.Top;
1494             if (colView.InternalItemSource.IsHeader(index))
1495             {
1496                 return (spaceStartX + headerMargin.Start, spaceStartY + headerMargin.Top);
1497             }
1498             else if (colView.InternalItemSource.IsFooter(index))
1499             {
1500                 return ((IsHorizontal? ScrollContentSize - footerSize - Padding.End + footerMargin.Start : spaceStartX + footerMargin.Start),
1501                         (IsHorizontal? spaceStartY + footerMargin.Top : ScrollContentSize - footerSize - Padding.Bottom + footerMargin.Top));
1502             }
1503             else if (isGrouped)
1504             {
1505                 GroupInfo gInfo = GetGroupInfo(index);
1506                 if (gInfo == null)
1507                 {
1508                     Tizen.Log.Error("NUI", "GroupInfo failed to get in GetItemPosition()!");
1509                     return (0, 0);
1510                 }
1511                 float current = GetGroupPosition(gInfo, index);
1512                 Extents itemMargin = CandidateMargin;
1513
1514                 if (colView.InternalItemSource.IsGroupHeader(index))
1515                 {
1516                     itemMargin = groupHeaderMargin;
1517                 }
1518                 else if (colView.InternalItemSource.IsGroupFooter(index))
1519                 {
1520                     itemMargin = groupFooterMargin;
1521                 }
1522                 return ((IsHorizontal?
1523                         itemMargin.Start + GetGroupPosition(gInfo, index):
1524                         spaceStartX + itemMargin.Start),
1525                         (IsHorizontal?
1526                         spaceStartY + itemMargin.Top:
1527                         itemMargin.Top + GetGroupPosition(gInfo, index)));
1528             }
1529             else if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
1530             {
1531                 //FIXME : CandidateMargin need to be actual itemMargin
1532                 return ((IsHorizontal? ItemPosition[index] + CandidateMargin.Start : spaceStartX + CandidateMargin.Start),
1533                         (IsHorizontal? spaceStartY + CandidateMargin.Top : ItemPosition[index] + CandidateMargin.Top));
1534             }
1535             else
1536             {
1537                 int adjustIndex = index - (hasHeader ? 1 : 0);
1538                 float current = (IsHorizontal ? spaceStartX : spaceStartY) + (hasHeader? headerSize : 0) + adjustIndex * StepCandidate;
1539                 //FIXME : CandidateMargin need to be actual itemMargin
1540                 return ((IsHorizontal? current + CandidateMargin.Start : spaceStartX + CandidateMargin.Start),
1541                         (IsHorizontal? spaceStartY + CandidateMargin.Top : current + CandidateMargin.Top));
1542             }
1543         }
1544
1545         // Item size excluding margins. this size is approximated size.
1546         internal override (float Width, float Height) GetItemSize(int index)
1547         {
1548             if (colView.InternalItemSource.IsHeader(index))
1549             {
1550                 return ((IsHorizontal? (int)headerSize : (int)(colView.Size.Width) - Padding.Start - Padding.End)
1551                         - headerMargin.Start - headerMargin.End,
1552                         (IsHorizontal? (int)colView.Size.Height - Padding.Top - Padding.Bottom: (int)headerSize)
1553                         - headerMargin.Top - headerMargin.Bottom);
1554             }
1555             else if (colView.InternalItemSource.IsFooter(index))
1556             {
1557                 return ((IsHorizontal? (int)footerSize : (int)(colView.Size.Width) - Padding.Start - Padding.End)
1558                         - footerMargin.Start - footerMargin.End,
1559                         (IsHorizontal? (int)colView.Size.Height - Padding.Top - Padding.Bottom: (int)footerSize)
1560                         - footerMargin.Top - footerMargin.Bottom);
1561             }
1562             else if (colView.InternalItemSource.IsGroupHeader(index))
1563             {
1564                 return ((IsHorizontal? (int)groupHeaderSize : (int)(colView.Size.Width) - Padding.Start - Padding.End)
1565                         - groupHeaderMargin.Start - groupHeaderMargin.End,
1566                         (IsHorizontal? (int)colView.Size.Height - Padding.Top - Padding.Bottom: (int)groupHeaderSize)
1567                         - groupHeaderMargin.Top - groupHeaderMargin.Bottom);
1568             }
1569             else if (colView.InternalItemSource.IsGroupFooter(index))
1570             {
1571                 return ((IsHorizontal? (int)groupFooterSize : (int)(colView.Size.Width) - Padding.Start - Padding.End)
1572                         - groupFooterMargin.Start - groupFooterMargin.End,
1573                         (IsHorizontal? (int)colView.Size.Height - Padding.Top - Padding.Bottom: (int)groupFooterSize)
1574                         - groupFooterMargin.Top - groupFooterMargin.Bottom);
1575             }
1576             else
1577             {
1578                 return ((IsHorizontal? (int)StepCandidate : (int)(colView.Size.Width) - Padding.Start - Padding.End)
1579                         - CandidateMargin.Start - CandidateMargin.End,
1580                         (IsHorizontal? (int)colView.Size.Height - Padding.Top - Padding.Bottom: (int)StepCandidate)
1581                         - CandidateMargin.Top - CandidateMargin.Bottom);
1582             }            
1583         }
1584
1585         private void DelayedRequestLayout(float scrollPosition , bool force = true)
1586         {
1587             if (requestLayoutTimer != null)
1588             {
1589                 requestLayoutTimer.Dispose();
1590             }
1591
1592             requestLayoutTimer = new Timer(1);
1593             requestLayoutTimer.Interval = 1;
1594             requestLayoutTimer.Tick += ((object target, Timer.TickEventArgs args) =>
1595             {
1596                 RequestLayout(scrollPosition, force);
1597                 return false;
1598             });
1599         }
1600
1601         /*
1602         private (int, float) FindTopItemInScreen()
1603         {
1604             int index = -1;
1605             float offset = 0.0F, Pos, Size;
1606
1607             foreach(RecyclerViewItem item in VisibleItems)
1608             {
1609                 Pos = IsHorizontal ? item.PositionX : item.PositionY;
1610                 Size = IsHorizontal ? item.SizeWidth : item.SizeHeight;
1611                 if (PrevScrollPosition >= Pos && PrevScrollPosition < Pos + Size)
1612                 {
1613                     index = item.Index;
1614                     offset = Pos - PrevScrollPosition;
1615                     break;
1616                 }
1617             }
1618
1619             return (index, offset);
1620         }
1621         */
1622
1623         private float GetItemStepSize(int index)
1624         {
1625             if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
1626             {
1627                 return ItemSize[index];
1628             }
1629             else
1630             {
1631                 if (colView.InternalItemSource.IsHeader(index))
1632                     return headerSize;
1633                 else if (colView.InternalItemSource.IsFooter(index))
1634                     return footerSize;
1635                 else if (colView.InternalItemSource.IsGroupHeader(index))
1636                     return groupHeaderSize;
1637                 else if (colView.InternalItemSource.IsGroupFooter(index))
1638                     return groupFooterSize;
1639                 else
1640                     return StepCandidate;
1641             }            
1642         }
1643
1644         private void UpdatePosition(int index)
1645         {
1646             bool IsGroup = (colView.InternalItemSource is IGroupableItemSource);
1647
1648             if (index <= 0) return;
1649             if (index >= colView.InternalItemSource.Count)
1650
1651                 if (IsGroup)
1652                 {
1653                     //IsGroupHeader = (colView.InternalItemSource as IGroupableItemSource).IsGroupHeader(index);
1654                     //IsGroupFooter = (colView.InternalItemSource as IGroupableItemSource).IsGroupFooter(index);
1655                     //Do Something
1656                 }
1657             if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
1658                 ItemPosition[index] = ItemPosition[index - 1] + GetItemStepSize(index - 1);
1659         }
1660
1661         private RecyclerViewItem GetVisibleItem(int index)
1662         {
1663             foreach (RecyclerViewItem item in VisibleItems)
1664             {
1665                 if (item.Index == index) return item;
1666             }
1667             return null;
1668         }
1669
1670         private GroupInfo GetGroupInfo(int index)
1671         {
1672             if (Visited != null)
1673             {
1674                 if (Visited.StartIndex <= index && Visited.StartIndex + Visited.Count > index)
1675                     return Visited;
1676             }
1677             if (hasHeader && index == 0) return null;
1678             foreach (GroupInfo group in groups)
1679             {
1680                 if (group.StartIndex <= index && group.StartIndex + group.Count > index)
1681                 {
1682                     Visited = group;
1683                     return group;
1684                 }
1685             }
1686             Visited = null;
1687             return null;
1688         }
1689
1690         private float GetGroupPosition(GroupInfo groupInfo, int index)
1691         {
1692             if (colView.SizingStrategy == ItemSizingStrategy.MeasureAll)
1693                 return groupInfo.GroupPosition + groupInfo.ItemPosition[index - groupInfo.StartIndex];
1694             else
1695             {
1696                 float pos = groupInfo.GroupPosition;
1697                 if (groupInfo.StartIndex == index) return pos;
1698
1699                 pos = pos + groupHeaderSize + StepCandidate * (index - groupInfo.StartIndex - 1);
1700
1701                 return pos;
1702             }
1703         }
1704         /*
1705                 private object GetGroupParent(int index)
1706                 {
1707                     if (Visited != null)
1708                     {
1709                         if (Visited.StartIndex <= index && Visited.StartIndex + Visited.Count > index)
1710                         return Visited.GroupParent;
1711                     }
1712                     if (hasHeader && index == 0) return null;
1713                     foreach (GroupInfo group in groups)
1714                     {
1715                         if (group.StartIndex <= index && group.StartIndex + group.Count > index)
1716                         {
1717                             Visited = group;
1718                             return group.GroupParent;
1719                         }
1720                     }
1721                     Visited = null;
1722                     return null;
1723                 }
1724         */
1725         class GroupInfo
1726         {
1727             public object GroupParent;
1728             public int StartIndex;
1729             public int Count;
1730             public float GroupSize;
1731             public float GroupPosition;
1732             //Items relative position from the GroupPosition. Only use for MeasureAll.
1733             public List<float> ItemPosition = new List<float>();
1734         }
1735     }
1736 }