private void ScrollToExtremity(Orientation direction, bool stopScrolling, bool isBeginning) { if (stopScrolling) { HideScrollBars(); StopCurrentScrolling(); } userManuallyScrolled = true; if (!CanScroll(direction)) { return; } if (ContentAsScrollInfo != null && ContentAsScrollInfo.CanScroll(direction)) // scrolling is delegated to Content { if (isBeginning) { ContentAsScrollInfo.ScrollToBeginning(direction); } else { ContentAsScrollInfo.ScrollToEnd(direction); } } else // scrolling should be performed by the scroll viewer { var translation = Vector3.Zero; translation[(int)direction] = isBeginning ? float.NegativeInfinity : float.PositiveInfinity; ScrollOf(translation, stopScrolling); } }
protected override Vector3 MeasureOverride(Vector3 availableSizeWithoutMargins) { // measure size desired by the children var childDesiredSizeWithMargins = Vector3.Zero; if (VisualContent != null) { // remove space for padding in availableSizeWithoutMargins var childAvailableSizeWithMargins = CalculateSizeWithoutThickness(ref availableSizeWithoutMargins, ref padding); // if the content is not scrollable perform space virtualization from the scroll viewer side. foreach (var index in ScrollModeToDirectionIndices[ScrollMode]) { if (ContentAsScrollInfo != null && ContentAsScrollInfo.CanScroll((Orientation)index)) { continue; } childAvailableSizeWithMargins[index] = float.PositiveInfinity; } VisualContent.Measure(childAvailableSizeWithMargins); childDesiredSizeWithMargins = VisualContent.DesiredSizeWithMargins; } // add the padding to the child desired size var desiredSizeWithPadding = CalculateSizeWithThickness(ref childDesiredSizeWithMargins, ref padding); return(desiredSizeWithPadding); }
private void UpdateVisualContentArrangeMatrix() { // calculate the offsets to move the element of var offsets = ScrollOffsets; for (var i = 0; i < 3; i++) { if (ContentAsScrollInfo != null && ContentAsScrollInfo.CanScroll((Orientation)i)) { offsets[i] = ContentAsScrollInfo.Offset[i]; } } // compute the rendering offsets of the child element wrt the parent origin (0,0,0) var childOffsets = offsets + new Vector3(Padding.Left, Padding.Top, Padding.Front) - ViewPort / 2; // set the arrange matrix of the child. VisualContent.DependencyProperties.Set(ContentArrangeMatrixPropertyKey, Matrix.Translation(childOffsets)); // update Hidden status for all VisualChildren foreach (UIElement child in VisualContent.VisualChildren) { Vector3 topLeft = child.RenderOffsets + offsets; Vector3 bottomRight = topLeft + child.RenderSize; child.Hidden = !(topLeft.Y <RenderSize.Y && bottomRight.Y> 0f && topLeft.X <RenderSize.X && bottomRight.X> 0f); } // force re-calculation of main element and scroll bars world matrices ArrangeChanged = true; }
/// <summary> /// Method triggered when <see cref="ScrollMode"/> changed. /// Can be overridden in inherited class to change the default behavior. /// </summary> protected virtual void OnScrollModeChanged() { ScrollOffsets = Vector3.Zero; if (ContentAsScrollInfo != null) { ContentAsScrollInfo.ScrollToBeginning(Orientation.Horizontal); ContentAsScrollInfo.ScrollToBeginning(Orientation.Vertical); ContentAsScrollInfo.ScrollToBeginning(Orientation.InDepth); } InvalidateMeasure(); }
protected override Vector3 ArrangeOverride(Vector3 finalSizeWithoutMargins) { // calculate the remaining space for the child after having removed the padding space. ViewPort = finalSizeWithoutMargins; // arrange the content if (VisualContent != null) { // calculate the final size given to the child (scroll view virtual size) var childSizeWithoutPadding = CalculateSizeWithoutThickness(ref finalSizeWithoutMargins, ref padding); foreach (var index in ScrollModeToDirectionIndices[ScrollMode]) { if (ContentAsScrollInfo == null || !ContentAsScrollInfo.CanScroll((Orientation)index)) { childSizeWithoutPadding[index] = Math.Max(VisualContent.DesiredSizeWithMargins[index], childSizeWithoutPadding[index]); } } // arrange the child VisualContent.Arrange(childSizeWithoutPadding, IsCollapsed); // update the scrolling bars UpdateScrollingBarsSize(); // update the scrolling if (scrollingRequests.Count > 0) { // perform the scrolling requests foreach (var request in scrollingRequests) { var scrollPosition = request.IsRelative ? ScrollOffsets - request.ScrollValue : -request.ScrollValue; UpdateScrollOffsets(scrollPosition); } } else { UpdateScrollOffsets(ScrollOffsets); // insures that scrolling is not out of bounds } // update the position of the child UpdateVisualContentArrangeMatrix(); } scrollingRequests.Clear(); return(finalSizeWithoutMargins); }
/// <summary> /// Try to scroll to the provided position (in virtual pixels). /// If the provided translation is too important, it is clamped. /// </summary> /// <remarks>Note that the computational cost of <see cref="ScrollTo"/> can be greatly higher than <see cref="ScrollOf"/> /// when scrolling is delegated to a <see cref="Content"/> virtualizing its items. When possible, prefer call to <see cref="ScrollOf"/></remarks> /// <param name="scrollAbsolutePosition">The scroll offsets to apply</param> /// <param name="stopScrolling">Indicate if the scrolling should be stopped after the scroll action.</param> public void ScrollTo(Vector3 scrollAbsolutePosition, bool stopScrolling = true) { if (stopScrolling) { HideScrollBars(); StopCurrentScrolling(); } userManuallyScrolled = true; if (VisualContent == null) { return; } // ask the content to internally scroll if (ContentAsScrollInfo != null) { var correctedScrollPosition = Vector3.Zero; foreach (var index in ScrollModeToDirectionIndices[ScrollMode]) { correctedScrollPosition[index] = scrollAbsolutePosition[index]; } // reset content scroll position to the beginning of the document and then scroll of the absolute position ContentAsScrollInfo.ScrollToBeginning(Orientation.Horizontal); ContentAsScrollInfo.ScrollToBeginning(Orientation.Vertical); ContentAsScrollInfo.ScrollToBeginning(Orientation.InDepth); ContentAsScrollInfo.ScrollOf(correctedScrollPosition); } if (IsArrangeValid) // the children size informations are still valid -> perform scrolling right away { UpdateScrollOffsets(-scrollAbsolutePosition); UpdateVisualContentArrangeMatrix(); } else // children may have changed of size -> delay scrolling to next draw call { InvalidateArrange(); // force next arrange to perform scrolls scrollingRequests.Clear(); // optimization remove previous request when provided position is absolute scrollingRequests.Add(new ScrollRequest { ScrollValue = scrollAbsolutePosition }); } }
public void ScrollOfInternal(ref Vector3 scrollTranslation, bool stopScrolling) { if (stopScrolling) { HideScrollBars(); StopCurrentScrolling(); } if (VisualContent == null) { return; } var correctedScrollTranslation = Vector3.Zero; foreach (var index in scrollModeToDirectionIndices[ScrollMode]) { correctedScrollTranslation[index] = scrollTranslation[index]; } // ask the content to internally scroll if (ContentAsScrollInfo != null) { ContentAsScrollInfo.ScrollOf(correctedScrollTranslation); } if (IsArrangeValid) // the children size informations are still valid -> perform scrolling right away { UpdateScrollOffsets(ScrollOffsets - scrollTranslation); UpdateVisualContentArrangeMatrix(); } else // children may have changed of size -> delay scrolling to next draw call { InvalidateArrange(); // force next arrange to perform scrolls scrollingRequests.Add(new ScrollRequest { IsRelative = true, ScrollValue = scrollTranslation }); } }
private void UpdateVisualContentArrangeMatrix() { // calculate the offsets to move the element of var offsets = ScrollOffsets; for (var i = 0; i < 3; i++) { if (ContentAsScrollInfo != null && ContentAsScrollInfo.CanScroll((Orientation)i)) { offsets[i] = ContentAsScrollInfo.Offset[i]; } } // compute the rendering offsets of the child element wrt the parent origin (0,0,0) var childOffsets = offsets + new Vector3(Padding.Left, Padding.Top, Padding.Front) - ViewPort / 2; // set the arrange matrix of the child. VisualContent.DependencyProperties.Set(ContentArrangeMatrixPropertyKey, Matrix.Translation(childOffsets)); // force re-calculation of main element and scroll bars world matrices ArrangeChanged = true; }
private void UpdateScrollOffsets(Vector3 desiredScrollPosition) { // the space desired by the child + the padding of the viewer Vector3 childRenderSize = VisualContent.RenderSize; childRenderSize.X += padding.Left + padding.Right; childRenderSize.Y += padding.Top + padding.Bottom; childRenderSize.Z += padding.Front + padding.Back; // update scroll viewer scroll offsets foreach (var index in ScrollModeToDirectionIndices[ScrollMode]) { // reset scroll offsets if scrolling is delegated to content if (ContentAsScrollInfo != null && ContentAsScrollInfo.CanScroll((Orientation)index)) { ScrollOffsets[index] = 0; continue; } // update the scroll offset ScrollOffsets[index] = desiredScrollPosition[index]; // no blank on the other extremity (reached the offset "right" limit) var minimumOffset = ViewPort[index] - childRenderSize[index]; if (ScrollOffsets[index] < minimumOffset) { ScrollOffsets[index] = minimumOffset; CurrentScrollingSpeed[index] = 0; } // no positive offsets (reached the offset "left" limit) if (ScrollOffsets[index] > 0) { ScrollOffsets[index] = 0; CurrentScrollingSpeed[index] = 0; } } }
protected override void UpdateWorldMatrix(ref Matrix parentWorldMatrix, bool parentWorldChanged) { var shouldUpdateScrollBars = parentWorldChanged || ArrangeChanged || LocalMatrixChanged; base.UpdateWorldMatrix(ref parentWorldMatrix, parentWorldChanged); // set the world matrices of the scroll bars if (shouldUpdateScrollBars && VisualContent != null) { foreach (var index in ScrollModeToDirectionIndices[ScrollMode]) { var scrollBar = scrollBars[index]; var barPosition = RenderSize / 2 - scrollBar.RenderSize; var childMinusParent = VisualContent.DesiredSizeWithMargins[index] - ViewPort[index]; // determine the position ratio of the scroll bar in the viewport var scrollBarPositionRatio = 0f; if (ContentAsScrollInfo != null && ContentAsScrollInfo.CanScroll((Orientation)index)) { scrollBarPositionRatio = -ContentAsScrollInfo.ScrollBarPositions[index]; } else if (childMinusParent > MathUtil.ZeroTolerance) { scrollBarPositionRatio = ScrollOffsets[index] / childMinusParent; } // adjust the position of the scroll bar barPosition[index] = -(RenderSize[index] / 2 + (scrollBarPositionRatio * (RenderSize[index] - scrollBar.RenderSize[index]))); var parentMatrix = WorldMatrix; parentMatrix.TranslationVector += barPosition; ((IUIElementUpdate)scrollBar).UpdateWorldMatrix(ref parentMatrix, true); } } }
private void UpdateScrollOffsets(Vector3 desiredScrollPosition) { // the space desired by the child + the padding of the viewer var childRenderSize = VisualContent.RenderSize; var childRenderSizeWithMargins = CalculateSizeWithoutThickness(ref childRenderSize, ref MarginInternal); var childRenderSizeWithPadding = CalculateSizeWithoutThickness(ref childRenderSizeWithMargins, ref padding); // update scroll viewer scroll offsets foreach (var index in ScrollModeToDirectionIndices[ScrollMode]) { // reset scroll offsets if scrolling is delegated to content if (ContentAsScrollInfo != null && ContentAsScrollInfo.CanScroll((Orientation)index)) { ScrollOffsets[index] = 0; continue; } // update the scroll offset ScrollOffsets[index] = desiredScrollPosition[index]; // no blank on the other extremity (reached the offset "right" limit) var minimumOffset = ViewPort[index] - childRenderSizeWithPadding[index]; if (ScrollOffsets[index] < minimumOffset) { ScrollOffsets[index] = minimumOffset; CurrentScrollingSpeed[index] = 0; } // no positive offsets (reached the offset "left" limit) if (ScrollOffsets[index] > 0) { ScrollOffsets[index] = 0; CurrentScrollingSpeed[index] = 0; } } }
protected override void Update(GameTime time) { base.Update(time); if (!IsEnabled) { return; } var elapsedSeconds = (float)time.Elapsed.TotalSeconds; if (elapsedSeconds < MathUtil.ZeroTolerance) { return; } if (IsUserScrollingViewer || userManuallyScrolled) // scrolling is controlled by the user. { userManuallyScrolled = false; for (var i = 0; i < startedSnapping.Length; i++) { startedSnapping[i] = false; } if (IsUserScrollingViewer) // compute the scrolling speed based on current translation { CurrentScrollingSpeed = LastFrameTranslation / elapsedSeconds; } } else // scrolling is free: compute the scrolling translation based on the scrolling speed and anchors { lastFrameTranslation = elapsedSeconds * CurrentScrollingSpeed; if (SnapToAnchors && ContentAsAnchorInfo != null) { for (var i = 0; i < 3; ++i) { if (!ContentAsAnchorInfo.ShouldAnchor((Orientation)i)) { continue; } // get the distance to anchors from the current position. var boundDistances = ContentAsAnchorInfo.GetSurroudingAnchorDistances((Orientation)i, -ScrollOffsets[i]); // determine the closest anchor index var closestAnchorIndex = Math.Abs(boundDistances.X) <= Math.Abs(boundDistances.Y) ? 0 : 1; // check that the anchor can be reached if (closestAnchorIndex == 1) { var offset = ContentAsScrollInfo != null && ContentAsScrollInfo.CanScroll((Orientation)i) ? -ContentAsScrollInfo.Offset[i] : ScrollOffsets[i]; var childRenderSize = VisualContent.RenderSize; var childRenderSizeWithMargins = CalculateSizeWithThickness(ref childRenderSize, ref MarginInternal); var childRenderSizeWithPadding = CalculateSizeWithThickness(ref childRenderSizeWithMargins, ref padding); if (offset - boundDistances[1] < ViewPort[i] - childRenderSizeWithPadding[i]) { closestAnchorIndex = 0; } } // set the position manually to the anchor if already very close. if (Math.Abs(CurrentScrollingSpeed[i]) < 5 && Math.Abs(boundDistances[closestAnchorIndex]) < 1) // very slow speed and closer than 1 virtual pixel to anchor { startedSnapping[i] = false; CurrentScrollingSpeed[i] = 0; lastFrameTranslation[i] = boundDistances[closestAnchorIndex]; continue; } var snappingSpeed = 5 * boundDistances[closestAnchorIndex]; if (startedSnapping[i] || Math.Abs(snappingSpeed) > Math.Abs(CurrentScrollingSpeed[i])) { // set the scrolling speed based on the remaining distance to the closest anchor CurrentScrollingSpeed[i] = snappingSpeed; lastFrameTranslation[i] = elapsedSeconds * CurrentScrollingSpeed[i]; startedSnapping[i] = true; } } } } // decrease the scrolling speed used for next frame foreach (var index in ScrollModeToDirectionIndices[ScrollMode]) { CurrentScrollingSpeed[index] = Math.Sign(CurrentScrollingSpeed[index]) * Math.Max(0, Math.Abs(CurrentScrollingSpeed[index]) - elapsedSeconds * Deceleration); } // update the scrolling position if (lastFrameTranslation != Vector3.Zero) { ScrollOfInternal(ref lastFrameTranslation, false); } // Smoothly hide the scroll bars if the no movements for (var dim = 0; dim < 3; dim++) { var shouldFadeOutScrollingBar = Math.Abs(CurrentScrollingSpeed[dim]) < MathUtil.ZeroTolerance && (!TouchScrollingEnabled || !IsUserScrollingViewer); if (shouldFadeOutScrollingBar) { for (var i = 0; i < 4; i++) { scrollBars[dim].BarColorInternal[i] = (byte)Math.Max(0, scrollBars[dim].BarColorInternal[i] - ScrollBarColor[i] * ScrollBarHidingSpeed * elapsedSeconds); } } else { scrollBars[dim].BarColor = ScrollBarColor; } } lastFrameTranslation = Vector3.Zero; }