[NUI] Introduce CollectionView and related classes. (#2525)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / RecyclerView / Layouter / ItemsLayouter.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 using Tizen.NUI.Binding;
21
22 namespace Tizen.NUI.Components
23 {
24     /// <summary>
25     /// Default layout manager for CollectionView.
26     /// Lay out ViewItem and recycle ViewItem.
27     /// </summary>
28     [EditorBrowsable(EditorBrowsableState.Never)]
29     public abstract class ItemsLayouter : ICollectionChangedNotifier, IDisposable
30     {
31         private bool disposed = false;
32
33         /// <summary>
34         /// Container which contains ViewItems.
35         /// </summary>
36         [EditorBrowsable(EditorBrowsableState.Never)]
37         protected View Container{ get ; set; }
38
39         /// <summary>
40         /// Parent ItemsView.
41         /// </summary>
42         [EditorBrowsable(EditorBrowsableState.Never)]
43         protected RecyclerView ItemsView{ get; set; }
44
45         /// <summary>
46         /// The last scrolled position which is calculated by ScrollableBase. The value should be updated in the Recycle() method.
47         /// </summary>
48         [EditorBrowsable(EditorBrowsableState.Never)]
49         protected float PrevScrollPosition { get; set; }
50
51         /// <summary>
52         /// First index of visible items.
53         /// </summary>
54         [EditorBrowsable(EditorBrowsableState.Never)]
55         protected int FirstVisible { get; set; } = -1;
56
57         /// <summary>
58         /// Last index of visible items.
59         /// </summary>
60         [EditorBrowsable(EditorBrowsableState.Never)]
61         protected int LastVisible { get; set; } = -1;
62
63         /// <summary>
64         /// Visible ViewItem.
65         /// </summary>
66         [EditorBrowsable(EditorBrowsableState.Never)]
67         protected List<RecyclerViewItem> VisibleItems { get; } = new List<RecyclerViewItem>();
68
69         /// <summary>
70         /// Flag of layouter initialization.
71         /// </summary>
72         [EditorBrowsable(EditorBrowsableState.Never)]
73         protected bool IsInitialized { get; set; } = false;
74
75         /// <summary>
76         /// Candidate item step size for scroll size measure.
77         /// </summary>
78         [EditorBrowsable(EditorBrowsableState.Never)]
79         protected float StepCandidate { get; set; }
80
81         /// <summary>
82         /// Content size of scrollable.
83         /// </summary>
84         [EditorBrowsable(EditorBrowsableState.Never)]
85         protected float ScrollContentSize { get; set; }
86
87         /// <summary>
88         /// boolean flag of scrollable horizontal direction.
89         /// </summary>
90         [EditorBrowsable(EditorBrowsableState.Never)]
91         protected bool IsHorizontal { get; set; }
92
93         /// <summary>
94         /// Clean up ItemsLayouter.
95         /// </summary>
96         /// <param name="view"> ItemsView of layouter.</param>
97         [EditorBrowsable(EditorBrowsableState.Never)]
98         public virtual void Initialize(RecyclerView view)
99         {
100             ItemsView = view ?? throw new ArgumentNullException(nameof(view));
101             Container = view.ContentContainer;
102             PrevScrollPosition = 0.0f;
103
104             IsHorizontal = (view.ScrollingDirection == ScrollableBase.Direction.Horizontal);
105
106             IsInitialized = true;
107         }
108
109         /// <summary>
110         /// This is called to find out where items are lain out according to current scroll position.
111         /// </summary>
112         /// <param name="scrollPosition">Scroll position which is calculated by ScrollableBase</param>
113         /// <param name="force">boolean force flag to layouting forcely.</param>
114         [EditorBrowsable(EditorBrowsableState.Never)]
115         public virtual void RequestLayout(float scrollPosition, bool force = false)
116         {
117            // Layouting Items in scrollPosition.
118         }
119
120         /// <summary>
121         /// Clear the current screen and all properties.
122         /// </summary>
123         [EditorBrowsable(EditorBrowsableState.Never)]
124         public virtual void Clear()
125         {
126             foreach (RecyclerViewItem item in VisibleItems)
127             {
128                 if (ItemsView != null) ItemsView.UnrealizeItem(item, false);
129             }
130             VisibleItems.Clear();
131             ItemsView = null;
132             Container = null;
133         }
134
135         /// <summary>
136         /// Position of layouting item.
137         /// </summary>
138         /// <param name="item">item of dataset.</param>
139         [EditorBrowsable(EditorBrowsableState.Never)]
140         public virtual (float X, float Y) GetItemPosition(object item)
141         {
142            if (item == null) throw new ArgumentNullException(nameof(item));
143            // Layouting Items in scrollPosition.
144            return (0, 0);
145         }
146
147         /// <summary>
148         /// Size of layouting item.
149         /// </summary>
150         /// <param name="item">item of dataset.</param>
151         [EditorBrowsable(EditorBrowsableState.Never)]
152         public virtual (float X, float Y) GetItemSize(object item)
153         {
154            if (item == null) throw new ArgumentNullException(nameof(item));
155            // Layouting Items in scrollPosition.
156            return (0, 0);
157         }
158
159         /// <summary>
160         /// This is called to find out how much container size can be.
161         /// </summary>
162         [EditorBrowsable(EditorBrowsableState.Never)]
163         public virtual float CalculateLayoutOrientationSize()
164         {
165             return 0.0f;
166         }
167
168         /// <summary>
169         /// Adjust scrolling position by own scrolling rules.
170         /// </summary>
171         /// <param name="scrollPosition">Scroll position which is calculated by ScrollableBase</param>
172         [EditorBrowsable(EditorBrowsableState.Never)]
173         public virtual float CalculateCandidateScrollPosition(float scrollPosition)
174         {
175             return scrollPosition;
176         }
177
178         /// <summary>
179         /// Notify the relayout of ViewItem.
180         /// </summary>
181         /// <param name="item">updated ViewItem.</param>
182         [EditorBrowsable(EditorBrowsableState.Never)]
183         public virtual void NotifyItemSizeChanged(RecyclerViewItem item)
184         {
185         }
186
187         /// <summary>
188         /// Notify the dataset is Changed.
189         /// </summary>
190         [EditorBrowsable(EditorBrowsableState.Never)]
191         public virtual void NotifyDataSetChanged()
192         {
193             Initialize(ItemsView);
194         }
195
196         /// <summary>
197         /// Notify the observable item in startIndex is changed.
198         /// </summary>
199         /// <param name="source">Dataset source.</param>
200         /// <param name="startIndex">Changed item index.</param>
201         [EditorBrowsable(EditorBrowsableState.Never)]
202         public virtual void NotifyItemChanged(IItemSource source, int startIndex)
203         {
204         }
205
206         /// <summary>
207         /// Notify the observable item is inserted in dataset.
208         /// </summary>
209         /// <param name="source">Dataset source.</param>
210         /// <param name="startIndex">Inserted item index.</param>
211         [EditorBrowsable(EditorBrowsableState.Never)]
212         public virtual void NotifyItemInserted(IItemSource source, int startIndex)
213         {
214         }
215
216         /// <summary>
217         /// Notify the observable item is moved from fromPosition to ToPosition.
218         /// </summary>
219         /// <param name="source">Dataset source.</param>
220         /// <param name="fromPosition">Previous item position.</param>
221         /// <param name="toPosition">Moved item position.</param>
222         [EditorBrowsable(EditorBrowsableState.Never)]
223         public virtual void NotifyItemMoved(IItemSource source, int fromPosition, int toPosition)
224         {
225         }
226
227         /// <summary>
228         /// Notify the range of observable items from start to end are changed.
229         /// </summary>
230         /// <param name="source">Dataset source.</param>
231         /// <param name="startRange">Start index of changed items range.</param>
232         /// <param name="endRange">End index of changed items range.</param>
233         [EditorBrowsable(EditorBrowsableState.Never)]
234         public virtual void NotifyItemRangeChanged(IItemSource source, int startRange, int endRange)
235         {
236         }
237
238         /// <summary>
239         /// Notify the count range of observable items are inserted in startIndex.
240         /// </summary>
241         /// <param name="source">Dataset source.</param>
242         /// <param name="startIndex">Start index of inserted items range.</param>
243         /// <param name="count">The number of inserted items.</param>
244         [EditorBrowsable(EditorBrowsableState.Never)]
245         public virtual void NotifyItemRangeInserted(IItemSource source, int startIndex, int count)
246         {
247         }
248
249         /// <summary>
250         /// Notify the count range of observable items from the startIndex are removed.
251         /// </summary>
252         /// <param name="source">Dataset source.</param>
253         /// <param name="startIndex">Start index of removed items range.</param>
254         /// <param name="count">The number of removed items</param>
255         [EditorBrowsable(EditorBrowsableState.Never)]
256         public virtual void NotifyItemRangeRemoved(IItemSource source, int startIndex, int count)
257         {
258         }
259
260         /// <summary>
261         /// Notify the observable item in startIndex is removed.
262         /// </summary>
263         /// <param name="source">Dataset source.</param>
264         /// <param name="startIndex">Index of removed item.</param>
265         [EditorBrowsable(EditorBrowsableState.Never)]
266         public virtual void NotifyItemRemoved(IItemSource source, int startIndex)
267         {
268         }
269
270         /// <summary>
271         /// Gets the next keyboard focusable view in this control towards the given direction.<br />
272         /// A control needs to override this function in order to support two dimensional keyboard navigation.<br />
273         /// </summary>
274         /// <param name="currentFocusedView">The current focused view.</param>
275         /// <param name="direction">The direction to move the focus towards.</param>
276         /// <param name="loopEnabled">Whether the focus movement should be looped within the control.</param>
277         /// <returns>The next keyboard focusable view in this control or an empty handle if no view can be focused.</returns>
278         [EditorBrowsable(EditorBrowsableState.Never)]
279         public virtual View RequestNextFocusableView(View currentFocusedView, View.FocusDirection direction, bool loopEnabled)
280         {
281             return null;
282         }
283
284         /// <summary>
285         /// Dispose ItemsLayouter and all children on it.
286         /// </summary>
287         [EditorBrowsable(EditorBrowsableState.Never)]
288         public void Dispose()
289         {
290             Dispose(true);
291             GC.SuppressFinalize(this);
292         }
293
294         /// <summary>
295         /// Measure the size of chlid ViewItem manually.
296         /// </summary>
297         /// <param name="parent">Parent ItemsView.</param>
298         /// <param name="child">Child ViewItem to Measure()</param>
299         [EditorBrowsable(EditorBrowsableState.Never)]
300         protected virtual void MeasureChild(RecyclerView parent, RecyclerViewItem child)
301         {
302             if (parent == null) throw new ArgumentNullException(nameof(parent));
303             if (child == null) throw new ArgumentNullException(nameof(child));
304
305             if (child.Layout == null) return;
306
307             //FIXME: This measure can be restricted size of child to be less than parent size.
308             // but in some multiple-line TextLabel can be long enough to over the it's parent size.
309
310             MeasureSpecification childWidthMeasureSpec = LayoutGroup.GetChildMeasureSpecification(
311                         new MeasureSpecification(new LayoutLength(parent.Size.Width), MeasureSpecification.ModeType.Exactly),
312                         new LayoutLength(0),
313                         new LayoutLength(child.WidthSpecification));
314
315             MeasureSpecification childHeightMeasureSpec = LayoutGroup.GetChildMeasureSpecification(
316                         new MeasureSpecification(new LayoutLength(parent.Size.Height), MeasureSpecification.ModeType.Exactly),
317                         new LayoutLength(0),
318                         new LayoutLength(child.HeightSpecification));
319
320             child.Layout.Measure(childWidthMeasureSpec, childHeightMeasureSpec);
321         }
322
323         /// <summary>
324         /// Find consecutive visible items index.
325         /// </summary>
326         /// <param name="visibleArea">float turple of visible area start position to end position. </param>
327         /// <return>int turple of start index to end index</return>
328         [EditorBrowsable(EditorBrowsableState.Never)]
329         protected virtual (int start, int end) FindVisibleItems((float X, float Y) visibleArea)
330         {
331             return (0, 0);
332         }
333
334         /// <summary>
335         /// Dispose ItemsLayouter and all children on it.
336         /// </summary>
337         /// <param name="disposing">true when it disposed by Dispose(). </param>
338         [EditorBrowsable(EditorBrowsableState.Never)]
339         protected virtual void Dispose(bool disposing)
340         {
341             if (disposed)
342             {
343                 return;
344             }
345
346             disposed = true;
347             if (disposing) Clear();
348         }
349     }
350 }