4 using System.Collections.Generic;
5 using System.Diagnostics;
7 using SiliconStudio.Core;
8 using SiliconStudio.Core.Mathematics;
9 using SiliconStudio.Paradox.Games;
11 namespace SiliconStudio.
Paradox.UI.Controls
18 [DebuggerDisplay(
"ScrollViewer - Name={Name}")]
21 private readonly
static Dictionary<ScrollingMode, int[]> scrollModeToDirectionIndices =
new Dictionary<ScrollingMode,int[]>
23 { ScrollingMode.None,
new int[0] },
24 { ScrollingMode.Horizontal,
new[] { 0 }},
25 { ScrollingMode.Vertical,
new[] { 1 }},
26 { ScrollingMode.InDepth,
new[] { 2 }},
27 { ScrollingMode.HorizontalVertical,
new[] { 0, 1 }},
28 { ScrollingMode.VerticalInDepth,
new[] { 1, 2 }},
29 { ScrollingMode.InDepthHorizontal,
new[] { 2, 0 }},
32 private static readonly HashSet<ScrollingMode>[] orientationToSupportedScrollingModes =
34 new HashSet<ScrollingMode> { ScrollingMode.Horizontal, ScrollingMode.HorizontalVertical, ScrollingMode.InDepthHorizontal},
35 new HashSet<ScrollingMode> { ScrollingMode.HorizontalVertical, ScrollingMode.Vertical, ScrollingMode.VerticalInDepth},
36 new HashSet<ScrollingMode> { ScrollingMode.VerticalInDepth, ScrollingMode.InDepthHorizontal, ScrollingMode.InDepth}
39 private static Color transparent =
new Color(0,0,0,0);
41 private const float ScrollBarHidingSpeed = 1f;
81 protected bool IsUserScrollingViewer {
get;
private set; }
86 public Vector3 ViewPort {
get;
private set; }
92 public bool SnapToAnchors {
get; set; }
97 public float ScrollStartThreshold {
get; set; }
99 private Vector3 lastFrameTranslation;
101 private Vector3 accumulatedTranslation;
103 private readonly
bool[] startedSnapping =
new bool[3];
128 public Vector3 ScrollPosition {
get {
return -ScrollOffsets; } }
130 private static void ValidateDecelarationProperty(ref
float value)
132 if (
float.IsNaN(value))
133 throw new ArgumentException(
"The deceleration must be a valid number. [Deceleration=" + value +
"]");
136 private readonly ScrollBar[] scrollBars =
138 new ScrollBar { Name =
"Left/Right scroll bar"},
139 new ScrollBar { Name =
"Top/Bottom scroll bar"},
140 new ScrollBar { Name =
"Back/Front scroll bar"}
143 private struct ScrollRequest
147 public bool IsRelative;
153 private readonly List<ScrollRequest> scrollingRequests =
new List<ScrollRequest>();
158 foreach (var bar
in scrollBars)
160 bar.Measure(Vector3.Zero);
161 VisualChildrenCollection.Add(bar);
162 SetVisualParent(bar,
this);
165 ScrollStartThreshold = 10;
166 CanBeHitByUser = TouchScrollingEnabled;
175 CurrentScrollingSpeed = Vector3.Zero;
185 return orientationToSupportedScrollingModes[(int)direction].
Contains(ScrollMode);
188 private class ScrollBarSorter : Comparer<UIElement>
207 private static readonly ScrollBarSorter scrollBarSorter =
new ScrollBarSorter();
209 private bool userManuallyScrolled;
211 public override UIElement
Content
219 if (ContentAsScrollInfo != null)
220 ContentAsScrollInfo.ScrollOwner = null;
221 if (ContentAsAnchorInfo != null)
222 ContentAsAnchorInfo.ScrollOwner = null;
224 base.Content = value;
228 StopCurrentScrolling();
229 ScrollOffsets = Vector3.Zero;
233 if (ContentAsScrollInfo != null)
234 ContentAsScrollInfo.ScrollOwner =
this;
238 if (ContentAsAnchorInfo != null)
239 ContentAsAnchorInfo.ScrollOwner =
this;
241 VisualChildrenCollection.Sort(scrollBarSorter);
247 SetScrollBarsColor(ref transparent);
250 private void SetScrollBarsColor(ref
Color color)
252 foreach (var index
in scrollModeToDirectionIndices[ScrollMode])
253 scrollBars[index].BarColor = color;
256 private static void TouchScrollingEnabledInvalidationCallback(
object propertyOwner,
PropertyKey<bool> propertyKey,
bool propertyOldValue)
258 var viewer = (ScrollViewer)propertyOwner;
259 viewer.OnTouchScrollingEnabledChanged();
264 var element = (ScrollViewer)propertyOwner;
265 element.OnScrollModeChanged();
274 ScrollOffsets = Vector3.Zero;
276 if (ContentAsScrollInfo != null)
278 ContentAsScrollInfo.ScrollToBeginning(Orientation.Horizontal);
279 ContentAsScrollInfo.ScrollToBeginning(Orientation.Vertical);
280 ContentAsScrollInfo.ScrollToBeginning(Orientation.InDepth);
292 CanBeHitByUser = TouchScrollingEnabled;
298 protected Vector3 LastFrameTranslation
300 get {
return lastFrameTranslation; }
308 get {
return DependencyProperties.Get(ScrollModePropertyKey); }
309 set { DependencyProperties.Set(ScrollModePropertyKey, value); }
315 public float Deceleration
317 get {
return DependencyProperties.Get(DecelerationPropertyKey); }
318 set { DependencyProperties.Set(DecelerationPropertyKey, value); }
324 public bool TouchScrollingEnabled
326 get {
return DependencyProperties.Get(TouchScrollingEnabledPropertyKey); }
327 set { DependencyProperties.Set(TouchScrollingEnabledPropertyKey, value); }
333 public Color ScrollBarColor
335 get {
return DependencyProperties.Get(ScrollBarColorPropertyKey); }
336 set { DependencyProperties.Set(ScrollBarColorPropertyKey, value); }
342 public float ScrollBarThickness
344 get {
return DependencyProperties.Get(ScrollBarThicknessPropertyKey); }
345 set { DependencyProperties.Set(ScrollBarThicknessPropertyKey, value); }
352 var elapsedSeconds = (float)time.
Elapsed.TotalSeconds;
356 if (IsUserScrollingViewer || userManuallyScrolled)
358 userManuallyScrolled =
false;
359 for (
int i = 0; i < startedSnapping.Length; i++)
360 startedSnapping[i] =
false;
362 if (IsUserScrollingViewer)
363 CurrentScrollingSpeed = LastFrameTranslation / elapsedSeconds;
367 lastFrameTranslation = elapsedSeconds * CurrentScrollingSpeed;
369 if (SnapToAnchors && ContentAsAnchorInfo != null)
371 for (var i = 0; i < 3; ++i)
373 if (!ContentAsAnchorInfo.ShouldAnchor((
Orientation)i))
377 var boundDistances = ContentAsAnchorInfo.GetSurroudingAnchorDistances((
Orientation)i, -ScrollOffsets[i]);
380 var closestAnchorIndex = Math.Abs(boundDistances.X) <= Math.Abs(boundDistances.Y) ? 0 : 1;
383 if (closestAnchorIndex == 1)
385 var offset = ContentAsScrollInfo != null && ContentAsScrollInfo.CanScroll((
Orientation)i) ? -ContentAsScrollInfo.Offset[i] : ScrollOffsets[i];
386 var childRenderSize = VisualContent.RenderSize;
387 var childRenderSizeWithMargins = CalculateSizeWithThickness(ref childRenderSize, ref MarginInternal);
388 var childRenderSizeWithPadding = CalculateSizeWithThickness(ref childRenderSizeWithMargins, ref padding);
389 if (offset - boundDistances[1] < ViewPort[i] - childRenderSizeWithPadding[i])
390 closestAnchorIndex = 0;
394 if (Math.Abs(CurrentScrollingSpeed[i]) < 5 && Math.Abs(boundDistances[closestAnchorIndex]) < 1)
396 startedSnapping[i] =
false;
397 CurrentScrollingSpeed[i] = 0;
398 lastFrameTranslation[i] = boundDistances[closestAnchorIndex];
402 var snappingSpeed = 5 * boundDistances[closestAnchorIndex];
403 if (startedSnapping[i] || Math.Abs(snappingSpeed) > Math.Abs(CurrentScrollingSpeed[i]))
406 CurrentScrollingSpeed[i] = snappingSpeed;
407 lastFrameTranslation[i] = elapsedSeconds * CurrentScrollingSpeed[i];
409 startedSnapping[i] =
true;
416 foreach (var index
in scrollModeToDirectionIndices[ScrollMode])
417 CurrentScrollingSpeed[index] = Math.Sign(CurrentScrollingSpeed[index]) * Math.Max(0, Math.Abs(CurrentScrollingSpeed[index]) - elapsedSeconds * Deceleration);
421 ScrollOfInternal(ref lastFrameTranslation,
false);
424 for (
int dim = 0; dim < 3; dim++)
426 var shouldFadeOutScrollingBar = Math.Abs(CurrentScrollingSpeed[dim]) <
MathUtil.
ZeroTolerance && (!TouchScrollingEnabled || !IsUserScrollingViewer);
427 if (shouldFadeOutScrollingBar)
428 for (
int i = 0; i < 4; i++)
429 scrollBars[dim].BarColorInternal[i] = (byte)Math.Max(0, scrollBars[dim].BarColorInternal[i] - ScrollBarColor[i] * ScrollBarHidingSpeed * elapsedSeconds);
431 scrollBars[dim].BarColor = ScrollBarColor;
434 lastFrameTranslation = Vector3.Zero;
444 ScrollToExtremity(direction, stopScrolling,
true);
454 ScrollToExtremity(direction, stopScrolling,
false);
457 private void ScrollToExtremity(
Orientation direction,
bool stopScrolling,
bool isBeginning)
462 StopCurrentScrolling();
465 userManuallyScrolled =
true;
467 if (!CanScroll(direction))
470 if (ContentAsScrollInfo != null && ContentAsScrollInfo.CanScroll(direction))
473 ContentAsScrollInfo.ScrollToBeginning(direction);
475 ContentAsScrollInfo.ScrollToEnd(direction);
479 var translation = Vector3.Zero;
480 translation[(int)direction] = isBeginning?
float.NegativeInfinity:
float.PositiveInfinity;
482 ScrollOf(translation, stopScrolling);
499 StopCurrentScrolling();
502 userManuallyScrolled =
true;
504 if(VisualContent == null)
508 if (ContentAsScrollInfo != null)
510 var correctedScrollPosition = Vector3.Zero;
511 foreach (var index
in scrollModeToDirectionIndices[ScrollMode])
512 correctedScrollPosition[index] = scrollAbsolutePosition[index];
515 ContentAsScrollInfo.ScrollToBeginning(Orientation.Horizontal);
516 ContentAsScrollInfo.ScrollToBeginning(Orientation.Vertical);
517 ContentAsScrollInfo.ScrollToBeginning(Orientation.InDepth);
518 ContentAsScrollInfo.ScrollOf(correctedScrollPosition);
523 UpdateScrollOffsets(-scrollAbsolutePosition);
525 UpdateVisualContentArrangeMatrix();
530 scrollingRequests.Clear();
531 scrollingRequests.Add(
new ScrollRequest { ScrollValue = scrollAbsolutePosition });
543 userManuallyScrolled =
true;
545 ScrollOfInternal(ref scrollTranslation, stopScrolling);
553 StopCurrentScrolling();
556 if (VisualContent == null)
559 var correctedScrollTranslation = Vector3.Zero;
560 foreach (var index
in scrollModeToDirectionIndices[ScrollMode])
561 correctedScrollTranslation[index] = scrollTranslation[index];
564 if (ContentAsScrollInfo != null)
565 ContentAsScrollInfo.ScrollOf(correctedScrollTranslation);
569 UpdateScrollOffsets(ScrollOffsets - scrollTranslation);
571 UpdateVisualContentArrangeMatrix();
576 scrollingRequests.Add(
new ScrollRequest { IsRelative =
true, ScrollValue = scrollTranslation });
580 private void UpdateScrollOffsets(
Vector3 desiredScrollPosition)
583 var childRenderSize = VisualContent.RenderSize;
584 var childRenderSizeWithMargins = CalculateSizeWithoutThickness(ref childRenderSize, ref MarginInternal);
585 var childRenderSizeWithPadding = CalculateSizeWithoutThickness(ref childRenderSizeWithMargins, ref padding);
588 foreach (var index
in scrollModeToDirectionIndices[ScrollMode])
591 if (ContentAsScrollInfo != null && ContentAsScrollInfo.CanScroll((
Orientation)index))
593 ScrollOffsets[index] = 0;
598 ScrollOffsets[index] = desiredScrollPosition[index];
601 var minimumOffset = ViewPort[index] - childRenderSizeWithPadding[index];
602 if (ScrollOffsets[index] < minimumOffset)
604 ScrollOffsets[index] = minimumOffset;
605 CurrentScrollingSpeed[index] = 0;
609 if (ScrollOffsets[index] > 0)
611 ScrollOffsets[index] = 0;
612 CurrentScrollingSpeed[index] = 0;
617 public override bool IsEnabled
624 base.IsEnabled = value;
631 var childDesiredSizeWithMargins = Vector3.Zero;
632 if (VisualContent != null)
635 var childAvailableSizeWithMargins = CalculateSizeWithoutThickness(ref availableSizeWithoutMargins, ref padding);
638 foreach (var index
in scrollModeToDirectionIndices[ScrollMode])
640 if (ContentAsScrollInfo != null && ContentAsScrollInfo.CanScroll((
Orientation)index))
643 childAvailableSizeWithMargins[index] = float.PositiveInfinity;
646 VisualContent.Measure(childAvailableSizeWithMargins);
647 childDesiredSizeWithMargins = VisualContent.DesiredSizeWithMargins;
651 var desiredSizeWithPadding = CalculateSizeWithThickness(ref childDesiredSizeWithMargins, ref padding);
653 return desiredSizeWithPadding;
659 ViewPort = finalSizeWithoutMargins;
662 if (VisualContent != null)
665 var childSizeWithoutPadding = CalculateSizeWithoutThickness(ref finalSizeWithoutMargins, ref padding);
666 foreach (var index
in scrollModeToDirectionIndices[ScrollMode])
668 if (ContentAsScrollInfo == null || !ContentAsScrollInfo.CanScroll((
Orientation)index))
669 childSizeWithoutPadding[index] = Math.Max(VisualContent.DesiredSizeWithMargins[index], childSizeWithoutPadding[index]);
673 VisualContent.Arrange(childSizeWithoutPadding, IsCollapsed);
676 UpdateScrollingBarsSize();
679 if (scrollingRequests.Count > 0)
682 foreach (var request
in scrollingRequests)
684 var scrollPosition = request.IsRelative? ScrollOffsets - request.ScrollValue: -request.ScrollValue;
685 UpdateScrollOffsets(scrollPosition);
690 UpdateScrollOffsets(ScrollOffsets);
694 UpdateVisualContentArrangeMatrix();
697 scrollingRequests.Clear();
699 return finalSizeWithoutMargins;
702 private void UpdateScrollingBarsSize()
705 foreach (var scrollBar
in scrollBars)
706 scrollBar.Arrange(Vector3.Zero,
false);
709 foreach (var index
in scrollModeToDirectionIndices[ScrollMode])
711 var sizeChildren = (ContentAsScrollInfo != null) ?
712 ContentAsScrollInfo.Extent[index] :
713 VisualContent.RenderSize[index] + VisualContent.MarginInternal[index] + VisualContent.MarginInternal[3 + index];
715 var barLength = Math.
Min(1f, ViewPort[index] / sizeChildren) * ViewPort[index];
717 var barSize = Vector3.Zero;
718 for (var dim = 0; dim < 3; dim++)
719 barSize[dim] = dim == index ? barLength : Math.Min(ScrollBarThickness, ViewPort[dim]);
721 scrollBars[index].Arrange(barSize, IsCollapsed);
725 private void UpdateVisualContentArrangeMatrix()
728 var offsets = ScrollOffsets;
729 for (
int i = 0; i < 3; i++)
731 if (ContentAsScrollInfo != null && ContentAsScrollInfo.CanScroll((
Orientation)i))
732 offsets[i] = ContentAsScrollInfo.Offset[i];
736 var childOffsets = offsets +
new Vector3(Padding.Left, Padding.Top, Padding.Back) - ViewPort / 2;
739 VisualContent.DependencyProperties.Set(ContentArrangeMatrixPropertyKey, Matrix.Translation(childOffsets));
742 ArrangeChanged =
true;
747 var shouldUpdateScrollBars = parentWorldChanged || ArrangeChanged || LocalMatrixChanged;
749 base.UpdateWorldMatrix(ref parentWorldMatrix, parentWorldChanged);
752 if (shouldUpdateScrollBars && VisualContent != null)
754 foreach (var index
in scrollModeToDirectionIndices[ScrollMode])
756 var scrollBar = scrollBars[index];
757 var barPosition = RenderSize / 2 - scrollBar.RenderSize;
758 var childMinusParent = VisualContent.DesiredSizeWithMargins[index] - ViewPort[index];
761 var scrollBarPositionRatio = 0f;
762 if (ContentAsScrollInfo != null && ContentAsScrollInfo.CanScroll((
Orientation)index))
764 scrollBarPositionRatio = -ContentAsScrollInfo.ScrollBarPositions[index];
768 scrollBarPositionRatio = ScrollOffsets[index] / childMinusParent;
772 barPosition[index] = -(RenderSize[index] / 2 + (scrollBarPositionRatio * (RenderSize[index] - scrollBar.RenderSize[index])));
774 var parentMatrix = WorldMatrix;
775 parentMatrix.TranslationVector += barPosition;
784 base.OnPreviewTouchDown(args);
786 StopCurrentScrolling();
787 accumulatedTranslation = Vector3.Zero;
792 base.OnTouchEnter(args);
794 StopCurrentScrolling();
795 accumulatedTranslation = Vector3.Zero;
800 base.OnTouchLeave(args);
802 IsUserScrollingViewer =
false;
803 VisualContent.PreventChildrenFromBeingHit =
false;
808 base.OnPreviewTouchMove(args);
810 if (ScrollMode ==
ScrollingMode.None || !TouchScrollingEnabled)
814 var translation = args.WorldTranslation;
815 foreach (var index
in scrollModeToDirectionIndices[ScrollMode])
816 lastFrameTranslation[index] -= translation[index];
818 accumulatedTranslation += lastFrameTranslation;
819 if (!IsUserScrollingViewer && accumulatedTranslation.Length() > ScrollStartThreshold)
821 IsUserScrollingViewer =
true;
822 lastFrameTranslation = accumulatedTranslation;
823 VisualContent.PreventChildrenFromBeingHit =
true;
826 if (IsUserScrollingViewer)
837 Action = args.Action,
838 ScreenPosition = args.ScreenPosition,
839 ScreenTranslation = args.ScreenTranslation,
840 Timestamp = args.Timestamp
847 child.RaiseTouchLeaveEvent(argsCopy);
848 RaiseLeaveTouchEventTohierarchyChildren(child, args);
855 base.OnPreviewTouchUp(args);
857 if (IsUserScrollingViewer)
860 RaiseLeaveTouchEventTohierarchyChildren(
this, args);
863 IsUserScrollingViewer =
false;
864 VisualContent.PreventChildrenFromBeingHit =
false;
873 if(ContentAsScrollInfo == null)
877 for (
int i = 0; i < 3; i++)
880 CurrentScrollingSpeed[i] = 0f;
883 UpdateScrollingBarsSize();
884 UpdateVisualContentArrangeMatrix();
Provides a base class for all the User Interface elements in Paradox applications.
Orientation
Defines the different orientations that a control or layout can have.
Interface for the update of the UIElements.
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ float size_t y
One bounding volume completely contains another.
const float ZeroTolerance
The value for which all absolute numbers smaller than are considered equal to zero.
Represents a three dimensional mathematical vector.
static void Min(ref Vector3 left, ref Vector3 right, out Vector3 result)
Returns a vector containing the smallest components of the specified vectors.
Represents a control with a single piece of content of any type.
ScrollingMode
The different ways of scrolling in a ScrollViewer.
static readonly Vector3 Zero
A SiliconStudio.Core.Mathematics.Vector3 with all of its components set to zero.
Current timing used for variable-step (real time) or fixed-step (game time) games.
SiliconStudio.Core.Mathematics.Color Color
UIElementCollection VisualChildrenCollection
The visual children of this element.
Represents a 32-bit color (4 bytes) in the form of RGBA (in byte order: R, G, B, A).
Provides data for touch input events.
Only valid for a property / field that has a class or struct type. When restored, instead of recreati...
SiliconStudio.Core.Mathematics.Vector3 Vector3
TimeSpan Elapsed
Gets the elapsed game time since the last update
Android.Widget.Orientation Orientation
A class that represents a tag propety.
Represents a 4x4 mathematical matrix.