1 /* Copyright (c) 2021 Samsung Electronics Co., Ltd.
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
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 using Tizen.NUI.BaseComponents;
18 using System.Collections.Generic;
19 using System.ComponentModel;
21 namespace Tizen.NUI.Components
24 /// Default layout manager for RecyclerView.
25 /// Layouting RecyclerViewItem on the scroll ContentContainer
26 /// which need to be visible on the view by scroll position.
28 /// <since_tizen> 9 </since_tizen>
29 public abstract class ItemsLayouter : ICollectionChangedNotifier, IDisposable
31 private bool disposed = false;
32 private Extents padding = new Extents(0, 0, 0, 0);
35 /// Padding for ContentContainer of RecyclerView.
37 [EditorBrowsable(EditorBrowsableState.Never)]
38 public Extents Padding {
46 if (ItemsView?.ContentContainer != null)
48 ItemsView.Padding = padding;
55 /// Internal item source that organized.
56 /// Check IItemSource and IGrouppedItemSoure also.
58 [EditorBrowsable(EditorBrowsableState.Never)]
59 protected IItemSource Source => ItemsView?.InternalSource;
62 /// Container which contains ViewItems.
64 [EditorBrowsable(EditorBrowsableState.Never)]
65 protected View Container =>ItemsView?.ContentContainer;
70 [EditorBrowsable(EditorBrowsableState.Never)]
71 protected RecyclerView ItemsView { get; set; }
74 /// The last scrolled position which is calculated by ScrollableBase. The value should be updated in the Recycle() method.
76 [EditorBrowsable(EditorBrowsableState.Never)]
77 protected float PrevScrollPosition { get; set; }
80 /// First index of visible items.
82 [EditorBrowsable(EditorBrowsableState.Never)]
83 protected int FirstVisible { get; set; } = -1;
86 /// Last index of visible items.
88 [EditorBrowsable(EditorBrowsableState.Never)]
89 protected int LastVisible { get; set; } = -1;
94 [EditorBrowsable(EditorBrowsableState.Never)]
95 protected List<RecyclerViewItem> VisibleItems { get; } = new List<RecyclerViewItem>();
100 [EditorBrowsable(EditorBrowsableState.Never)]
101 protected virtual List<GroupInfo> GroupItems { get; }
104 /// Flag of layouter initialization.
106 [EditorBrowsable(EditorBrowsableState.Never)]
107 protected bool IsInitialized { get; set; } = false;
110 /// Candidate item step size for scroll size measure.
112 [EditorBrowsable(EditorBrowsableState.Never)]
113 protected float StepCandidate { get; set; }
116 /// Candidate item's Margin for scroll size measure.
118 [EditorBrowsable(EditorBrowsableState.Never)]
119 protected Extents CandidateMargin { get; set; }
122 /// Content size of scrollable.
124 [EditorBrowsable(EditorBrowsableState.Never)]
125 protected float ScrollContentSize { get; set; }
128 /// boolean flag of scrollable horizontal direction.
130 [EditorBrowsable(EditorBrowsableState.Never)]
131 protected bool IsHorizontal { get; set; }
134 /// Clean up ItemsLayouter.
136 /// <param name="view"> ItemsView of layouter.</param>
137 /// <since_tizen> 9 </since_tizen>
138 public virtual void Initialize(RecyclerView view)
140 ItemsView = view ?? throw new ArgumentNullException(nameof(view));
141 PrevScrollPosition = 0.0f;
143 IsHorizontal = (view.ScrollingDirection == ScrollableBase.Direction.Horizontal);
145 IsInitialized = true;
149 /// This is called to find out where items are lain out according to current scroll position.
151 /// <param name="scrollPosition">Scroll position which is calculated by ScrollableBase</param>
152 /// <param name="force">boolean force flag to layouting forcely.</param>
153 /// <since_tizen> 9 </since_tizen>
154 public virtual void RequestLayout(float scrollPosition, bool force = false)
156 // Layouting Items in scrollPosition.
160 /// Clear the current screen and all properties.
162 /// <since_tizen> 9 </since_tizen>
163 public virtual void Clear()
165 if (VisibleItems != null)
167 foreach (RecyclerViewItem item in VisibleItems)
169 if (ItemsView != null) ItemsView.UnrealizeItem(item, false);
171 VisibleItems.Clear();
173 if (CandidateMargin != null)
175 CandidateMargin.Dispose();
176 CandidateMargin = null;
180 if (ItemsView != null) Container.Size = ItemsView.Size;
181 Container.Position = new Position(0.0f, 0.0f);
187 /// This is called to find out how much container size can be.
189 [EditorBrowsable(EditorBrowsableState.Never)]
190 public virtual float CalculateLayoutOrientationSize()
196 /// Adjust scrolling position by own scrolling rules.
198 /// <param name="scrollPosition">Scroll position which is calculated by ScrollableBase</param>
199 [EditorBrowsable(EditorBrowsableState.Never)]
200 public virtual float CalculateCandidateScrollPosition(float scrollPosition)
202 return scrollPosition;
206 /// Notify the relayout of ViewItem.
208 /// <param name="item">updated ViewItem.</param>
209 [EditorBrowsable(EditorBrowsableState.Never)]
210 public virtual void NotifyItemSizeChanged(RecyclerViewItem item)
215 /// Notify the dataset is Changed.
217 [EditorBrowsable(EditorBrowsableState.Never)]
218 public virtual void NotifyDataSetChanged()
220 if (ItemsView != null)
222 Initialize(ItemsView);
227 /// Notify the observable item in startIndex is changed.
229 /// <param name="source">Dataset source.</param>
230 /// <param name="startIndex">Changed item index.</param>
231 [EditorBrowsable(EditorBrowsableState.Never)]
232 public virtual void NotifyItemChanged(IItemSource source, int startIndex)
237 /// Notify the observable item is inserted in dataset.
239 /// <param name="source">Dataset source.</param>
240 /// <param name="startIndex">Inserted item index.</param>
241 [EditorBrowsable(EditorBrowsableState.Never)]
242 public virtual void NotifyItemInserted(IItemSource source, int startIndex)
247 /// Notify the observable item is moved from fromPosition to ToPosition.
249 /// <param name="source">Dataset source.</param>
250 /// <param name="fromPosition">Previous item position.</param>
251 /// <param name="toPosition">Moved item position.</param>
252 [EditorBrowsable(EditorBrowsableState.Never)]
253 public virtual void NotifyItemMoved(IItemSource source, int fromPosition, int toPosition)
258 /// Notify the range of the observable items are moved from fromPosition to ToPosition.
260 /// <param name="source"></param>
261 /// <param name="fromPosition"></param>
262 /// <param name="toPosition"></param>
263 /// <param name="count"></param>
264 [EditorBrowsable(EditorBrowsableState.Never)]
265 public virtual void NotifyItemRangeMoved(IItemSource source, int fromPosition, int toPosition, int count)
270 /// Notify the range of observable items from start to end are changed.
272 /// <param name="source">Dataset source.</param>
273 /// <param name="startRange">Start index of changed items range.</param>
274 /// <param name="endRange">End index of changed items range.</param>
275 [EditorBrowsable(EditorBrowsableState.Never)]
276 public virtual void NotifyItemRangeChanged(IItemSource source, int startRange, int endRange)
281 /// Notify the count range of observable items are inserted in startIndex.
283 /// <param name="source">Dataset source.</param>
284 /// <param name="startIndex">Start index of inserted items range.</param>
285 /// <param name="count">The number of inserted items.</param>
286 [EditorBrowsable(EditorBrowsableState.Never)]
287 public virtual void NotifyItemRangeInserted(IItemSource source, int startIndex, int count)
292 /// Notify the count range of observable items from the startIndex are removed.
294 /// <param name="source">Dataset source.</param>
295 /// <param name="startIndex">Start index of removed items range.</param>
296 /// <param name="count">The number of removed items</param>
297 [EditorBrowsable(EditorBrowsableState.Never)]
298 public virtual void NotifyItemRangeRemoved(IItemSource source, int startIndex, int count)
303 /// Notify the observable item in startIndex is removed.
305 /// <param name="source">Dataset source.</param>
306 /// <param name="startIndex">Index of removed item.</param>
307 [EditorBrowsable(EditorBrowsableState.Never)]
308 public virtual void NotifyItemRemoved(IItemSource source, int startIndex)
313 /// Gets the next keyboard focusable view in this control towards the given direction.<br />
314 /// A control needs to override this function in order to support two dimensional keyboard navigation.<br />
316 /// <param name="currentFocusedView">The current focused view.</param>
317 /// <param name="direction">The direction to move the focus towards.</param>
318 /// <param name="loopEnabled">Whether the focus movement should be looped within the control.</param>
319 /// <returns>The next keyboard focusable view in this control or an empty handle if no view can be focused.</returns>
320 [EditorBrowsable(EditorBrowsableState.Never)]
321 public virtual View RequestNextFocusableView(View currentFocusedView, View.FocusDirection direction, bool loopEnabled)
327 /// Dispose ItemsLayouter and all children on it.
329 /// <since_tizen> 9 </since_tizen>
330 public void Dispose()
333 GC.SuppressFinalize(this);
337 /// Measure the size of child ViewItem manually.
339 /// <param name="parent">Parent ItemsView.</param>
340 /// <param name="child">Child ViewItem to Measure()</param>
341 [EditorBrowsable(EditorBrowsableState.Never)]
342 protected virtual void MeasureChild(RecyclerView parent, RecyclerViewItem child)
344 if (parent == null) throw new ArgumentNullException(nameof(parent));
345 if (child == null) throw new ArgumentNullException(nameof(child));
347 if (child.Layout == null) return;
349 //FIXME: This measure can be restricted size of child to be less than parent size.
350 // but our parent can be not calculated yet, also some child can be bigger than it's parent size,
351 // so we use implicit value 9999 as restricted specification.
352 MeasureSpecification childWidthMeasureSpec = LayoutGroup.GetChildMeasureSpecification(
353 new MeasureSpecification(new LayoutLength(9999), MeasureSpecification.ModeType.Exactly),
355 new LayoutLength(child.WidthSpecification));
357 MeasureSpecification childHeightMeasureSpec = LayoutGroup.GetChildMeasureSpecification(
358 new MeasureSpecification(new LayoutLength(9999), MeasureSpecification.ModeType.Exactly),
360 new LayoutLength(child.HeightSpecification));
362 child.Layout.Measure(childWidthMeasureSpec, childHeightMeasureSpec);
366 /// Find consecutive visible items index.
368 /// <param name="visibleArea">float turple of visible area start position to end position. </param>
369 /// <return>int turple of start index to end index</return>
370 [EditorBrowsable(EditorBrowsableState.Never)]
371 protected virtual (int start, int end) FindVisibleItems((float X, float Y) visibleArea)
377 /// Dispose ItemsLayouter and all children on it.
379 /// <param name="disposing">true when it disposed by Dispose(). </param>
380 [EditorBrowsable(EditorBrowsableState.Never)]
381 protected virtual void Dispose(bool disposing)
397 /// Get item position.
399 [EditorBrowsable(EditorBrowsableState.Never)]
400 protected internal virtual (float X, float Y) GetItemPosition(int index)
408 [EditorBrowsable(EditorBrowsableState.Never)]
409 protected internal virtual (float Width, float Height) GetItemSize(int index)
415 /// Get visible item object on index if it is realized.
417 [EditorBrowsable(EditorBrowsableState.Never)]
418 protected virtual RecyclerViewItem GetVisibleItem(int index)
421 foreach (RecyclerViewItem item in VisibleItems)
423 if (item.Index == index) return item;
429 /// The data class for group informations.
430 /// inherited class can use this data to managing group items feature.
432 [EditorBrowsable(EditorBrowsableState.Never)]
433 protected internal class GroupInfo
436 /// Group parent object.
438 [EditorBrowsable(EditorBrowsableState.Never)]
439 public object GroupParent;
442 /// Group start index.
444 [EditorBrowsable(EditorBrowsableState.Never)]
445 public int StartIndex;
450 [EditorBrowsable(EditorBrowsableState.Never)]
454 /// Group size. this value is size of scrollable axis only.
456 [EditorBrowsable(EditorBrowsableState.Never)]
457 public float GroupSize;
460 /// Group position. this value is size of scrollable axis only.
462 [EditorBrowsable(EditorBrowsableState.Never)]
463 public float GroupPosition;
466 /// List of group items position.
468 [EditorBrowsable(EditorBrowsableState.Never)]
469 public List<float> ItemPosition = new List<float>();