예제 #1
0
        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);
            }
        }
예제 #2
0
        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);
        }
예제 #3
0
        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;
        }
예제 #4
0
        /// <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();
        }
예제 #5
0
        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);
        }
예제 #6
0
        /// <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
                });
            }
        }
예제 #7
0
        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
                });
            }
        }
예제 #8
0
        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;
        }
예제 #9
0
        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;
                }
            }
        }
예제 #10
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);
                }
            }
        }
예제 #11
0
        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;
                }
            }
        }
예제 #12
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;
        }