*
*/
using System;
+using Tizen.NUI;
using Tizen.NUI.BaseComponents;
using System.Collections.Generic;
using System.ComponentModel;
}
/// <summary>
+ /// ScrollOutofBoundEventArgs is to record scroll out-of-bound event arguments which will be sent to user.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class ScrollOutOfBoundEventArgs : EventArgs
+ {
+ /// <summary>
+ /// The bound to be scrolled out of.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public enum Bound
+ {
+ /// <summary>
+ /// Top bound.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Top,
+
+ /// <summary>
+ /// Bottom bound.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Bottom
+ }
+
+ /// <summary>
+ /// Default constructor.
+ /// </summary>
+ /// <param name="bound">Current scrollable bound</param>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public ScrollOutOfBoundEventArgs(Bound bound)
+ {
+ ScrollableBound = bound;
+ }
+
+ /// <summary>
+ /// Current position of ContentContainer.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Bound ScrollableBound
+ {
+ get;
+ }
+ }
+
+ /// <summary>
/// This class provides a View that can scroll a single View with a layout. This View can be a nest of Views.
/// </summary>
/// <since_tizen> 8 </since_tizen>
{
protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
{
- Extents padding = Padding;
- float totalHeight = padding.Top + padding.Bottom;
- float totalWidth = padding.Start + padding.End;
-
MeasuredSize.StateType childWidthState = MeasuredSize.StateType.MeasuredSizeOK;
MeasuredSize.StateType childHeightState = MeasuredSize.StateType.MeasuredSizeOK;
scrollingDirection = scrollableBase.ScrollingDirection;
}
+ float totalWidth = 0.0f;
+ float totalHeight = 0.0f;
+
// measure child, should be a single scrolling child
foreach (LayoutItem childLayout in LayoutChildren)
{
- if (childLayout != null)
+ if (childLayout != null && childLayout.Owner.Name == "ContentContainer")
{
// 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)
// or Width for horizontal scrolling
- MeasureSpecification unrestrictedMeasureSpec = new MeasureSpecification(heightMeasureSpec.Size, MeasureSpecification.ModeType.Unspecified);
-
if (scrollingDirection == Direction.Vertical)
{
- MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), unrestrictedMeasureSpec, new LayoutLength(0)); // Height unrestricted by parent
+ MeasureSpecification unrestrictedMeasureSpec = new MeasureSpecification(heightMeasureSpec.Size, MeasureSpecification.ModeType.Unspecified);
+ MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), unrestrictedMeasureSpec, new LayoutLength(0)); // Height unrestricted by parent
}
else
{
- MeasureChildWithMargins(childLayout, unrestrictedMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0)); // Width unrestricted by parent
+ MeasureSpecification unrestrictedMeasureSpec = new MeasureSpecification(widthMeasureSpec.Size, MeasureSpecification.ModeType.Unspecified);
+ MeasureChildWithMargins(childLayout, unrestrictedMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0)); // Width unrestricted by parent
}
- 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 childMargin = childLayout.Margin;
- totalWidth = childWidth + childMargin.Start + childMargin.End;
- totalHeight = childHeight + childMargin.Top + childMargin.Bottom;
+ totalWidth = childLayout.MeasuredWidth.Size.AsDecimal();
+ totalHeight = childLayout.MeasuredHeight.Size.AsDecimal();
if (childLayout.MeasuredWidth.State == MeasuredSize.StateType.MeasuredSizeTooSmall)
{
}
}
-
- MeasuredSize widthSizeAndState = ResolveSizeAndState(new LayoutLength(totalWidth + Padding.Start + Padding.End), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
- MeasuredSize heightSizeAndState = ResolveSizeAndState(new LayoutLength(totalHeight + Padding.Top + Padding.Bottom), heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK);
+ 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();
widthSizeAndState.State = childWidthState;
heightSizeAndState.State = childHeightState;
- SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(totalWidth + Padding.Start + Padding.End), widthMeasureSpec, childWidthState),
- ResolveSizeAndState(new LayoutLength(totalHeight + Padding.Top + Padding.Bottom), heightMeasureSpec, childHeightState));
+ SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(totalWidth), widthMeasureSpec, childWidthState),
+ ResolveSizeAndState(new LayoutLength(totalHeight), heightMeasureSpec, childHeightState));
// Size of ScrollableBase is changed. Change Page width too.
scrollableBase.mPageWidth = (int)MeasuredWidth.Size.AsRoundedValue();
- scrollableBase.OnScrollingChildRelayout(null , null);
+ scrollableBase.OnScrollingChildRelayout(null, null);
}
protected override void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
{
foreach (LayoutItem childLayout in LayoutChildren)
{
- if (childLayout != null)
+ if (childLayout != null && childLayout.Owner.Name == "ContentContainer")
{
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);
+ LayoutLength childLeft = new LayoutLength(childPosition.X);
+ LayoutLength childTop = new LayoutLength(childPosition.Y);
childLayout.Layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
}
if (value != mScrollingDirection)
{
mScrollingDirection = value;
- mPanGestureDetector.RemoveDirection(value == Direction.Horizontal ?
- PanGestureDetector.DirectionVertical : PanGestureDetector.DirectionHorizontal);
+ mPanGestureDetector.ClearAngles();
mPanGestureDetector.AddDirection(value == Direction.Horizontal ?
PanGestureDetector.DirectionHorizontal : PanGestureDetector.DirectionVertical);
- ContentContainer.WidthSpecification = mScrollingDirection == Direction.Vertical ?
- LayoutParamPolicies.MatchParent : LayoutParamPolicies.WrapContent;
- ContentContainer.HeightSpecification = mScrollingDirection == Direction.Vertical ?
- LayoutParamPolicies.WrapContent : LayoutParamPolicies.MatchParent;
+ ContentContainer.WidthSpecification = LayoutParamPolicies.WrapContent;
+ ContentContainer.HeightSpecification = LayoutParamPolicies.WrapContent;
}
}
}
/// <since_tizen> 8 </since_tizen>
public event EventHandler<ScrollEventArgs> ScrollDragEnded;
-
/// <summary>
/// An event emitted when the scrolling slide animation starts, user can subscribe or unsubscribe to this event handler.<br />
/// </summary>
/// <since_tizen> 8 </since_tizen>
public event EventHandler<ScrollEventArgs> ScrollAnimationEnded;
-
/// <summary>
/// An event emitted when scrolling, user can subscribe or unsubscribe to this event handler.<br />
/// </summary>
/// <since_tizen> 8 </since_tizen>
public event EventHandler<ScrollEventArgs> Scrolling;
+ /// <summary>
+ /// An event emitted when scrolling out of bound, user can subscribe or unsubscribe to this event handler.<br />
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public event EventHandler<ScrollOutOfBoundEventArgs> ScrollOutOfBound;
/// <summary>
/// Scrollbar for ScrollableBase.
ContentContainer.Layout = value;
if (ContentContainer.Layout != null)
{
- ContentContainer.Layout.SetPositionByLayout = false;
+ ContentContainer.Layout.SetPositionByLayout = true;
}
}
}
}
}
-
/// <summary>
/// Page will be changed when velocity of panning is over threshold.
/// The unit of threshold is pixel per milisec.
}
/// <summary>
+ /// Padding for the ScrollableBase
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Extents Padding
+ {
+ get
+ {
+ return ContentContainer.Padding;
+ }
+ set
+ {
+ ContentContainer.Padding = value;
+ }
+ }
+
+ /// <summary>
/// Alphafunction for scroll animation.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
// Let's consider more whether this needs to be set as protected.
- public float NoticeAnimationEndBeforePosition { get => noticeAnimationEndBeforePosition; set => noticeAnimationEndBeforePosition = value; }
+ public float NoticeAnimationEndBeforePosition
+ {
+ get => noticeAnimationEndBeforePosition;
+ set => noticeAnimationEndBeforePosition = value;
+ }
// Let's consider more whether this needs to be set as protected.
private float finalTargetPosition;
private float logValueOfDeceleration = 0.0f;
private float decelerationRate = 0.0f;
- private View mVerticalTopShadowView;
- private View mVerticalBottomShadowView;
- private const int mVerticalShadowScaleHeightLimit = 64 * 3;
- private const int mVerticalShadowAnimationDuration = 300;
- private Animation mVerticalShadowAnimation;
+ private View verticalTopShadowView;
+ private View verticalBottomShadowView;
+ private const int verticalShadowScaleHeightLimit = 64 * 3;
+ private const int verticalShadowAnimationDuration = 300;
+ private Animation verticalShadowAnimation;
private bool isVerticalShadowShown = false;
- private float mStartShowShadowDisplacement;
+ private float startShowShadowDisplacement;
/// <summary>
/// Default Constructor
//Default Scrolling child
ContentContainer = new View()
{
- WidthSpecification = ScrollingDirection == Direction.Vertical ? LayoutParamPolicies.MatchParent : LayoutParamPolicies.WrapContent,
- HeightSpecification = ScrollingDirection == Direction.Vertical ? LayoutParamPolicies.WrapContent : LayoutParamPolicies.MatchParent,
- Layout = new AbsoluteLayout() { SetPositionByLayout = false },
+ Name = "ContentContainer",
+ WidthSpecification = LayoutParamPolicies.WrapContent,
+ HeightSpecification = LayoutParamPolicies.WrapContent,
+ Layout = new LinearLayout()
+ {
+ SetPositionByLayout = true
+ },
};
ContentContainer.Relayout += OnScrollingChildRelayout;
propertyNotification = ContentContainer.AddPropertyNotification("position", PropertyCondition.Step(mScrollingEventThreshold));
mInterruptTouchingChild.TouchEvent += OnIterruptTouchingChildTouched;
Scrollbar = new Scrollbar();
- //Show vertical shadow when panning down (or up) on the scroll top (or end).
- mVerticalTopShadowView = new View
+ //Show vertical shadow on the top (or bottom) of the scrollable when panning down (or up).
+ verticalTopShadowView = new View
{
- BackgroundImage = StyleManager.GetFrameworkResourcePath("nui_component_default_scroll_over_shooting_top.png"),
+ BackgroundImage = Tizen.NUI.StyleManager.FrameworkResourcePath + "nui_component_default_scroll_over_shooting_top.png",
Opacity = 1.0f,
SizeHeight = 0.0f,
PositionUsesPivotPoint = true,
ParentOrigin = NUI.ParentOrigin.TopCenter,
PivotPoint = NUI.PivotPoint.TopCenter,
};
- mVerticalBottomShadowView = new View
+ verticalBottomShadowView = new View
{
- BackgroundImage = StyleManager.GetFrameworkResourcePath("nui_component_default_scroll_over_shooting_bottom.png"),
+ BackgroundImage = Tizen.NUI.StyleManager.FrameworkResourcePath + "nui_component_default_scroll_over_shooting_bottom.png",
Opacity = 1.0f,
SizeHeight = 0.0f,
PositionUsesPivotPoint = true,
private void OnScrollingChildRelayout(object source, EventArgs args)
{
// Size is changed. Calculate maxScrollDistance.
- bool isSizeChanged = previousContainerSize.Width != ContentContainer.Size.Width || previousContainerSize.Height != ContentContainer.Size.Height
- || previousSize.Width != Size.Width || previousSize.Height != Size.Height;
+ bool isSizeChanged = previousContainerSize.Width != ContentContainer.Size.Width || previousContainerSize.Height != ContentContainer.Size.Height ||
+ previousSize.Width != Size.Width || previousSize.Height != Size.Height;
if (isSizeChanged)
{
float childCurrentPosition = (ScrollingDirection == Direction.Horizontal) ? ContentContainer.PositionX : ContentContainer.PositionY;
Debug.WriteLineIf(LayoutDebugScrollableBase, "ScrollBy childCurrentPosition:" + childCurrentPosition +
- " displacement:" + displacement,
- " maxScrollDistance:" + maxScrollDistance);
+ " displacement:" + displacement,
+ " maxScrollDistance:" + maxScrollDistance);
childTargetPosition = childCurrentPosition + displacement; // child current position + gesture displacement
-
Debug.WriteLineIf(LayoutDebugScrollableBase, "ScrollBy currentAxisPosition:" + childCurrentPosition + "childTargetPosition:" + childTargetPosition);
if (animate)
}
Debug.WriteLineIf(LayoutDebugScrollableBase, "ScrollBy maxScrollDistance:" + (scrollingChildLength - scrollerLength) +
- " parent length:" + scrollerLength +
- " scrolling child length:" + scrollingChildLength);
+ " parent length:" + scrollerLength +
+ " scrolling child length:" + scrollingChildLength);
return Math.Max(scrollingChildLength - scrollerLength, 0);
}
private void PageSnap(float velocity)
{
Debug.WriteLineIf(LayoutDebugScrollableBase, "PageSnap with pan candidate totalDisplacement:" + totalDisplacementForPan +
- " currentPage[" + CurrentPage + "]");
+ " currentPage[" + CurrentPage + "]");
//Increment current page if total displacement enough to warrant a page change.
if (Math.Abs(totalDisplacementForPan) > (mPageWidth * ratioOfScreenWidthToCompleteScroll))
// stop animation if necessary.
StopVerticalShadowAnimation();
- base.Add(mVerticalTopShadowView);
- base.Add(mVerticalBottomShadowView);
+ base.Add(verticalTopShadowView);
+ base.Add(verticalBottomShadowView);
- mVerticalTopShadowView.Size = new Size(SizeWidth, 0.0f);
- mVerticalTopShadowView.Opacity = 1.0f;
+ verticalTopShadowView.Size = new Size(SizeWidth, 0.0f);
+ verticalTopShadowView.Opacity = 1.0f;
- mVerticalBottomShadowView.Size = new Size(SizeWidth, 0.0f);
- mVerticalBottomShadowView.Opacity = 1.0f;
+ verticalBottomShadowView.Size = new Size(SizeWidth, 0.0f);
+ verticalBottomShadowView.Opacity = 1.0f;
// at the beginning, height of vertical shadow is 0, so it is invisible.
isVerticalShadowShown = false;
// save start displacement, and re-calculate displacement.
if (!isVerticalShadowShown)
{
- mStartShowShadowDisplacement = displacement;
+ startShowShadowDisplacement = displacement;
+ OnScrollOutOfBound(ScrollOutOfBoundEventArgs.Bound.Top);
}
isVerticalShadowShown = true;
- float newDisplacement = displacement < mStartShowShadowDisplacement ? 0 : displacement - mStartShowShadowDisplacement;
+ float newDisplacement = (int)displacement < (int)startShowShadowDisplacement ? 0 : displacement - startShowShadowDisplacement;
// scale limit of width is 60%.
- float widthScale = newDisplacement / mVerticalShadowScaleHeightLimit;
- mVerticalTopShadowView.SizeWidth = widthScale > 0.6f ? SizeWidth * 0.4f : SizeWidth * (1.0f - widthScale);
+ float widthScale = newDisplacement / verticalShadowScaleHeightLimit;
+ verticalTopShadowView.SizeWidth = widthScale > 0.6f ? SizeWidth * 0.4f : SizeWidth * (1.0f - widthScale);
// scale limit of height is 300%.
- mVerticalTopShadowView.SizeHeight = newDisplacement > mVerticalShadowScaleHeightLimit ? mVerticalShadowScaleHeightLimit : newDisplacement;
+ verticalTopShadowView.SizeHeight = newDisplacement > verticalShadowScaleHeightLimit ? verticalShadowScaleHeightLimit : newDisplacement;
}
else if ((int)displacement < 0) // upwards
{
// save start displacement, and re-calculate displacement.
if (!isVerticalShadowShown)
{
- mStartShowShadowDisplacement = displacement;
+ startShowShadowDisplacement = displacement;
+ OnScrollOutOfBound(ScrollOutOfBoundEventArgs.Bound.Bottom);
}
isVerticalShadowShown = true;
- float newDisplacement = mStartShowShadowDisplacement < displacement ? 0 : mStartShowShadowDisplacement - displacement;
+ float newDisplacement = (int)startShowShadowDisplacement < (int)displacement ? 0 : startShowShadowDisplacement - displacement;
// scale limit of width is 60%.
- float widthScale = newDisplacement / mVerticalShadowScaleHeightLimit;
- mVerticalBottomShadowView.SizeWidth = widthScale > 0.6f ? SizeWidth * 0.4f : SizeWidth * (1.0f - widthScale);
+ float widthScale = newDisplacement / verticalShadowScaleHeightLimit;
+ verticalBottomShadowView.SizeWidth = widthScale > 0.6f ? SizeWidth * 0.4f : SizeWidth * (1.0f - widthScale);
// scale limit of height is 300%.
- mVerticalBottomShadowView.SizeHeight = newDisplacement > mVerticalShadowScaleHeightLimit ? mVerticalShadowScaleHeightLimit : newDisplacement;
+ verticalBottomShadowView.SizeHeight = newDisplacement > verticalShadowScaleHeightLimit ? verticalShadowScaleHeightLimit : newDisplacement;
}
else
{
// stop animation if necessary.
StopVerticalShadowAnimation();
- if (mVerticalShadowAnimation == null)
+ if (verticalShadowAnimation == null)
{
- mVerticalShadowAnimation = new Animation(mVerticalShadowAnimationDuration);
- mVerticalShadowAnimation.Finished += OnVerticalShadowAnimationFinished;
+ verticalShadowAnimation = new Animation(verticalShadowAnimationDuration);
+ verticalShadowAnimation.Finished += OnVerticalShadowAnimationFinished;
}
- View targetView = totalDisplacementForPan < 0 ? mVerticalBottomShadowView : mVerticalTopShadowView;
- mVerticalShadowAnimation.AnimateTo(targetView, "SizeWidth", SizeWidth);
- mVerticalShadowAnimation.AnimateTo(targetView, "SizeHeight", 0.0f);
- mVerticalShadowAnimation.AnimateTo(targetView, "Opacity", 0.0f);
- mVerticalShadowAnimation.Play();
+ View targetView = totalDisplacementForPan < 0 ? verticalBottomShadowView : verticalTopShadowView;
+ verticalShadowAnimation.AnimateTo(targetView, "SizeWidth", SizeWidth);
+ verticalShadowAnimation.AnimateTo(targetView, "SizeHeight", 0.0f);
+ verticalShadowAnimation.AnimateTo(targetView, "Opacity", 0.0f);
+ verticalShadowAnimation.Play();
}
private void StopVerticalShadowAnimation()
{
- if (mVerticalShadowAnimation == null || mVerticalShadowAnimation.State != Animation.States.Playing)
+ if (verticalShadowAnimation == null || verticalShadowAnimation.State != Animation.States.Playing)
return;
- Debug.WriteLineIf(LayoutDebugScrollableBase, "gesture finished. Stop Vertical Shadow Animation Playing.");
- mVerticalShadowAnimation.Stop(Animation.EndActions.Cancel);
+ verticalShadowAnimation.Stop(Animation.EndActions.Cancel);
OnVerticalShadowAnimationFinished(null, null);
- mVerticalShadowAnimation.Clear();
+ verticalShadowAnimation.Clear();
}
private void OnVerticalShadowAnimationFinished(object sender, EventArgs e)
{
- base.Remove(mVerticalTopShadowView);
- base.Remove(mVerticalBottomShadowView);
+ base.Remove(verticalTopShadowView);
+ base.Remove(verticalBottomShadowView);
- mVerticalTopShadowView.Size = new Size(SizeWidth, 0.0f);
- mVerticalBottomShadowView.Size = new Size(SizeWidth, 0.0f);
+ verticalTopShadowView.Size = new Size(SizeWidth, 0.0f);
+ verticalBottomShadowView.Size = new Size(SizeWidth, 0.0f);
// after animation finished, height & opacity of vertical shadow both are 0, so it is invisible.
isVerticalShadowShown = false;
}
+ private void OnScrollOutOfBound(ScrollOutOfBoundEventArgs.Bound bound)
+ {
+ ScrollOutOfBoundEventArgs args = new ScrollOutOfBoundEventArgs(bound);
+ ScrollOutOfBound?.Invoke(this, args);
+ }
+
private void OnPanGestureDetected(object source, PanGestureDetector.DetectedEventArgs e)
{
OnPanGesture(e.PanGesture);