2 * Copyright(c) 2019 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 using System.ComponentModel;
19 using System.Collections.Generic;
20 using Tizen.NUI.BaseComponents;
22 namespace Tizen.NUI.Components
25 /// FlexibleView ItemClick Event Arguments.
27 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
28 [EditorBrowsable(EditorBrowsableState.Never)]
29 public class FlexibleViewItemClickedEventArgs : EventArgs
32 /// The clicked FlexibleViewViewHolder.
34 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
35 [EditorBrowsable(EditorBrowsableState.Never)]
36 public FlexibleViewViewHolder ClickedView { get; set; }
40 /// FlexibleView ItemTouch Event Arguments.
42 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
43 [EditorBrowsable(EditorBrowsableState.Never)]
44 public class FlexibleViewItemTouchEventArgs : View.TouchEventArgs
47 /// The touched FlexibleViewViewHolder.
49 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
50 [EditorBrowsable(EditorBrowsableState.Never)]
51 public FlexibleViewViewHolder TouchedView { get; set; }
55 /// A flexible view for providing a limited window into a large data set.
57 /// <since_tizen> 6 </since_tizen>
58 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
59 [EditorBrowsable(EditorBrowsableState.Never)]
60 public partial class FlexibleView : Control
63 /// Constant value: -1.
65 /// <since_tizen> 6 </since_tizen>
66 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
67 [EditorBrowsable(EditorBrowsableState.Never)]
68 public static readonly int NO_POSITION = -1;
70 /// Constant value: -1.
72 /// <since_tizen> 6 </since_tizen>
73 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
74 [EditorBrowsable(EditorBrowsableState.Never)]
75 public static readonly int INVALID_TYPE = -1;
77 private FlexibleViewAdapter mAdapter;
78 private FlexibleViewLayoutManager mLayout;
79 private FlexibleViewRecycler mRecycler;
80 private RecycledViewPool mRecyclerPool;
81 private ChildHelper mChildHelper;
83 private PanGestureDetector mPanGestureDetector;
85 private int mFocusedItemIndex = NO_POSITION;
87 private AdapterHelper mAdapteHelper;
89 private ScrollBar mScrollBar = null;
90 private Timer mScrollBarShowTimer = null;
92 private EventHandler<FlexibleViewItemClickedEventArgs> clickEventHandlers;
93 private EventHandler<FlexibleViewItemTouchEventArgs> touchEventHandlers;
94 private EventHandler<NUI.StyleManager.StyleChangedEventArgs> styleChangedEventHandlers;
97 /// Creates a FlexibleView instance.
99 /// <since_tizen> 6 </since_tizen>
100 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
101 [EditorBrowsable(EditorBrowsableState.Never)]
102 public FlexibleView()
104 mRecyclerPool = new RecycledViewPool(this);
106 mRecycler = new FlexibleViewRecycler(this);
107 mRecycler.SetRecycledViewPool(mRecyclerPool);
109 mChildHelper = new ChildHelper(this);
111 mPanGestureDetector = new PanGestureDetector();
112 mPanGestureDetector.Attach(this);
113 mPanGestureDetector.Detected += OnPanGestureDetected;
115 mAdapteHelper = new AdapterHelper(this);
117 ClippingMode = ClippingModeType.ClipToBoundingBox;
121 /// Item click event.
123 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
124 [EditorBrowsable(EditorBrowsableState.Never)]
125 public event EventHandler<FlexibleViewItemClickedEventArgs> ItemClicked
129 clickEventHandlers += value;
134 clickEventHandlers -= value;
140 /// Item touch event.
142 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
143 [EditorBrowsable(EditorBrowsableState.Never)]
144 public event EventHandler<FlexibleViewItemTouchEventArgs> ItemTouch
148 touchEventHandlers += value;
153 touchEventHandlers -= value;
158 /// Style changed, for example default font size.
160 /// <since_tizen> 6 </since_tizen>
161 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
162 [EditorBrowsable(EditorBrowsableState.Never)]
163 public event EventHandler<NUI.StyleManager.StyleChangedEventArgs> StyleChanged
167 styleChangedEventHandlers += value;
172 styleChangedEventHandlers -= value;
176 private new Extents padding;
178 /// overwrite the Padding.
180 /// <since_tizen> 6 </since_tizen>
181 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
182 [EditorBrowsable(EditorBrowsableState.Never)]
183 public new Extents Padding
189 padding = new Extents((ushort start, ushort end, ushort top, ushort bottom) =>
191 padding.Start = start;
194 padding.Bottom = bottom;
204 padding = new Extents((ushort start, ushort end, ushort top, ushort bottom) =>
206 padding.Start = start;
209 padding.Bottom = bottom;
213 padding.CopyFrom(value);
218 /// Gets or sets the focused item index(adapter position).
220 /// <since_tizen> 6 </since_tizen>
221 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
222 [EditorBrowsable(EditorBrowsableState.Never)]
223 public int FocusedItemIndex
227 return mFocusedItemIndex;
231 if (value == mFocusedItemIndex)
236 if (mAdapter == null)
246 FlexibleViewViewHolder nextFocusView = FindViewHolderForAdapterPosition(value);
247 if (nextFocusView == null)
249 mLayout.ScrollToPosition(value);
253 mLayout.RequestChildRectangleOnScreen(this, nextFocusView, mRecycler, true);
254 DispatchFocusChanged(value);
260 /// Set a new adapter to provide child views on demand.
262 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
263 [EditorBrowsable(EditorBrowsableState.Never)]
264 public void SetAdapter(FlexibleViewAdapter adapter)
272 mAdapter.ItemEvent += OnItemEvent;
276 /// Retrieves the previously set adapter or null if no adapter is set.
278 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
279 [EditorBrowsable(EditorBrowsableState.Never)]
280 public FlexibleViewAdapter GetAdapter()
286 /// Set the FlexibleViewLayoutManager that this FlexibleView will use.
288 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
289 [EditorBrowsable(EditorBrowsableState.Never)]
290 public void SetLayoutManager(FlexibleViewLayoutManager layoutManager)
292 if (null == layoutManager) return;
293 mLayout = layoutManager;
295 mLayout.SetRecyclerView(this);
297 if (mLayout.CanScrollHorizontally())
299 mPanGestureDetector.AddDirection(PanGestureDetector.DirectionHorizontal);
301 else if (mLayout.CanScrollVertically())
303 mPanGestureDetector.AddDirection(PanGestureDetector.DirectionVertical);
308 /// Return the FlexibleViewLayoutManager currently responsible for layout policy for this FlexibleView.
310 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
311 [EditorBrowsable(EditorBrowsableState.Never)]
312 public FlexibleViewLayoutManager GetLayoutManager()
319 /// Convenience method to scroll to a certain position
321 /// <param name="position">Adapter position</param>
322 /// <param name="offset">The distance (in pixels) between the start edge of the item view and start edge of the FlexibleView.</param>
323 /// <since_tizen> 6 </since_tizen>
324 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
325 [EditorBrowsable(EditorBrowsableState.Never)]
326 public void ScrollToPositionWithOffset(int position, int offset)
328 mLayout.ScrollToPositionWithOffset(position, offset);
332 /// Move focus by direction.
334 /// <param name="direction">Direction. Should be "Left", "Right", "Up" or "Down" </param>
335 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
336 [EditorBrowsable(EditorBrowsableState.Never)]
337 public void MoveFocus(FlexibleViewLayoutManager.Direction direction)
339 mLayout.MoveFocus(direction, mRecycler);
343 /// Attach a scrollbar to this FlexibleView.
345 /// <param name="scrollBar">ScrollBar</param>
346 /// <since_tizen> 6 </since_tizen>
347 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
348 [EditorBrowsable(EditorBrowsableState.Never)]
349 public void AttachScrollBar(ScrollBar scrollBar)
351 if (scrollBar == null)
355 mScrollBar = scrollBar;
360 /// Detach the scrollbar from this FlexibleView.
362 /// <since_tizen> 6 </since_tizen>
363 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
364 [EditorBrowsable(EditorBrowsableState.Never)]
365 public void DetachScrollBar()
367 if (mScrollBar == null)
376 /// Return the FlexibleViewViewHolder for the item in the given position of the data set as of the latest layout pass.
377 /// This method checks only the children of FlexibleViewRecyclerView. If the item at the given position is not laid out, it will not create a new one.
379 /// <param name="position">The position of the item in the data set of the adapter</param>
380 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
381 [EditorBrowsable(EditorBrowsableState.Never)]
382 public FlexibleViewViewHolder FindViewHolderForLayoutPosition(int position)
384 int childCount = mChildHelper.GetChildCount();
385 for (int i = 0; i < childCount; i++)
387 if (mChildHelper.GetChildAt(i) is FlexibleViewViewHolder holder)
389 if (holder.LayoutPosition == position)
400 /// Return the FlexibleViewViewHolder for the item in the given position of the data set.
401 /// This method checks only the children of FlexibleViewRecyclerView. If the item at the given position is not laid out, it will not create a new one.
403 /// <param name="position">The position of the item in the data set of the adapter</param>
404 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
405 [EditorBrowsable(EditorBrowsableState.Never)]
406 public FlexibleViewViewHolder FindViewHolderForAdapterPosition(int position)
408 int childCount = mChildHelper.GetChildCount();
409 for (int i = 0; i < childCount; i++)
411 if (mChildHelper.GetChildAt(i) is FlexibleViewViewHolder holder)
413 if (holder.AdapterPosition == position)
424 /// Return the recycler instance.
426 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
427 [EditorBrowsable(EditorBrowsableState.Never)]
428 public FlexibleViewRecycler GetRecycler()
434 /// you can override it to clean-up your own resources.
436 /// <param name="type">DisposeTypes</param>
437 /// <since_tizen> 6 </since_tizen>
438 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
439 [EditorBrowsable(EditorBrowsableState.Never)]
440 protected override void Dispose(DisposeTypes type)
447 if (type == DisposeTypes.Explicit)
451 mLayout.StopScroll(false);
452 mLayout.ClearRecyclerView();
456 if (mAdapter != null)
458 mAdapter.ItemEvent -= OnItemEvent;
461 if (mPanGestureDetector != null)
463 mPanGestureDetector.Detected -= OnPanGestureDetected;
464 mPanGestureDetector.Dispose();
465 mPanGestureDetector = null;
468 if (mScrollBarShowTimer != null)
470 mScrollBarShowTimer.Tick -= OnShowTimerTick;
471 mScrollBarShowTimer.Stop();
472 mScrollBarShowTimer.Dispose();
473 mScrollBarShowTimer = null;
476 if (mRecyclerPool != null)
478 mRecyclerPool.Clear();
479 mRecyclerPool = null;
482 if (mChildHelper != null)
484 mChildHelper.Clear();
485 mChildHelper.Dispose();
493 /// you can override it to create your own default style.
495 /// <since_tizen> 6 </since_tizen>
496 /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
497 [EditorBrowsable(EditorBrowsableState.Never)]
498 protected override ViewStyle CreateViewStyle()
504 /// you can override it to relayout elements.
506 /// <since_tizen> 6 </since_tizen>
507 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
508 [EditorBrowsable(EditorBrowsableState.Never)]
509 public override void OnRelayout(Vector2 size, RelayoutContainer container)
511 if (mAdapter == null)
521 DispatchLayoutStep1();
523 mLayout.OnLayoutChildren(mRecycler);
525 RemoveAndRecycleScrapInt();
529 /// you can override it to do something for style change.
531 /// <since_tizen> 6 </since_tizen>
532 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API.
533 [EditorBrowsable(EditorBrowsableState.Never)]
534 public override void OnStyleChange(NUI.StyleManager styleManager, StyleChangeType change)
536 if (change == StyleChangeType.DefaultFontSizeChange)
538 NUI.StyleManager.StyleChangedEventArgs args = new NUI.StyleManager.StyleChangedEventArgs();
539 args.StyleManager = styleManager;
540 args.StyleChange = change;
542 styleChangedEventHandlers?.Invoke(this, args);
548 private void DispatchLayoutStep1()
550 ProcessAdapterUpdates();
555 private void ProcessAdapterUpdates()
557 mAdapteHelper.PreProcess();
560 private void OffsetPositionRecordsForInsert(int positionStart, int itemCount)
562 int childCount = mChildHelper.GetChildCount();
563 for (int i = 0; i < childCount; i++)
565 FlexibleViewViewHolder holder = mChildHelper.GetChildAt(i);
566 if (holder != null && holder.AdapterPosition >= positionStart)
568 holder.OffsetPosition(itemCount, false);
572 if (positionStart <= mFocusedItemIndex)
574 mFocusedItemIndex += itemCount;
578 private void OffsetPositionRecordsForRemove(int positionStart, int itemCount, bool applyToPreLayout)
580 int positionEnd = positionStart + itemCount;
581 int childCount = mChildHelper.GetChildCount();
582 for (int i = 0; i < childCount; i++)
584 FlexibleViewViewHolder holder = mChildHelper.GetChildAt(i);
587 if (holder.AdapterPosition >= positionEnd)
589 holder.OffsetPosition(-itemCount, applyToPreLayout);
591 else if (holder.AdapterPosition >= positionStart)
593 holder.FlagRemovedAndOffsetPosition(positionStart - 1, -itemCount, applyToPreLayout);
598 if (positionEnd <= mFocusedItemIndex)
600 mFocusedItemIndex -= itemCount;
602 else if (positionStart <= mFocusedItemIndex)
604 mFocusedItemIndex = positionStart;
605 if (mFocusedItemIndex >= mAdapter.GetItemCount())
607 mFocusedItemIndex = mAdapter.GetItemCount() - 1;
612 private void SaveOldPositions()
614 int childCount = mChildHelper.GetChildCount();
615 for (int i = 0; i < childCount; i++)
617 FlexibleViewViewHolder holder = mChildHelper.GetChildAt(i);
618 holder.SaveOldPosition();
622 private void ClearOldPositions()
624 int childCount = mChildHelper.GetChildCount();
625 for (int i = 0; i < childCount; i++)
627 FlexibleViewViewHolder holder = mChildHelper.GetChildAt(i);
628 holder.ClearOldPosition();
632 private void RemoveAndRecycleScrapInt()
634 int scrapCount = mRecycler.GetScrapCount();
635 for (int i = 0; i < scrapCount; i++)
637 FlexibleViewViewHolder scrap = mRecycler.GetScrapViewAt(i);
638 mChildHelper.RemoveView(scrap);
639 mRecycler.RecycleView(scrap);
644 private void ShowScrollBar(uint millisecond = 700, bool flagAni = false)
646 if (mScrollBar == null || mLayout == null)
651 float extent = mLayout.ComputeScrollExtent();
652 float range = mLayout.ComputeScrollRange();
657 float offset = mLayout.ComputeScrollOffset();
659 float size = mScrollBar.Direction == ScrollBar.DirectionType.Vertical ? mScrollBar.SizeHeight : mScrollBar.SizeWidth;
660 float thickness = mScrollBar.Direction == ScrollBar.DirectionType.Vertical ? mScrollBar.SizeWidth : mScrollBar.SizeHeight;
661 float length = (float)Math.Round(size * extent / range);
663 // avoid the tiny thumb
664 float minLength = thickness * 2;
665 if (length < minLength)
669 // avoid the too-big thumb
670 if (offset > range - extent)
672 offset = range - extent;
678 if (mScrollBar.Direction == ScrollBar.DirectionType.Vertical)
680 mScrollBar.Style.Thumb.Size = new Size(thickness, length);
684 mScrollBar.Style.Thumb.Size = new Size(length, thickness);
686 mScrollBar.MinValue = 0;
687 mScrollBar.MaxValue = (int)(range - extent);
688 mScrollBar.SetCurrentValue((int)offset, flagAni);
690 if (mScrollBarShowTimer == null)
692 mScrollBarShowTimer = new Timer(millisecond);
693 mScrollBarShowTimer.Tick += OnShowTimerTick;
697 mScrollBarShowTimer.Interval = millisecond;
699 mScrollBarShowTimer.Start();
702 private bool OnShowTimerTick(object source, EventArgs e)
704 if (mScrollBar != null)
712 internal void DispatchFocusChanged(int nextFocusPosition)
714 mAdapter.OnFocusChange(this, mFocusedItemIndex, nextFocusPosition);
716 mFocusedItemIndex = nextFocusPosition;
721 private void DispatchChildAttached(FlexibleViewViewHolder holder)
723 if (mAdapter != null && holder != null)
725 mAdapter.OnViewAttachedToWindow(holder);
729 private void DispatchChildDetached(FlexibleViewViewHolder holder)
731 if (mAdapter != null && holder != null)
733 mAdapter.OnViewDetachedFromWindow(holder);
737 private void DispatchChildDestroyed(FlexibleViewViewHolder holder)
739 if (mAdapter != null && holder != null)
741 mAdapter.OnDestroyViewHolder(holder);
745 private void DispatchItemClicked(FlexibleViewViewHolder clickedHolder)
747 FlexibleViewItemClickedEventArgs args = new FlexibleViewItemClickedEventArgs();
748 args.ClickedView = clickedHolder;
749 OnClickEvent(this, args);
752 private void DispatchItemTouched(FlexibleViewViewHolder touchedHolder, Touch touchEvent)
754 FlexibleViewItemTouchEventArgs args = new FlexibleViewItemTouchEventArgs();
755 args.TouchedView = touchedHolder;
756 args.Touch = touchEvent;
757 OnTouchEvent(this, args);
760 private void OnPanGestureDetected(object source, PanGestureDetector.DetectedEventArgs e)
762 if (e.PanGesture.State == Gesture.StateType.Started)
764 mLayout.StopScroll(true);
766 else if (e.PanGesture.State == Gesture.StateType.Continuing)
768 if (mLayout.CanScrollVertically())
770 mLayout.ScrollVerticallyBy(e.PanGesture.Displacement.Y, mRecycler, true);
772 else if (mLayout.CanScrollHorizontally())
774 mLayout.ScrollHorizontallyBy(e.PanGesture.Displacement.X, mRecycler, true);
779 else if (e.PanGesture.State == Gesture.StateType.Finished)
781 if (mLayout.CanScrollVertically())
783 mLayout.ScrollVerticallyBy(e.PanGesture.Velocity.Y * 600, mRecycler, false);
785 else if (mLayout.CanScrollHorizontally())
787 mLayout.ScrollHorizontallyBy(e.PanGesture.Velocity.X * 600, mRecycler, false);
789 ShowScrollBar(1200, true);
793 private void OnItemEvent(object sender, FlexibleViewAdapter.ItemEventArgs e)
797 case FlexibleViewAdapter.ItemEventType.Insert:
798 mAdapteHelper.OnItemRangeInserted(e.param[0], e.param[1]);
801 case FlexibleViewAdapter.ItemEventType.Remove:
802 mAdapteHelper.OnItemRangeRemoved(e.param[0], e.param[1]);
805 case FlexibleViewAdapter.ItemEventType.Move:
807 case FlexibleViewAdapter.ItemEventType.Change:
816 private void OnClickEvent(object sender, FlexibleViewItemClickedEventArgs e)
818 clickEventHandlers?.Invoke(sender, e);
821 private void OnTouchEvent(object sender, FlexibleViewItemTouchEventArgs e)
823 touchEventHandlers?.Invoke(sender, e);
826 internal void LayoutManagerRelayoutRequest()
831 internal ChildHelper GetChildHelper()