4 using System.Collections.Generic;
5 using System.Diagnostics;
6 using SiliconStudio.Core;
7 using SiliconStudio.Core.Mathematics;
9 namespace SiliconStudio.
Paradox.UI.Panels
14 [DebuggerDisplay(
"StackPanel - Name={Name}")]
25 protected static readonly
int[] OrientationToMaximizeIndex1 = { 1, 0, 0 };
29 protected static readonly
int[] OrientationToMaximizeIndex2 = { 2, 2, 1 };
33 protected static readonly List<int[]> ScrollingModeToInfiniteAxis =
new List<int[]>
49 private float scrollPosition;
56 public float ScrollPosition {
get {
return scrollPosition; } }
58 private bool itemVirtualizationEnabled;
64 private readonly List<UIElement> visibleChildren =
new List<UIElement>();
66 private readonly List<UIElement> cachedVisibleChildren =
new List<UIElement>();
70 private readonly List<float> elementBounds =
new List<float>();
75 private int indexElementMaxScrolling;
81 public bool ItemVirtualizationEnabled
83 get {
return itemVirtualizationEnabled; }
86 if(itemVirtualizationEnabled == value)
89 itemVirtualizationEnabled = value;
92 if (!itemVirtualizationEnabled)
95 while (VisualChildrenCollection.Count > 0)
96 SetVisualParent(VisualChildrenCollection[0], null);
99 foreach (var child
in Children)
100 SetVisualParent(child,
this);
103 VisualChildrenCollection.Sort(PanelChildrenSorter);
107 visibleChildren.Clear();
114 private enum ScrollRequestType
121 private struct ScrollRequest
124 public float ScrollValue;
125 public ScrollRequestType Type;
128 private readonly List<ScrollRequest> scrollingRequets =
new List<ScrollRequest>();
135 private float EstimateExtentLength()
142 var indexElement = Children.Count;
143 var accumulatedSize = 0f;
144 while (indexElement > 0 && accumulatedSize < Viewport[scrollAxis])
147 accumulatedSize += GetSafeChildSize(indexElement, scrollAxis);
151 return accumulatedSize / (Children.Count - indexElement) * Children.Count;
156 var element = (UIElement)propertyOwner;
157 element.InvalidateMeasure();
165 get {
return DependencyProperties.Get(OrientationPropertyKey); }
166 set { DependencyProperties.Set(OrientationPropertyKey, value); }
171 base.OnLogicalChildRemoved(oldElement, index);
173 if (index < (
int)Math.Floor(scrollPosition))
179 base.OnLogicalChildAdded(newElement, index);
181 if (index < (
int)Math.Floor(scrollPosition))
187 Viewport = availableSizeWithoutMargins;
190 if(ItemVirtualizationEnabled)
191 AdjustOffsetsAndVisualChildren(scrollPosition);
194 var maximizeIndex1 = OrientationToMaximizeIndex1[(int)
Orientation];
195 var maximizeIndex2 = OrientationToMaximizeIndex2[(int)
Orientation];
198 var childAvailableSizeWithMargins = availableSizeWithoutMargins;
199 childAvailableSizeWithMargins[accumulatorIndex] = float.PositiveInfinity;
202 if (ScrollOwner != null)
204 foreach (var i
in ScrollingModeToInfiniteAxis[(
int)ScrollOwner.ScrollMode])
205 childAvailableSizeWithMargins[i] =
float.PositiveInfinity;
209 var children = ItemVirtualizationEnabled ? visibleChildren : Children.UnderlyingList;
210 foreach (var child
in children)
211 child.Measure(childAvailableSizeWithMargins);
214 var desiredSize = Vector3.Zero;
215 foreach (var child
in children)
217 desiredSize[accumulatorIndex] += child.DesiredSizeWithMargins[accumulatorIndex];
218 desiredSize[maximizeIndex1] = Math.Max(desiredSize[maximizeIndex1], child.DesiredSizeWithMargins[maximizeIndex1]);
219 desiredSize[maximizeIndex2] = Math.Max(desiredSize[maximizeIndex2], child.DesiredSizeWithMargins[maximizeIndex2]);
227 visibleChildren.Clear();
229 Viewport = finalSizeWithoutMargins;
235 if (!ItemVirtualizationEnabled)
240 indexElementMaxScrolling = elementBounds.Count - 2;
241 while (indexElementMaxScrolling > 0 && elementBounds[indexElementMaxScrolling] > elementBounds[elementBounds.Count-1] - Viewport[stackAxis])
242 --indexElementMaxScrolling;
246 extent = finalSizeWithoutMargins;
247 if (ItemVirtualizationEnabled)
248 extent[stackAxis] = EstimateExtentLength();
250 extent[stackAxis] = elementBounds[elementBounds.Count - 1];
253 if (scrollingRequets.Count > 0)
255 foreach (var request
in scrollingRequets)
257 switch (request.Type)
259 case ScrollRequestType.AbsolutePosition:
260 ScrolllToElement(request.ScrollValue);
262 case ScrollRequestType.RelativeElement:
263 ScrolllToNeigbourElement(
Orientation, request.ScrollValue);
265 case ScrollRequestType.RelativePosition:
266 ScrollOf(request.ScrollValue);
269 throw new ArgumentOutOfRangeException();
275 AdjustOffsetsAndVisualChildren(scrollPosition);
277 scrollingRequets.Clear();
280 if (ScrollOwner != null)
281 ScrollOwner.InvalidateAnchorInfo();
283 return finalSizeWithoutMargins;
286 private void ArrangeChildren()
289 elementBounds.Clear();
293 var maximizeIndex1 = OrientationToMaximizeIndex1[(int)
Orientation];
294 var maximizeIndex2 = OrientationToMaximizeIndex2[(int)
Orientation];
297 elementBounds.Add(0);
300 var children = ItemVirtualizationEnabled ? visibleChildren : Children.UnderlyingList;
301 foreach (var child
in children)
303 var startBound = elementBounds[elementBounds.Count - 1];
306 var childOrigin = -Viewport / 2;
307 childOrigin[accumulatorIndex] += startBound;
310 child.DependencyProperties.Set(PanelArrangeMatrixPropertyKey, Matrix.Translation(childOrigin));
313 var childSizeWithMargins = child.DesiredSizeWithMargins;
314 childSizeWithMargins[maximizeIndex1] = Viewport[maximizeIndex1];
315 childSizeWithMargins[maximizeIndex2] = Viewport[maximizeIndex2];
318 child.Arrange(childSizeWithMargins, IsCollapsed);
321 if (child.IsCollapsed)
322 elementBounds.Add(startBound);
324 elementBounds.Add(startBound + child.RenderSize[accumulatorIndex] + child.MarginInternal[accumulatorIndex] + child.MarginInternal[3 + accumulatorIndex]);
333 public Vector3 Extent {
get {
return extent; } }
335 public Vector3 Offset {
get {
return offset; } }
342 return base.GetSurroudingAnchorDistances(direction, position);
346 GetDistanceToSurroundingAnchors((
int)direction, out distances);
351 private void GetDistanceToSurroundingAnchors(
int axisIndex, out
Vector2 distances)
353 var currentElementIndex = (int)Math.Floor(scrollPosition);
354 var currentElementRatio = scrollPosition - currentElementIndex;
355 var currentElementSize = GetSafeChildSize(currentElementIndex, axisIndex);
356 var elementSizeRatio = currentElementRatio * currentElementSize;
358 distances =
new Vector2(-elementSizeRatio, currentElementSize - elementSizeRatio);
369 AdjustOffsetsAndVisualChildren(elementIndex);
374 scrollingRequets.Clear();
375 scrollingRequets.Add(
new ScrollRequest { ScrollValue = elementIndex });
379 private void ScrolllToElement(
Orientation orientation,
float elementIndex)
384 ScrolllToElement(elementIndex);
400 var absOffsetToApply = Math.Abs(offsetToApply);
408 var newElementIndex = (int)Math.Floor(scrollPosition);
409 var currentPositionChildSize = GetSafeChildSize(newElementIndex, axis);
410 var currentOffsetInChild = (scrollPosition - newElementIndex) * currentPositionChildSize;
412 var scrollForward = offsetToApply > 0;
413 var previousElementAccumulatedSize = scrollForward ? -currentOffsetInChild : currentOffsetInChild - currentPositionChildSize;
414 var newElementSize = currentPositionChildSize;
416 while (previousElementAccumulatedSize + newElementSize < absOffsetToApply && (scrollForward ? newElementIndex < Children.Count - 1 : newElementIndex > 0))
418 newElementIndex += Math.Sign(offsetToApply);
419 previousElementAccumulatedSize += newElementSize;
420 newElementSize = GetSafeChildSize(newElementIndex, axis);
423 var offsetToApplyRemainder = absOffsetToApply - previousElementAccumulatedSize;
424 var partialChildSize = scrollForward ? offsetToApplyRemainder : newElementSize - offsetToApplyRemainder;
425 var newScrollPosition = newElementIndex + partialChildSize / newElementSize;
427 AdjustOffsetsAndVisualChildren(newScrollPosition);
433 scrollingRequets.Add(
new ScrollRequest { ScrollValue = offsetToApply, Type = ScrollRequestType.RelativePosition});
437 public Vector3 ScrollBarPositions
441 var positionRatio = Vector3.Zero;
444 if (Children.Count == 0)
445 return positionRatio;
447 var extentMinusViewport = extent[scrollAxis] - Viewport[scrollAxis];
449 return positionRatio;
451 if (ItemVirtualizationEnabled)
454 var indexElement = Children.Count;
455 var accumulatedSize = 0f;
456 while (indexElement > 0 && accumulatedSize < Viewport[scrollAxis])
459 accumulatedSize += GetSafeChildSize(indexElement, scrollAxis);
461 var maxScrollPosition = Math.Max(0, indexElement + (accumulatedSize-Viewport[scrollAxis]) / GetSafeChildSize(indexElement, scrollAxis));
462 positionRatio[scrollAxis] = scrollPosition / maxScrollPosition;
466 var elementIndex = (int)Math.Floor(scrollPosition);
467 var elementRemainder = scrollPosition - elementIndex;
469 if (elementBounds.Count > elementIndex + 1)
471 var previousPosition = elementBounds[elementIndex];
472 var nextPosition = elementBounds[elementIndex + 1];
473 positionRatio[scrollAxis] = Math.Min(1, (previousPosition + elementRemainder * (nextPosition - previousPosition)) / extentMinusViewport);
477 return positionRatio;
501 ScrolllToNeigbourElement(direction, 1);
506 ScrolllToNeigbourElement(direction, -1);
509 private void ScrolllToNeigbourElement(
Orientation direction,
float side)
516 AdjustOffsetsAndVisualChildren((
float)(side > 0? Math.Floor(scrollPosition + 1) : Math.Ceiling(scrollPosition - 1)));
521 scrollingRequets.Add(
new ScrollRequest { ScrollValue = side, Type = ScrollRequestType.RelativeElement });
545 ScrollPages(direction, 1);
550 ScrollPages(direction, -1);
573 ScrolllToElement(direction, 0);
578 ScrolllToElement(direction,
int.MaxValue);
581 private void ScrollPages(
Orientation direction,
float numberOfPages)
586 ScrollOf(numberOfPages * Viewport[(
int)
Orientation]);
592 private void AdjustOffsetsAndVisualChildren(
float desiredNewScrollPosition)
594 offset = Vector3.Zero;
597 if (ItemVirtualizationEnabled)
599 UpdateScrollPosition(desiredNewScrollPosition);
600 UpdateAndArrangeVisibleChildren();
604 if (elementBounds.Count < 2)
607 offset = Vector3.Zero;
611 var viewportSize = Viewport[axis];
612 var inferiorBound = elementBounds[indexElementMaxScrolling];
613 var superiorBound = elementBounds[indexElementMaxScrolling + 1];
614 var boundDifference = superiorBound - inferiorBound;
617 float maxScrollPosition = indexElementMaxScrolling;
619 maxScrollPosition += Math.Min(1 - MathUtil.ZeroTolerance, (extent[axis] - viewportSize - inferiorBound) / boundDifference);
622 scrollPosition = Math.Max(0, Math.Min(maxScrollPosition, desiredNewScrollPosition));
625 var firstElementIndex = (int)Math.Floor(scrollPosition);
626 offset[axis] = -elementBounds[firstElementIndex];
629 visibleChildren.Clear();
630 for (var i = firstElementIndex; i < Children.Count; i++)
632 visibleChildren.Add(Children[i]);
633 if (elementBounds[i+1] - elementBounds[firstElementIndex+1] > viewportSize)
640 var scrollPositionIndex = (int)Math.Floor(scrollPosition);
641 var scrollPositionRemainder = scrollPosition - scrollPositionIndex;
642 offset[axis] -= scrollPositionRemainder * GetSafeChildSize(scrollPositionIndex, axis);
645 if(ScrollOwner != null)
646 ScrollOwner.InvalidateScrollInfo();
649 private void UpdateAndArrangeVisibleChildren()
654 cachedVisibleChildren.Clear();
655 foreach (var child
in visibleChildren)
656 cachedVisibleChildren.Add(child);
659 visibleChildren.Clear();
662 while (VisualChildrenCollection.Count > 0)
663 SetVisualParent(VisualChildrenCollection[0], null);
666 var elementIndex = (int)Math.Floor(scrollPosition);
667 var firstChildSize = GetSafeChildSize(elementIndex, axis);
670 var currentSize = -(scrollPosition - elementIndex) * firstChildSize;
671 while (elementIndex <
Children.Count && currentSize <= Viewport[axis])
673 currentSize += GetSafeChildSize(elementIndex, axis);
675 visibleChildren.Add(
Children[elementIndex]);
676 SetVisualParent(Children[elementIndex],
this);
681 VisualChildrenCollection.Sort(PanelChildrenSorter);
684 if (visibleChildren.Count > 0)
686 var shouldRearrangeChildren = cachedVisibleChildren.Count == 0 || cachedVisibleChildren.Count != visibleChildren.Count;
689 if (!shouldRearrangeChildren)
691 for (
int i = 0; i < visibleChildren.Count; i++)
693 if (cachedVisibleChildren[i] != visibleChildren[i])
695 shouldRearrangeChildren =
true;
700 if(shouldRearrangeChildren)
705 private void UpdateScrollPosition(
float newScrollPosition)
708 var viewportSize = Viewport[axis];
711 var validNextScrollPosition = Math.Max(0, Math.Min(Children.Count - MathUtil.ZeroTolerance, newScrollPosition));
712 var firstElementIndex = (int)Math.Floor(validNextScrollPosition);
713 var startOffset = (validNextScrollPosition - firstElementIndex) * GetSafeChildSize(firstElementIndex, axis);
714 var currentSize = -startOffset;
717 var currentElementIndex = firstElementIndex;
718 while (currentElementIndex <
Children.Count && currentSize < viewportSize)
720 currentSize += GetSafeChildSize(currentElementIndex, axis);
721 ++currentElementIndex;
725 if (currentSize < viewportSize)
727 currentSize += startOffset - GetSafeChildSize(firstElementIndex, axis);
728 while (firstElementIndex >= 0)
730 var elementSize = GetSafeChildSize(firstElementIndex, axis);
731 currentSize += elementSize;
733 if (currentSize >= viewportSize)
739 if (firstElementIndex < 0)
741 validNextScrollPosition = 0;
745 var firstElementSize = GetSafeChildSize(firstElementIndex, axis);
746 validNextScrollPosition = firstElementIndex + (currentSize - viewportSize) / firstElementSize;
751 scrollPosition = validNextScrollPosition;
754 private float GetSafeChildSize(
int childIndex,
int dimension)
761 if (child.IsCollapsed)
764 if (!child.IsMeasureValid)
766 var childProvidedSize = Viewport;
768 if (ScrollOwner != null)
770 foreach (var i
in ScrollingModeToInfiniteAxis[(
int)ScrollOwner.ScrollMode])
771 childProvidedSize[i] = float.PositiveInfinity;
774 child.Measure(childProvidedSize);
777 if (!child.IsArrangeValid)
779 var childProvidedSize = Viewport;
782 child.Arrange(childProvidedSize, Parent != null && Parent.IsCollapsed);
785 return child.RenderSize[dimension] + child.Margin[dimension] + child.Margin[dimension + 3];
788 protected internal override List<UIElement> HitableChildren
790 get {
return visibleChildren; }
void ScrollToNextLine()
Scroll to the next element of the StackPanel.
void ScrollToEnd(Orientation direction)
Go to the end of the element in the given the direction.
Provides a base class for all the User Interface elements in Paradox applications.
SiliconStudio.Paradox.Games.Mathematics.Vector2 Vector2
void ScrollOf(float offsetToApply)
Scroll of the provided offset from the current position in the direction given by the stack panel Ori...
void ScrollToNextLine(Orientation direction)
Go to the next line in the given the direction.
void ScrollOf(Vector3 offsetsToApply)
Increase the amount of offset from the current scrolling position.
Represents a two dimensional mathematical vector.
void ScrollToNextPage()
Scroll to the next page of elements of the StackPanel.
void ScrollToBeginning()
Scroll to the beginning of the StackPanel.
override Vector2 GetSurroudingAnchorDistances(Orientation direction, float position)
Get the distances to the previous and next anchors in the provided direction and from given position...
Orientation
Defines the different orientations that a control or layout can have.
override Vector3 MeasureOverride(Vector3 availableSizeWithoutMargins)
When overridden in a derived class, measures the size in layout required for possible child elements ...
const float ZeroTolerance
The value for which all absolute numbers smaller than are considered equal to zero.
Represents a three dimensional mathematical vector.
void ScrollToBeginning(Orientation direction)
Go to the beginning of the element in the given the direction.
override Vector3 ArrangeOverride(Vector3 finalSizeWithoutMargins)
When overridden in a derived class, positions possible child elements and determines a size for a UIE...
void ScrollToEnd()
Scroll to the end of the StackPanel.
void ScrollToPreviousPage(Orientation direction)
Go to the previous page in the given the direction.
Arranges child elements into a single line that can be oriented horizontally or vertically.
override void OnLogicalChildRemoved(UIElement oldElement, int index)
Action to perform when a logical child is removed.
void ScrollToNextPage(Orientation direction)
Go to the next page in the given the direction.
bool CanScroll(Orientation direction)
Gets a value that indicates if the UIElement can scroll in the provided direction.
override void OnLogicalChildAdded(UIElement newElement, int index)
Action to perform when a logical child is added.
Android.Widget.Orientation Orientation
A class that represents a tag propety.
void ScrollToPreviousPage()
Scroll to the previous page of elements of the StackPanel.
void ScrollToPreviousLine(Orientation direction)
Go to the previous line in the given the direction.
void ScrolllToElement(float elementIndex)
Jump to the element having the provided index.
void ScrollToPreviousLine()
Scroll to the previous element of the StackPanel.
Provides a base class for all Panel elements. Use Panel elements to position and arrange child object...