[NUI] Add GridRecycleLayotManager (#1685)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / RecyclerView / GridRecycleLayoutManager.cs
1 /* Copyright (c) 2020 Samsung Electronics Co., Ltd.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  *
15  */
16 using System;
17 using Tizen.NUI.BaseComponents;
18 using System.Collections.Generic;
19 using System.ComponentModel;
20
21 namespace Tizen.NUI.Components
22 {
23     /// <summary>
24     /// [Draft] This class implements a grid box layout.
25     /// </summary>
26     /// <since_tizen> 8 </since_tizen>
27     /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
28     [EditorBrowsable(EditorBrowsableState.Never)]
29     public class GridRecycleLayoutManager : RecycleLayoutManager
30     {
31         private int rows = 1;
32
33         /// <summary>
34         /// [draft ]Get/Set the number of rows in the grid
35         /// </summary>
36         /// <since_tizen> 8 </since_tizen>
37         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
38         [EditorBrowsable(EditorBrowsableState.Never)]
39         public int Rows
40         {
41             get
42             {
43                 return rows;
44             }
45             set
46             {
47                 rows = value;
48
49                 if (Container != null)
50                 {
51                     Layout(mPrevScrollPosition);
52                 }
53             }
54         }
55
56         private int columns = 1;
57
58
59         /// <summary>
60         /// [Draft] Get/Set the number of columns in the grid
61         /// </summary>
62         /// <since_tizen> 8 </since_tizen>
63         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
64         [EditorBrowsable(EditorBrowsableState.Never)]
65         public int Columns
66         {
67             get
68             {
69                 return columns;
70             }
71             set
72             {
73                 columns = value;
74
75                 if (Container != null)
76                 {
77                     Layout(mPrevScrollPosition);
78                 }
79             }
80         }
81
82         private int firstVisibleItemIndex = -1;
83         private int lastVisibleItemIndex = -1;
84
85         private bool IsItemVisible(float scrollPosition, RecycleItem item)
86         {
87             bool result = false;
88             View list = Container.GetParent() as View;
89
90             Vector2 visibleArea = new Vector2(Math.Abs(scrollPosition),
91                 Math.Abs(scrollPosition) + (LayoutOrientation == Orientation.Horizontal ?
92                                                 list.Size.Width : list.Size.Height)
93             );
94
95             float firstCheckPoint = LayoutOrientation == Orientation.Horizontal ? item.Position.X : item.Position.Y;
96             float secondCheckPoint = LayoutOrientation == Orientation.Horizontal ?
97                                         firstCheckPoint + item.Size.Width :
98                                         firstCheckPoint + item.Size.Height;
99
100             result = (firstCheckPoint >= visibleArea.X && firstCheckPoint <= visibleArea.Y) || (secondCheckPoint >= visibleArea.X && secondCheckPoint <= visibleArea.Y);
101
102             return result;
103         }
104
105         /// <summary>
106         /// This is called to find out how much container size can be.
107         /// </summary>
108         /// <since_tizen> 8 </since_tizen>
109         /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
110         [EditorBrowsable(EditorBrowsableState.Never)]
111         public override float CalculateLayoutOrientationSize()
112         {
113             float orientationFactor = LayoutOrientation == Orientation.Horizontal ? Rows : Columns;
114             return mStepSize * (int)Math.Ceiling((double)DataCount / (double)orientationFactor);
115         }
116
117
118         /// <summary>
119         /// This is called to find out where items are lain out according to current scroll position.
120         /// </summary>
121         /// <param name="scrollPosition">Scroll position which is calculated by ScrollableBase</param>
122         /// <since_tizen> 8 </since_tizen>
123         /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
124         public override void Layout(float scrollPosition)
125         {
126             int itemInGroup = LayoutOrientation == Orientation.Horizontal ? Rows : Columns;
127             firstVisibleItemIndex = -1;
128             lastVisibleItemIndex = -1;
129
130             RecycleItem previousItem = null;
131
132             for (int i = 0; i < Container.Children.Count; i++)
133             {
134                 RecycleItem item = Container.Children[i] as RecycleItem;
135
136                 if (previousItem != null)
137                 {
138                     item.Position = LayoutOrientation == Orientation.Horizontal ?
139                         new Position(
140                             (i % itemInGroup == 0 ?
141                             previousItem.Position.X + (previousItem.CurrentSize.Width != 0 ?
142                                                             previousItem.CurrentSize.Width :
143                                                             previousItem.Size.Width) :
144                             previousItem.Position.X),
145                             (i % itemInGroup == 0 ?
146                             0 :
147                             previousItem.PositionY + (previousItem.CurrentSize.Height != 0 ?
148                                                             previousItem.CurrentSize.Height :
149                                                             previousItem.Size.Height))
150                         ) :
151                         new Position(
152                             (i % itemInGroup == 0 ?
153                             0 :
154                             previousItem.PositionX + (previousItem.CurrentSize.Width != 0 ?
155                                                             previousItem.CurrentSize.Width :
156                                                             previousItem.Size.Width)),
157                             (i % itemInGroup == 0 ?
158                             previousItem.Position.Y + (previousItem.CurrentSize.Height != 0 ?
159                                                             previousItem.CurrentSize.Height :
160                                                             previousItem.Size.Height) :
161                             previousItem.Position.Y)
162                         );
163                 }
164
165                 bool isVisible = IsItemVisible(scrollPosition, item);
166
167                 if (isVisible)
168                 {
169                     firstVisibleItemIndex = firstVisibleItemIndex == -1 ? i : firstVisibleItemIndex;
170                     lastVisibleItemIndex = i;
171                 }
172
173                 previousItem = item;
174             }
175
176             if (mStepSize == 0)
177             {
178                 mStepSize = LayoutOrientation == Orientation.Horizontal ? ItemSize.Width : ItemSize.Height;
179             }
180         }
181
182
183         /// <summary>
184         /// This is called to find out which items should be recycled according to current scroll position.
185         /// </summary>
186         /// <param name="scrollPosition">Scroll position which is calculated by ScrollableBase</param>
187         /// <returns>List of RecycleItems which should be recycled.</returns>
188         /// <since_tizen> 8 </since_tizen>
189         /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
190         public override List<RecycleItem> Recycle(float scrollPosition)
191         {
192             List<RecycleItem> result = new List<RecycleItem>();
193             bool checkFront = (mPrevScrollPosition - scrollPosition) > 0;
194
195             int itemInGroup = LayoutOrientation == Orientation.Horizontal ? Rows : Columns;
196
197             if (checkFront)
198             {
199                 int currentGroupNum = (int)(firstVisibleItemIndex / itemInGroup) + 1;
200
201                 if (currentGroupNum > 2)
202                 {
203                     // Too many item is in front!!! move first item to back!!!!
204                     for (int i = 0; i < itemInGroup; i++)
205                     {
206                         RecycleItem target = Container.Children[0] as RecycleItem;
207                         target.DataIndex = target.DataIndex + Container.Children.Count;
208                         target.SiblingOrder = Container.Children.Count - 1;
209
210                         result.Add(target);
211                     }
212                 }
213             }
214             else
215             {
216                 int currentGroupNum = (int)(lastVisibleItemIndex / itemInGroup) + 1;
217
218                 if (currentGroupNum < (int)(Container.Children.Count / itemInGroup) - 3)
219                 {
220                     for (int i = 0; i < itemInGroup; i++)
221                     {
222                         RecycleItem prevFirstItem = Container.Children[itemInGroup] as RecycleItem;
223
224                         RecycleItem target = Container.Children[Container.Children.Count - 1] as RecycleItem;
225                         target.Position = new Position(
226                             LayoutOrientation == Orientation.Horizontal ? (prevFirstItem.Position.X - target.Size.Width) : prevFirstItem.Position.X,
227                             LayoutOrientation == Orientation.Horizontal ? prevFirstItem.Position.Y : (prevFirstItem.Position.Y - target.Size.Height)
228                         );
229                         target.DataIndex = target.DataIndex - Container.Children.Count;
230                         target.SiblingOrder = 0;
231
232                         result.Add(target);
233                     }
234                 }
235             }
236
237
238             mPrevScrollPosition = scrollPosition;
239
240             return result;
241         }
242
243         /// <summary>
244         /// Adjust scrolling position by own scrolling rules.
245         /// </summary>
246         /// <param name="scrollPosition">Scroll position which is calculated by ScrollableBase</param>
247         /// <since_tizen> 8 </since_tizen>
248         /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
249         public override float CalculateCandidateScrollPosition(float scrollPosition)
250         {
251             return scrollPosition;
252         }
253     }
254 }