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;
54 /// Container which contains ViewItems.
56 [EditorBrowsable(EditorBrowsableState.Never)]
57 protected View Container { get; set; }
62 [EditorBrowsable(EditorBrowsableState.Never)]
63 protected RecyclerView ItemsView { get; set; }
66 /// The last scrolled position which is calculated by ScrollableBase. The value should be updated in the Recycle() method.
68 [EditorBrowsable(EditorBrowsableState.Never)]
69 protected float PrevScrollPosition { get; set; }
72 /// First index of visible items.
74 [EditorBrowsable(EditorBrowsableState.Never)]
75 protected int FirstVisible { get; set; } = -1;
78 /// Last index of visible items.
80 [EditorBrowsable(EditorBrowsableState.Never)]
81 protected int LastVisible { get; set; } = -1;
86 [EditorBrowsable(EditorBrowsableState.Never)]
87 protected List<RecyclerViewItem> VisibleItems { get; } = new List<RecyclerViewItem>();
90 /// Flag of layouter initialization.
92 [EditorBrowsable(EditorBrowsableState.Never)]
93 protected bool IsInitialized { get; set; } = false;
96 /// Candidate item step size for scroll size measure.
98 [EditorBrowsable(EditorBrowsableState.Never)]
99 protected float StepCandidate { get; set; }
102 /// Candidate item's Margin for scroll size measure.
104 [EditorBrowsable(EditorBrowsableState.Never)]
105 protected Extents CandidateMargin { get; set; }
108 /// Content size of scrollable.
110 [EditorBrowsable(EditorBrowsableState.Never)]
111 protected float ScrollContentSize { get; set; }
114 /// boolean flag of scrollable horizontal direction.
116 [EditorBrowsable(EditorBrowsableState.Never)]
117 protected bool IsHorizontal { get; set; }
120 /// Clean up ItemsLayouter.
122 /// <param name="view"> ItemsView of layouter.</param>
123 /// <since_tizen> 9 </since_tizen>
124 public virtual void Initialize(RecyclerView view)
126 ItemsView = view ?? throw new ArgumentNullException(nameof(view));
127 Container = view.ContentContainer;
128 PrevScrollPosition = 0.0f;
130 IsHorizontal = (view.ScrollingDirection == ScrollableBase.Direction.Horizontal);
132 IsInitialized = true;
136 /// This is called to find out where items are lain out according to current scroll position.
138 /// <param name="scrollPosition">Scroll position which is calculated by ScrollableBase</param>
139 /// <param name="force">boolean force flag to layouting forcely.</param>
140 /// <since_tizen> 9 </since_tizen>
141 public virtual void RequestLayout(float scrollPosition, bool force = false)
143 // Layouting Items in scrollPosition.
147 /// Clear the current screen and all properties.
149 /// <since_tizen> 9 </since_tizen>
150 public virtual void Clear()
152 if (VisibleItems != null)
154 foreach (RecyclerViewItem item in VisibleItems)
156 if (ItemsView != null) ItemsView.UnrealizeItem(item, false);
158 VisibleItems.Clear();
160 if (CandidateMargin != null)
162 CandidateMargin.Dispose();
163 CandidateMargin = null;
167 Container.Size = ItemsView.Size;
168 Container.Position = new Position(0.0f, 0.0f);
175 /// This is called to find out how much container size can be.
177 [EditorBrowsable(EditorBrowsableState.Never)]
178 public virtual float CalculateLayoutOrientationSize()
184 /// Adjust scrolling position by own scrolling rules.
186 /// <param name="scrollPosition">Scroll position which is calculated by ScrollableBase</param>
187 [EditorBrowsable(EditorBrowsableState.Never)]
188 public virtual float CalculateCandidateScrollPosition(float scrollPosition)
190 return scrollPosition;
194 /// Notify the relayout of ViewItem.
196 /// <param name="item">updated ViewItem.</param>
197 [EditorBrowsable(EditorBrowsableState.Never)]
198 public virtual void NotifyItemSizeChanged(RecyclerViewItem item)
203 /// Notify the dataset is Changed.
205 [EditorBrowsable(EditorBrowsableState.Never)]
206 public virtual void NotifyDataSetChanged()
208 Initialize(ItemsView);
212 /// Notify the observable item in startIndex is changed.
214 /// <param name="source">Dataset source.</param>
215 /// <param name="startIndex">Changed item index.</param>
216 [EditorBrowsable(EditorBrowsableState.Never)]
217 public virtual void NotifyItemChanged(IItemSource source, int startIndex)
222 /// Notify the observable item is inserted in dataset.
224 /// <param name="source">Dataset source.</param>
225 /// <param name="startIndex">Inserted item index.</param>
226 [EditorBrowsable(EditorBrowsableState.Never)]
227 public virtual void NotifyItemInserted(IItemSource source, int startIndex)
232 /// Notify the observable item is moved from fromPosition to ToPosition.
234 /// <param name="source">Dataset source.</param>
235 /// <param name="fromPosition">Previous item position.</param>
236 /// <param name="toPosition">Moved item position.</param>
237 [EditorBrowsable(EditorBrowsableState.Never)]
238 public virtual void NotifyItemMoved(IItemSource source, int fromPosition, int toPosition)
243 /// Notify the range of the observable items are moved from fromPosition to ToPosition.
245 /// <param name="source"></param>
246 /// <param name="fromPosition"></param>
247 /// <param name="toPosition"></param>
248 /// <param name="count"></param>
249 [EditorBrowsable(EditorBrowsableState.Never)]
250 public virtual void NotifyItemRangeMoved(IItemSource source, int fromPosition, int toPosition, int count)
255 /// Notify the range of observable items from start to end are changed.
257 /// <param name="source">Dataset source.</param>
258 /// <param name="startRange">Start index of changed items range.</param>
259 /// <param name="endRange">End index of changed items range.</param>
260 [EditorBrowsable(EditorBrowsableState.Never)]
261 public virtual void NotifyItemRangeChanged(IItemSource source, int startRange, int endRange)
266 /// Notify the count range of observable items are inserted in startIndex.
268 /// <param name="source">Dataset source.</param>
269 /// <param name="startIndex">Start index of inserted items range.</param>
270 /// <param name="count">The number of inserted items.</param>
271 [EditorBrowsable(EditorBrowsableState.Never)]
272 public virtual void NotifyItemRangeInserted(IItemSource source, int startIndex, int count)
277 /// Notify the count range of observable items from the startIndex are removed.
279 /// <param name="source">Dataset source.</param>
280 /// <param name="startIndex">Start index of removed items range.</param>
281 /// <param name="count">The number of removed items</param>
282 [EditorBrowsable(EditorBrowsableState.Never)]
283 public virtual void NotifyItemRangeRemoved(IItemSource source, int startIndex, int count)
288 /// Notify the observable item in startIndex is removed.
290 /// <param name="source">Dataset source.</param>
291 /// <param name="startIndex">Index of removed item.</param>
292 [EditorBrowsable(EditorBrowsableState.Never)]
293 public virtual void NotifyItemRemoved(IItemSource source, int startIndex)
298 /// Gets the next keyboard focusable view in this control towards the given direction.<br />
299 /// A control needs to override this function in order to support two dimensional keyboard navigation.<br />
301 /// <param name="currentFocusedView">The current focused view.</param>
302 /// <param name="direction">The direction to move the focus towards.</param>
303 /// <param name="loopEnabled">Whether the focus movement should be looped within the control.</param>
304 /// <returns>The next keyboard focusable view in this control or an empty handle if no view can be focused.</returns>
305 [EditorBrowsable(EditorBrowsableState.Never)]
306 public virtual View RequestNextFocusableView(View currentFocusedView, View.FocusDirection direction, bool loopEnabled)
312 /// Dispose ItemsLayouter and all children on it.
314 /// <since_tizen> 9 </since_tizen>
315 public void Dispose()
318 GC.SuppressFinalize(this);
322 /// Measure the size of child ViewItem manually.
324 /// <param name="parent">Parent ItemsView.</param>
325 /// <param name="child">Child ViewItem to Measure()</param>
326 [EditorBrowsable(EditorBrowsableState.Never)]
327 protected virtual void MeasureChild(RecyclerView parent, RecyclerViewItem child)
329 if (parent == null) throw new ArgumentNullException(nameof(parent));
330 if (child == null) throw new ArgumentNullException(nameof(child));
332 if (child.Layout == null) return;
334 //FIXME: This measure can be restricted size of child to be less than parent size.
335 // but in some multiple-line TextLabel can be long enough to over the it's parent size.
337 MeasureSpecification childWidthMeasureSpec = LayoutGroup.GetChildMeasureSpecification(
338 new MeasureSpecification(new LayoutLength(parent.Size.Width - parent.Padding.Start - parent.Padding.End - child.Margin.Start - child.Margin.End), MeasureSpecification.ModeType.Exactly),
340 new LayoutLength(child.WidthSpecification));
342 MeasureSpecification childHeightMeasureSpec = LayoutGroup.GetChildMeasureSpecification(
343 new MeasureSpecification(new LayoutLength(parent.Size.Height - parent.Padding.Top - parent.Padding.Bottom - child.Margin.Top - child.Margin.Bottom), MeasureSpecification.ModeType.Exactly),
345 new LayoutLength(child.HeightSpecification));
347 child.Layout.Measure(childWidthMeasureSpec, childHeightMeasureSpec);
351 /// Find consecutive visible items index.
353 /// <param name="visibleArea">float turple of visible area start position to end position. </param>
354 /// <return>int turple of start index to end index</return>
355 [EditorBrowsable(EditorBrowsableState.Never)]
356 protected virtual (int start, int end) FindVisibleItems((float X, float Y) visibleArea)
362 /// Dispose ItemsLayouter and all children on it.
364 /// <param name="disposing">true when it disposed by Dispose(). </param>
365 [EditorBrowsable(EditorBrowsableState.Never)]
366 protected virtual void Dispose(bool disposing)
381 internal virtual (float X, float Y) GetItemPosition(int index)
386 internal virtual (float Width, float Height) GetItemSize(int index)