From f131e9c4805a8ce436541795cc9886edbade8ee4 Mon Sep 17 00:00:00 2001 From: agnelovaz Date: Tue, 3 Dec 2019 19:47:07 +0900 Subject: [PATCH] DropDownButton to use Layout scrolling (#1160) Squashed: Now uses NUI Layouting for the drop downbox DropDown API fixes Delete/Select LayoutScroller immediate positioning Applied LayoutScoller updates LayoutScroller now a CustomLayout with OnMeasure and OnLayout implemented Velocity of flick not throttled hence can be fast. Updates after Style refactor Features: Persistant selection Icon/Checkbox Highlight for touched selection Scrolling list Styled using ControlStyle system. Change-Id: I656bc978331df5f38e1885635ba50b6b5e178976 --- src/Tizen.NUI.Components/Controls/DropDown.cs | 345 ++++++++++++++++----- .../Controls/LayoutScroller.cs | 314 +++++++++++++++++++ .../src/internal/Layouting/LayoutController.cs | 1 + 3 files changed, 577 insertions(+), 83 deletions(-) create mode 100755 src/Tizen.NUI.Components/Controls/LayoutScroller.cs diff --git a/src/Tizen.NUI.Components/Controls/DropDown.cs b/src/Tizen.NUI.Components/Controls/DropDown.cs index 71e21f0..a6e888b 100755 --- a/src/Tizen.NUI.Components/Controls/DropDown.cs +++ b/src/Tizen.NUI.Components/Controls/DropDown.cs @@ -157,12 +157,22 @@ namespace Tizen.NUI.Components private TextLabel headerText = null; private TextLabel buttonText = null; private ImageView listBackgroundImage = null; - private FlexibleView list = null; + + // Component that scrolls the child added to it. + private LayoutScroller layoutScroller = null; + + // The LinearLayout container to house the items in the drop down list. + private View dropDownMenuFullList = null; + private DropDownListBridge adapter = new DropDownListBridge(); - private DropDownItemView touchedView = null; private int selectedItemIndex = -1; private Extents listPadding = null; + DropDownItemView selectedItemView = null; + + private bool itemPressed = false; + + private TapGestureDetector tapGestureDetector = null; /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] @@ -217,7 +227,7 @@ namespace Tizen.NUI.Components public event ClickEventHandler ItemClickEvent; /// - /// List orientation. + /// List position in relation to the main button. /// /// 6 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API. @@ -312,6 +322,7 @@ namespace Tizen.NUI.Components set { SetValue(ListRelativeOrientationProperty, value); + RelayoutRequest(); } } private ListOrientation privateListRelativeOrientation @@ -398,7 +409,12 @@ namespace Tizen.NUI.Components } set { - SetValue(SelectedItemIndexProperty, value); + if (value == selectedItemIndex || adapter == null || value >= adapter.GetItemCount()) + { + return; + } + + SetValue(SelectedItemIndexProperty, value); // UpdatesSelectedItem } } private int privateSelectedItemIndex @@ -501,7 +517,9 @@ namespace Tizen.NUI.Components [EditorBrowsable(EditorBrowsableState.Never)] public void AddItem(DropDownDataItem itemData) { - adapter.InsertData(-1, itemData); + // Add item to adaptor, will be added to list via AddItemAt during OnUpdate() + int insertionPosition = adapter.GetItemCount(); + adapter.InsertData(insertionPosition, itemData); } /// @@ -528,6 +546,17 @@ namespace Tizen.NUI.Components } adapter.RemoveData(index); + + if(index < dropDownMenuFullList.ChildCount) + { + View childToRemove = dropDownMenuFullList.GetChildAt((uint)index); + if (childToRemove) + { + childToRemove.TouchEvent -= ListItemTouchEvent; + dropDownMenuFullList.Remove(childToRemove); + dropDownMenuFullList?.Layout?.RequestLayout(); + } + } } /// @@ -562,11 +591,11 @@ namespace Tizen.NUI.Components [EditorBrowsable(EditorBrowsableState.Never)] public void AttachScrollBar(ScrollBar scrollBar) { - if (list == null) + if (layoutScroller == null) { return; } - list.AttachScrollBar(scrollBar); + Tizen.Log.Error("DropDown","Feature unsupported"); } /// @@ -577,11 +606,11 @@ namespace Tizen.NUI.Components [EditorBrowsable(EditorBrowsableState.Never)] public void DetachScrollBar() { - if (list == null) + if (layoutScroller == null) { return; } - list.DetachScrollBar(); + Tizen.Log.Error("DropDown","Feature unsupported"); } protected override void RegisterDetectionOfSubstyleChanges() @@ -592,7 +621,7 @@ namespace Tizen.NUI.Components Style.HeaderText.PropertyChanged += HeaderTextAttributesPropertyChanged; Style.Button.PropertyChanged += ButtonAttributesPropertyChanged; - Style.Button.Icon.PropertyChanged += IconStylePropertyChanged; + Style.Button.Icon.PropertyChanged += IconStylePropertyChanged;; } private void IconStylePropertyChanged(object sender, PropertyChangedEventArgs e) @@ -680,13 +709,20 @@ namespace Tizen.NUI.Components if (listBackgroundImage == null) { CreateListBackgroundImage(); - CreateList(); } int temp = (int)Style.FocusedItemIndex; - list.FocusedItemIndex = temp; - list.Size = Style.ListSize; - list.Padding = Style.ListPadding; + + if (layoutScroller==null) // layoutScroller used to test of ListContainer Setup invoked already + { + SetUpListContainer(); + } + + listBackgroundImage.Size = Style.ListSize; + + // Resize and position scrolling list within the drop down list container. Can be used to position list in relation to the background image. + layoutScroller.Size = Style.ListSize - new Size((Style.ListPadding.Start + Style.ListPadding.End), (Style.ListPadding.Top + Style.ListPadding.Bottom), 0 ); + layoutScroller.Position2D = new Position2D(Style.ListPadding.Start,Style.ListPadding.Top); int listBackgroundImageX = 0; int listBackgroundImageY = 0; @@ -703,15 +739,30 @@ namespace Tizen.NUI.Components if (Style.ListMargin != null) { int listWidth = 0; - if (list.Size2D != null) + if (dropDownMenuFullList.Size2D != null) { - listWidth = list.Size2D.Width; + listWidth = dropDownMenuFullList.Size2D.Width; } listBackgroundImageX = Size2D.Width - listWidth - (int)Style.ListMargin.End; listBackgroundImageY = (int)Style.ListMargin.Top; } } listBackgroundImage.Position2D = new Position2D(listBackgroundImageX, listBackgroundImageY); + dropDownMenuFullList?.Layout?.RequestLayout(); + + int numberOfItemsToAdd = adapter.GetItemCount(); + + if (adapter.AdapterPurge == true) + { + adapter.AdapterPurge = false; + for (int i =0; i=0) ) { - UpdateSelectedItem(e.ClickedView.AdapterPosition); - - ItemClickEventArgs args = new ItemClickEventArgs(); - args.Index = e.ClickedView.AdapterPosition; - args.Text = (e.ClickedView.ItemView as DropDownItemView)?.Text; - OnClickEvent(this, args); + return dropDownMenuFullList.GetChildAt(index); } + else + { + return null; + } + } - listBackgroundImage.Hide(); + private void SetListItemToSelected(DropDownItemView targetItemView) + { + // Set the DropDownItemView matching the targetItemView to selected. + if (selectedItemView!=targetItemView) + { + if (selectedItemView!=null) + { + // clear selection status of currently selected item view + selectedItemView.IsSelected = false; + } + // Set target item to selected + targetItemView.IsSelected = true; + selectedItemView = targetItemView; + } } - private void ListItemTouchEvent(object sender, FlexibleView.ItemTouchEventArgs e) + private bool ListItemTouchEvent(object sender, TouchEventArgs e) { PointStateType state = e.Touch.GetState(0); + DropDownItemView touchedView = sender as DropDownItemView;; switch (state) { case PointStateType.Down: - if (e.TouchedView != null) + if (touchedView != null && touchedView.BackgroundColorSelector != null) { - touchedView = e.TouchedView.ItemView as DropDownItemView; - if (touchedView != null && touchedView.BackgroundColorSelector != null) - { - touchedView.BackgroundColor = touchedView.BackgroundColorSelector.GetValue(ControlStates.Pressed); - } + touchedView.BackgroundColor = touchedView.BackgroundColorSelector.GetValue(ControlStates.Pressed); } + itemPressed = true; // if matched with a Up then a click event. break; case PointStateType.Motion: if (touchedView != null && touchedView.BackgroundColorSelector != null) { touchedView.BackgroundColor = touchedView.BackgroundColorSelector.GetValue(ControlStates.Normal); } + itemPressed = false; break; case PointStateType.Up: if (touchedView != null && touchedView.BackgroundColorSelector != null) { touchedView.BackgroundColor = touchedView.BackgroundColorSelector.GetValue(ControlStates.Selected); + + if (itemPressed) // if Down was previously sent without motion (Scrolling) in-between then a clicked event occurred. + { + // List item clicked + Console.WriteLine("Tapped{0}", touchedView.Name); + SetListItemToSelected(touchedView); + button.Text = touchedView.Text; + button.Show(); + listBackgroundImage.Hide(); + } } break; default: break; } - } + return true; + } private void UpdateSelectedItem(int index) { @@ -846,11 +961,9 @@ namespace Tizen.NUI.Components { data.IsSelected = false; } - DropDownItemView view = list?.FindViewHolderForAdapterPosition(selectedItemIndex)?.ItemView as DropDownItemView; - if (view != null) - { - view.IsSelected = false; - } + DropDownItemView listItemView = dropDownMenuFullList.GetChildAt((uint)selectedItemIndex) as DropDownItemView; + data.IsSelected = false; + SetListItemToSelected(listItemView); } if (index != -1) @@ -859,16 +972,16 @@ namespace Tizen.NUI.Components if (data != null) { data.IsSelected = true; - } - DropDownItemView view = list?.FindViewHolderForAdapterPosition(index)?.ItemView as DropDownItemView; - if (view != null) - { - view.IsSelected = true; - button.Style.Text.Text = view.Text; + DropDownItemView listItemView = dropDownMenuFullList?.GetChildAt((uint)index) as DropDownItemView; + if(listItemView) + { + SetListItemToSelected(listItemView); + } } } selectedItemIndex = index; + dropDownMenuFullList?.Layout?.RequestLayout(); } private void CreateListBackgroundImage() @@ -890,7 +1003,10 @@ namespace Tizen.NUI.Components private void ButtonClickEvent(object sender, Button.ClickEventArgs e) { + button.Hide(); listBackgroundImage.Show(); + dropDownMenuFullList?.Layout?.RequestLayout(); + listBackgroundImage.RaiseToTop(); } private void CreateHeaderTextAttributes() @@ -1008,7 +1124,7 @@ namespace Tizen.NUI.Components [EditorBrowsable(EditorBrowsableState.Never)] public DropDownDataItem() { - Initalize(); + Initialize(); } /// @@ -1029,7 +1145,7 @@ namespace Tizen.NUI.Components } itemDataStyle = attributes as DropDownItemStyle; } - Initalize(); + Initialize(); } /// @@ -1042,7 +1158,7 @@ namespace Tizen.NUI.Components public DropDownDataItem(DropDownItemStyle style) { itemDataStyle.CopyFrom(style); - Initalize(); + Initialize(); } /// @@ -1322,7 +1438,7 @@ namespace Tizen.NUI.Components } } - private void Initalize() + private void Initialize() { if (itemDataStyle == null) { @@ -1716,6 +1832,7 @@ namespace Tizen.NUI.Components PositionUsesPivotPoint = true, ParentOrigin = Tizen.NUI.ParentOrigin.TopLeft, PivotPoint = Tizen.NUI.PivotPoint.TopLeft, + Name = "checkedImage", }; Add(mCheck); } @@ -1727,14 +1844,16 @@ namespace Tizen.NUI.Components #region DropDownListBridge /// - /// DropDownListBridge is bridge to contact item data and item view. + /// DropDownListBridge is bridge to connect item data and an item View. /// /// 6 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] - public class DropDownListBridge : FlexibleView.Adapter + public class DropDownListBridge { - private List mDatas = new List(); + private List itemDataList = new List(); + + internal bool AdapterPurge {get;set;} = false; // Set to true if adapter content changed since last iteration. /// /// Creates a new instance of a DropDownListBridge. @@ -1758,10 +1877,10 @@ namespace Tizen.NUI.Components { if(position == -1) { - position = mDatas.Count; + position = itemDataList.Count; } - mDatas.Insert(position, data); - NotifyItemInserted(position); + itemDataList.Insert(position, data); + AdapterPurge = true; } /// @@ -1773,8 +1892,8 @@ namespace Tizen.NUI.Components [EditorBrowsable(EditorBrowsableState.Never)] public void RemoveData(int position) { - mDatas.RemoveAt(position); - NotifyItemRemoved(position); + itemDataList.RemoveAt(position); + AdapterPurge = true; } /// @@ -1786,34 +1905,33 @@ namespace Tizen.NUI.Components [EditorBrowsable(EditorBrowsableState.Never)] public DropDownDataItem GetData(int position) { - return mDatas[position]; + return itemDataList[position]; } /// /// Get view holder by view type. /// - /// Create item view. /// 6 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] - public override FlexibleView.ViewHolder OnCreateViewHolder(int viewType) + public ViewHolder OnCreateViewHolder() { - FlexibleView.ViewHolder viewHolder = new FlexibleView.ViewHolder(new DropDownItemView()); + ViewHolder viewHolder = new ViewHolder(new DropDownItemView()); return viewHolder; } /// - /// Binder view holder, it can be override. + /// Bind ViewHolder with View. /// /// View holder. - /// Position index where will be gotten. + /// Position index of source data. /// 6 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] - public override void OnBindViewHolder(FlexibleView.ViewHolder holder, int position) + public void BindViewHolder(ViewHolder holder, int position) { - DropDownDataItem listItemData = mDatas[position]; + DropDownDataItem listItemData = itemDataList[position]; if(listItemData == null) { return; @@ -1822,7 +1940,23 @@ namespace Tizen.NUI.Components listItemView.Name = "Item" + position; if (listItemData.Size != null) { - holder.ItemView.Size = listItemData.Size; + if (listItemData.Size.Width > 0) + { + holder.ItemView.WidthSpecification = (int)listItemData.Size.Width; + } + else + { + holder.ItemView.WidthSpecification = LayoutParamPolicies.MatchParent; + } + + if (listItemData.Size.Height > 0) + { + holder.ItemView.HeightSpecification = (int)listItemData.Size.Height; + } + else + { + holder.ItemView.HeightSpecification = LayoutParamPolicies.MatchParent; + } } if (listItemView != null) @@ -1857,7 +1991,7 @@ namespace Tizen.NUI.Components } listItemView.IsSelected = listItemData.IsSelected; - } + } } /// @@ -1867,7 +2001,7 @@ namespace Tizen.NUI.Components /// 6 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] - public override void OnDestroyViewHolder(FlexibleView.ViewHolder holder) + public void OnDestroyViewHolder(ViewHolder holder) { if (holder.ItemView != null) { @@ -1881,11 +2015,56 @@ namespace Tizen.NUI.Components /// 6 /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API. [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetItemCount() + public int GetItemCount() + { + return itemDataList.Count; + } + } + #endregion + + #region ViewHolder + + /// + /// A ViewHolder is a class that holds a View created from DropDownListBridge data. + /// + /// 6 + /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API. + [EditorBrowsable(EditorBrowsableState.Never)] + public class ViewHolder + { + /// + /// ViewHolder constructor. + /// + /// View + /// 6 + /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API. + [EditorBrowsable(EditorBrowsableState.Never)] + public ViewHolder(View itemView) + { + if (itemView == null) + { + throw new ArgumentNullException("itemView may not be null"); + } + this.ItemView = itemView; + } + + /// + /// Returns the view. + /// + /// 6 + /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API. + [EditorBrowsable(EditorBrowsableState.Never)] + public View ItemView { get; } + + internal bool IsBound { - return mDatas.Count; - } + get; + set; + } + } + #endregion + } } diff --git a/src/Tizen.NUI.Components/Controls/LayoutScroller.cs b/src/Tizen.NUI.Components/Controls/LayoutScroller.cs new file mode 100755 index 0000000..03b15c9 --- /dev/null +++ b/src/Tizen.NUI.Components/Controls/LayoutScroller.cs @@ -0,0 +1,314 @@ +/* Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +using System; +using Tizen.NUI.BaseComponents; +using System.ComponentModel; +using System.Diagnostics; +namespace Tizen.NUI.Components +{ + /// + /// [Draft] This class provides a View that can scroll a single View with a layout. + /// + internal class LayoutScroller : CustomView + { + static bool LayoutDebugScroller = true; // Debug flag + + private class ScrollerCustomLayout : LayoutGroup + { + protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec) + { + float totalHeight = 0.0f; + float totalWidth = 0.0f; + + MeasuredSize.StateType childWidthState = MeasuredSize.StateType.MeasuredSizeOK; + MeasuredSize.StateType childHeightState = MeasuredSize.StateType.MeasuredSizeOK; + + // measure children + foreach( LayoutItem childLayout in LayoutChildren ) + { + if (childLayout != null) + { + // Get size of child + // Use an Unspecified MeasureSpecification mode so scrolling child is not restricted to it's parents size in Height (for vertical scrolling) + MeasureSpecification heightMeasureSpecUnrestricted = new MeasureSpecification( heightMeasureSpec.Size, MeasureSpecification.ModeType.Unspecified); + MeasureChild( childLayout, widthMeasureSpec, heightMeasureSpecUnrestricted ); + float childWidth = childLayout.MeasuredWidth.Size.AsDecimal(); + float childHeight = childLayout.MeasuredHeight.Size.AsDecimal(); + + // Determine the width and height needed by the children using their given position and size. + // Children could overlap so find the left most and right most child. + Position2D childPosition = childLayout.Owner.Position2D; + float childLeft = childPosition.X; + float childTop = childPosition.Y; + + // Store current width and height needed to contain all children. + Extents padding = Padding; + Extents childMargin = childLayout.Margin; + totalWidth = childWidth + padding.Start + padding.End + childMargin.Start + childMargin.End; + totalHeight = childHeight + padding.Top + padding.Bottom + childMargin.Top + childMargin.Bottom; + + if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall) + { + childWidthState = MeasuredSize.StateType.MeasuredSizeTooSmall; + } + if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall) + { + childHeightState = MeasuredSize.StateType.MeasuredSizeTooSmall; + } + } + } + + + MeasuredSize widthSizeAndState = ResolveSizeAndState(new LayoutLength(totalWidth), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK); + MeasuredSize heightSizeAndState = ResolveSizeAndState(new LayoutLength(totalHeight), heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK); + totalWidth = widthSizeAndState.Size.AsDecimal(); + totalHeight = heightSizeAndState.Size.AsDecimal(); + + // Ensure layout respects it's given minimum size + totalWidth = Math.Max( totalWidth, SuggestedMinimumWidth.AsDecimal() ); + totalHeight = Math.Max( totalHeight, SuggestedMinimumHeight.AsDecimal() ); + + widthSizeAndState.State = childWidthState; + heightSizeAndState.State = childHeightState; + + SetMeasuredDimensions( ResolveSizeAndState( new LayoutLength(totalWidth), widthMeasureSpec, childWidthState ), + ResolveSizeAndState( new LayoutLength(totalHeight), heightMeasureSpec, childHeightState ) ); + } + + protected override void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom) + { + foreach( LayoutItem childLayout in LayoutChildren ) + { + if( childLayout != null ) + { + LayoutLength childWidth = childLayout.MeasuredWidth.Size; + LayoutLength childHeight = childLayout.MeasuredHeight.Size; + + Position2D childPosition = childLayout.Owner.Position2D; + Extents padding = Padding; + Extents childMargin = childLayout.Margin; + + LayoutLength childLeft = new LayoutLength(childPosition.X + childMargin.Start + padding.Start); + LayoutLength childTop = new LayoutLength(childPosition.Y + childMargin.Top + padding.Top); + + childLayout.Layout( childLeft, childTop, childLeft + childWidth, childTop + childHeight ); + } + } + } + } + + private Animation scrollAnimation; + private float maxScrollDistance; + private float childTargetPosition = 0.0f; + private PanGestureDetector mPanGestureDetector; + private TapGestureDetector mTapGestureDetector; + private View mScrollingChild; + + private bool Scrolling = false; + + /// + /// [Draft] Constructor + /// + /// 6 + public LayoutScroller() : base(typeof(VisualView).FullName, CustomViewBehaviour.ViewBehaviourDefault | CustomViewBehaviour.RequiresTouchEventsSupport) + { + mPanGestureDetector = new PanGestureDetector(); + mPanGestureDetector.Attach(this); + mPanGestureDetector.Detected += OnPanGestureDetected; + + mTapGestureDetector = new TapGestureDetector(); + mTapGestureDetector.Attach(this); + mTapGestureDetector.Detected += OnTapGestureDetected; + + ClippingMode = ClippingModeType.ClipToBoundingBox; + + mScrollingChild = new View(); + + Layout = new ScrollerCustomLayout(); + } + + public void AddLayoutToScroll(View child) + { + mScrollingChild = child; + Add(mScrollingChild); + } + + + /// + /// Scroll vertically by displacement pixels in screen coordinates. + /// + /// distance to scroll in pixels. Y increases as scroll position approaches the top. + /// 6 + /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API. + public float ScrollVerticallyBy(float displacement) + { + Debug.WriteLineIf( LayoutDebugScroller, "ScrollVerticallyBy displacement:" + displacement); + return ScrollBy(displacement, false); + } + + internal void StopScroll() + { + if (scrollAnimation != null && scrollAnimation.State == Animation.States.Playing) + { + scrollAnimation.Stop(Animation.EndActions.Cancel); + scrollAnimation.Clear(); + } + } + + // static constructor registers the control type (for user can add kinds of visuals to it) + static LayoutScroller() + { + // ViewRegistry registers control type with DALi type registry + // also uses introspection to find any properties that need to be registered with type registry + CustomViewRegistry.Instance.Register(CreateInstance, typeof(LayoutScroller)); + } + + internal static CustomView CreateInstance() + { + return new LayoutScroller(); + } + + public void OffsetChildVertically(float displacement, bool animate) + { + float previousTargetPosition = childTargetPosition; + + childTargetPosition = childTargetPosition + displacement; + childTargetPosition = Math.Min(0,childTargetPosition); + childTargetPosition = Math.Max(-maxScrollDistance,childTargetPosition); + + Debug.WriteLineIf( LayoutDebugScroller, "OffsetChildVertically currentYPosition:" + mScrollingChild.PositionY + "childTargetPosition:" + childTargetPosition); + + if (animate) + { + if (scrollAnimation == null) + { + scrollAnimation = new Animation(); + scrollAnimation.Finished += ScrollAnimationFinished; + + } + else if (scrollAnimation.State == Animation.States.Playing) + { + scrollAnimation.Stop(Animation.EndActions.StopFinal); + scrollAnimation.Clear(); + } + scrollAnimation.Duration = 1000; + scrollAnimation.DefaultAlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.EaseOutSine); + scrollAnimation.AnimateTo(mScrollingChild, "PositionY", childTargetPosition); + scrollAnimation.Play(); + } + else + { + // Set position of scrolling child without an animation + mScrollingChild.PositionY = childTargetPosition; + } + } + + /// + /// you can override it to clean-up your own resources. + /// + /// DisposeTypes + /// 6 + /// This will be public opened in tizen_5.5 after ACR done. Before ACR, need to be hidden as inhouse API. + [EditorBrowsable(EditorBrowsableState.Never)] + protected override void Dispose(DisposeTypes type) + { + if (disposed) + { + return; + } + + if (type == DisposeTypes.Explicit) + { + StopScroll(); + + if (mPanGestureDetector != null) + { + mPanGestureDetector.Detected -= OnPanGestureDetected; + mPanGestureDetector.Dispose(); + mPanGestureDetector = null; + } + + if (mTapGestureDetector != null) + { + mTapGestureDetector.Detected -= OnTapGestureDetected; + mTapGestureDetector.Dispose(); + mTapGestureDetector = null; + } + } + base.Dispose(type); + } + + private float ScrollBy(float displacement, bool animate) + { + if (GetChildCount() == 0 || displacement == 0) + { + return 0; + } + + int scrollingChildHeight = (int)mScrollingChild.Layout.MeasuredHeight.Size.AsRoundedValue(); + maxScrollDistance = scrollingChildHeight - CurrentSize.Height; + + Debug.WriteLineIf( LayoutDebugScroller, "ScrollBy maxScrollDistance:" + maxScrollDistance + + " parent length:" + CurrentSize.Height + + " scrolling child length:" + mScrollingChild.CurrentSize.Height); + + float absDisplacement = Math.Abs(displacement); + + OffsetChildVertically(displacement, animate); + + return absDisplacement; + } + + private void OnPanGestureDetected(object source, PanGestureDetector.DetectedEventArgs e) + { + if (e.PanGesture.State == Gesture.StateType.Started) + { + if(Scrolling) + { + StopScroll(); + } + } + else if (e.PanGesture.State == Gesture.StateType.Continuing) + { + ScrollVerticallyBy(e.PanGesture.Displacement.Y); + } + else if (e.PanGesture.State == Gesture.StateType.Finished) + { + ScrollVerticallyBy(e.PanGesture.Velocity.Y * 600); + } + } + + private void OnTapGestureDetected(object source, TapGestureDetector.DetectedEventArgs e) + { + if (e.TapGesture.Type == Gesture.GestureType.Tap) + { + // Stop scrolling if touch detected + if(Scrolling) + { + StopScroll(); + } + } + } + + private void ScrollAnimationFinished(object sender, EventArgs e) + { + Scrolling = false; + } + + + } + +} // namespace diff --git a/src/Tizen.NUI/src/internal/Layouting/LayoutController.cs b/src/Tizen.NUI/src/internal/Layouting/LayoutController.cs index 36f0552..a725d23 100755 --- a/src/Tizen.NUI/src/internal/Layouting/LayoutController.cs +++ b/src/Tizen.NUI/src/internal/Layouting/LayoutController.cs @@ -181,6 +181,7 @@ namespace Tizen.NUI { if (rootNode.Layout != null) { + Debug.WriteLineIf( LayoutDebugController, "LayoutController Root found:" + rootNode.Name); // rootNode has a layout, ensure all children have default layouts or layout items. AutomaticallyAssignLayouts(rootNode); // rootNode has a layout, start measuring and layouting from here. -- 2.7.4