[NUI] Use 'Container GetParent() for derived class' instead of Parent.
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Wearable / src / public / RecyclerView / LinearRecycleLayoutManager.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 Tizen.NUI.Components;
19 using System.Collections.Generic;
20 using System.ComponentModel;
21
22 namespace Tizen.NUI.Wearable
23 {
24     /// <summary>
25     /// [Draft] This class implements a linear box layout.
26     /// </summary>
27     /// <since_tizen> 8 </since_tizen>
28     /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
29     [EditorBrowsable(EditorBrowsableState.Never)]
30     public class LinearRecycleLayoutManager : RecycleLayoutManager
31     {
32         private int firstVisibleItemIndex = -1;
33         private int lastVisibleItemIndex = -1;
34
35         private bool IsItemVisible(float scrollPosition, RecycleItem item)
36         {
37             bool result = false;
38             View list = Container.GetParent() as View;
39             if (list == null)
40             {
41                 return result;
42             }
43
44             Vector2 visibleArea = new Vector2(Math.Abs(scrollPosition),
45                 Math.Abs(scrollPosition) + (LayoutOrientation == Orientation.Horizontal ?
46                                                 list.Size.Width : list.Size.Height)
47             );
48
49             float firstCheckPoint = LayoutOrientation == Orientation.Horizontal ? item.Position.X : item.Position.Y;
50             float secondCheckPoint = LayoutOrientation == Orientation.Horizontal ?
51                                         firstCheckPoint + item.Size.Width :
52                                         firstCheckPoint + item.Size.Height;
53
54             // Tizen.Log.Error("NUI", "[1p] "+visibleArea.X+ " =< "+firstCheckPoint+" =< "+visibleArea.Y+" ==== \n");
55             // Tizen.Log.Error("NUI", "[2p] "+visibleArea.X+ " =< "+secondCheckPoint+" =< "+visibleArea.Y+" ==== \n");
56
57             result = (firstCheckPoint >= visibleArea.X && firstCheckPoint <= visibleArea.Y) || (secondCheckPoint >= visibleArea.X && secondCheckPoint <= visibleArea.Y);
58
59             return result;
60         }
61
62         /// <summary>
63         /// This is called to find out where items are lain out according to current scroll position.
64         /// </summary>
65         /// <param name="scrollPosition">Scroll position which is calculated by ScrollableBase</param>
66         /// <since_tizen> 8 </since_tizen>
67         /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
68         public override void Layout(float scrollPosition)
69         {
70             firstVisibleItemIndex = -1;
71             lastVisibleItemIndex = -1;
72
73             RecycleItem previousItem = null;
74
75             for (int i = 0; i < Container.Children.Count; i++)
76             {
77                 RecycleItem item = Container.Children[i] as RecycleItem;
78
79                 if (previousItem != null && item != null)
80                 {
81                     item.Position = LayoutOrientation == Orientation.Horizontal ?
82                         new Position(
83                             previousItem.Position.X + (previousItem.CurrentSize.Width != 0 ?
84                                                             previousItem.CurrentSize.Width :
85                                                             previousItem.Size.Width),
86                             item.PositionY
87                         ) :
88                         new Position(
89                             item.PositionX,
90                             previousItem.Position.Y + (previousItem.CurrentSize.Height != 0 ?
91                                                             previousItem.CurrentSize.Height :
92                                                             previousItem.Size.Height)
93                         );
94                 }
95
96                 bool isVisible = IsItemVisible(scrollPosition, item);
97
98                 if (isVisible)
99                 {
100                     firstVisibleItemIndex = firstVisibleItemIndex == -1 ? i : firstVisibleItemIndex;
101                     lastVisibleItemIndex = i;
102                 }
103
104                 previousItem = item;
105
106                 // Tizen.Log.Error("NUI","["+item.DataIndex+"] "+item.Position.Y+" ==== \n");
107             }
108
109             if (StepSize == 0)
110             {
111                 StepSize = LayoutOrientation == Orientation.Horizontal ? ItemSize.Width : ItemSize.Height;
112             }
113         }
114
115         /// <summary>
116         /// This is called to find out how much container size can be.
117         /// </summary>
118         /// <since_tizen> 8 </since_tizen>
119         /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
120         [EditorBrowsable(EditorBrowsableState.Never)]
121         public override float CalculateLayoutOrientationSize()
122         {
123             return StepSize * DataCount;
124         }
125
126         /// <summary>
127         /// This is called to find out which items should be recycled according to current scroll position.
128         /// </summary>
129         /// <param name="scrollPosition">Scroll position which is calculated by ScrollableBase</param>
130         /// <returns>List of RecycleItems which should be recycled.</returns>
131         /// <since_tizen> 8 </since_tizen>
132         /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
133         public override List<RecycleItem> Recycle(float scrollPosition)
134         {
135             List<RecycleItem> result = new List<RecycleItem>();
136
137             bool checkFront = (PrevScrollPosition - scrollPosition) > 0;
138
139             if (checkFront)
140             {
141                 if (firstVisibleItemIndex > 3)
142                 {
143                     // Too many item is in front!!! move first item to back!!!!
144                     RecycleItem target = Container.Children[0] as RecycleItem;
145                     if (target != null)
146                     {
147                         target.DataIndex = target.DataIndex + Container.Children.Count;
148                         target.SiblingOrder = Container.Children.Count - 1;
149
150                         result.Add(target);
151                     }
152                 }
153             }
154             else
155             {
156                 if (lastVisibleItemIndex < Container.Children.Count - 3)
157                 {
158                     RecycleItem prevFirstItem = Container.Children[0] as RecycleItem;
159                     RecycleItem target = Container.Children[Container.Children.Count - 1] as RecycleItem;
160                     if (prevFirstItem != null && target != null)
161                     {
162                         target.Position = new Position(
163                             LayoutOrientation == Orientation.Horizontal ? (prevFirstItem.Position.X - target.Size.Width) : prevFirstItem.Position.X,
164                             LayoutOrientation == Orientation.Horizontal ? prevFirstItem.Position.Y : (prevFirstItem.Position.Y - target.Size.Height)
165                         );
166                         target.DataIndex = target.DataIndex - Container.Children.Count;
167                         target.SiblingOrder = 0;
168
169                         result.Add(target);
170                     }
171                 }
172             }
173
174             PrevScrollPosition = scrollPosition;
175
176             return result;
177         }
178
179         /// <summary>
180         /// Adjust scrolling position by own scrolling rules.
181         /// </summary>
182         /// <param name="scrollPosition">Scroll position which is calculated by ScrollableBase</param>
183         /// <since_tizen> 8 </since_tizen>
184         /// This may be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API
185         public override float CalculateCandidateScrollPosition(float scrollPosition)
186         {
187             return scrollPosition;
188         }
189
190         public override View RequestNextFocusableView(View currentFocusedView, View.FocusDirection direction, bool loopEnabled)
191         {
192             View nextFocusedView = null;
193             int targetSibling = -1;
194             bool isHorizontal = LayoutOrientation == Orientation.Horizontal;
195
196             switch (direction)
197             {
198                 case View.FocusDirection.Left:
199                     {
200                         targetSibling = isHorizontal ? currentFocusedView.SiblingOrder - 1 : targetSibling;
201                         break;
202                     }
203                 case View.FocusDirection.Right:
204                     {
205                         targetSibling = isHorizontal ? currentFocusedView.SiblingOrder + 1 : targetSibling;
206                         break;
207                     }
208                 case View.FocusDirection.Up:
209                     {
210                         targetSibling = isHorizontal ? targetSibling : currentFocusedView.SiblingOrder - 1;
211                         break;
212                     }
213                 case View.FocusDirection.Down:
214                     {
215                         targetSibling = isHorizontal ? targetSibling : currentFocusedView.SiblingOrder + 1;
216                         break;
217                     }
218             }
219
220             if (targetSibling > -1 && targetSibling < Container.Children.Count)
221             {
222                 RecycleItem candidate = Container.Children[targetSibling] as RecycleItem;
223                 if (candidate != null && candidate.DataIndex >= 0 && candidate.DataIndex < DataCount)
224                 {
225                     nextFocusedView = candidate;
226                 }
227             }
228
229             return nextFocusedView;
230         }
231     }
232 }