Beispiel #1
0
        double GetAverageLineInfo(

            Size availableSize,
            VirtualizingLayoutContext context,
            FlowLayoutState flowState,
            double avgCountInLine)
        {
            // default to 1 item per line with 0 size
            double avgLineSize = 0;

            avgCountInLine = 1;

            MUX_ASSERT(context.ItemCount > 0);
            if (flowState.TotalLinesMeasured == 0)
            {
                var tmpElement  = context.GetOrCreateElementAt(0, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
                var desiredSize = flowState.FlowAlgorithm.MeasureElement(tmpElement, 0, availableSize, context);
                context.RecycleElement(tmpElement);

                int estimatedCountInLine = Math.Max(1, (int)(Minor(availableSize) / Minor(desiredSize)));
                flowState.OnLineArranged(0, estimatedCountInLine, Major(desiredSize), context);
                flowState.SpecialElementDesiredSize = desiredSize;
            }

            avgCountInLine = Math.Max(1.0, flowState.TotalItemsPerLine / flowState.TotalLinesMeasured);
            avgLineSize    = Math.Round(flowState.TotalLineSize / flowState.TotalLinesMeasured);

            return(_uno_lastKnownAverageLineSize = avgLineSize);
        }
Beispiel #2
0
        public UIElement GetAt(int realizedIndex)
        {
            UIElement element;

            if (IsVirtualizingContext)
            {
                if (!m_realizedElements.TryGetElementAt(realizedIndex, out element))
                {
                    // Sentinel. Create the element now since we need it.
                    int dataIndex = GetDataIndexFromRealizedRangeIndex(realizedIndex);
                    REPEATER_TRACE_INFO("Creating element for sentinal with data index %d. \n", dataIndex);
                    element = m_context.GetOrCreateElementAt(dataIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
                    m_realizedElements[realizedIndex] = element;
                }
            }
            else
            {
                // realizedIndex and dataIndex are the same (everything is realized)
                element = m_context.GetOrCreateElementAt(realizedIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
            }

            return(element);
        }
Beispiel #3
0
        void MakeAnchor(
            VirtualizingLayoutContext context,
            int index,
            Size availableSize)
        {
            m_elementManager.ClearRealizedRange();
            // FlowLayout requires that the anchor is the first element in the row.
            var internalAnchor = m_algorithmCallbacks.Algorithm_GetAnchorForTargetElement(index, availableSize, context);

            global::System.Diagnostics.Debug.Assert(internalAnchor.Index <= index);

            // No need to set the position of the anchor.
            // (0,0) is fine for now since the extent can
            // grow in any direction.
            for (int dataIndex = internalAnchor.Index; dataIndex < index + 1; ++dataIndex)
            {
                var element = context.GetOrCreateElementAt(dataIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
                element.Measure(m_algorithmCallbacks.Algorithm_GetMeasureSize(dataIndex, availableSize, context));
                m_elementManager.Add(element, dataIndex);
            }
        }
Beispiel #4
0
        double GetAverageElementSize(
            Size availableSize,
            VirtualizingLayoutContext context,
            StackLayoutState stackLayoutState)
        {
            double averageElementSize = 0;

            if (context.ItemCount > 0)
            {
                if (stackLayoutState.TotalElementsMeasured == 0)
                {
                    var tmpElement = context.GetOrCreateElementAt(0, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
                    stackLayoutState.FlowAlgorithm.MeasureElement(tmpElement, 0, availableSize, context);
                    context.RecycleElement(tmpElement);
                }

                MUX_ASSERT(stackLayoutState.TotalElementsMeasured > 0);
                averageElementSize = Math.Round(stackLayoutState.TotalElementSize / stackLayoutState.TotalElementsMeasured);
            }

            return(averageElementSize);
        }
Beispiel #5
0
        int GetAnchorIndex(
            Size availableSize,
            bool isWrapping,
            double minItemSpacing,
            string layoutId)
        {
            int   anchorIndex    = -1;
            Point anchorPosition = default;
            var   context        = m_context;

            if (!IsVirtualizingContext)
            {
                // Non virtualizing host, start generating from the element 0
                anchorIndex = context.ItemCount > 0 ? 0 : -1;
            }
            else
            {
                bool isRealizationWindowConnected = m_elementManager.IsWindowConnected(RealizationRect, ScrollOrientation, m_scrollOrientationSameAsFlow);
                // Item spacing and size in non-virtualizing direction change can cause elements to reflow
                // and get a new column position. In that case we need the anchor to be positioned in the
                // correct column.
                bool needAnchorColumnRevaluation = isWrapping && (
                    Minor(m_lastAvailableSize) != Minor(availableSize) ||
                    m_lastItemSpacing != minItemSpacing ||
                    m_collectionChangePending);

                var suggestedAnchorIndex = m_context.RecommendedAnchorIndex;

                bool isAnchorSuggestionValid = suggestedAnchorIndex >= 0 &&
                                               m_elementManager.IsDataIndexRealized(suggestedAnchorIndex);

                if (isAnchorSuggestionValid)
                {
                    REPEATER_TRACE_INFO("%*s: \tUsing suggested anchor %d\n", context.Indent, layoutId, suggestedAnchorIndex);
                    anchorIndex = m_algorithmCallbacks.Algorithm_GetAnchorForTargetElement(
                        suggestedAnchorIndex,
                        availableSize,
                        context).Index;

                    if (m_elementManager.IsDataIndexRealized(anchorIndex))
                    {
                        var anchorBounds = m_elementManager.GetLayoutBoundsForDataIndex(anchorIndex);
                        if (needAnchorColumnRevaluation)
                        {
                            // We were provided a valid anchor, but its position might be incorrect because for example it is in
                            // the wrong column. We do know that the anchor is the first element in the row, so we can force the minor position
                            // to start at 0.
                            anchorPosition = MinorMajorPoint(0, (float)MajorStart(anchorBounds));
                        }
                        else
                        {
                            anchorPosition = new Point(anchorBounds.X, anchorBounds.Y);
                        }
                    }
                    else
                    {
                        // It is possible to end up in a situation during a collection change where GetAnchorForTargetElement returns an index
                        // which is not in the realized range. Eg. insert one item at index 0 for a grid layout.
                        // SuggestedAnchor will be 1 (used to be 0) and GetAnchorForTargetElement will return 0 (left most item in row). However 0 is not in the
                        // realized range yet. In this case we realize the gap between the target anchor and the suggested anchor.
                        int firstRealizedDataIndex = m_elementManager.GetDataIndexFromRealizedRangeIndex(0);
                        global::System.Diagnostics.Debug.Assert(anchorIndex < firstRealizedDataIndex);
                        for (int i = firstRealizedDataIndex - 1; i >= anchorIndex; --i)
                        {
                            m_elementManager.EnsureElementRealized(false /*forward*/, i, layoutId);
                        }

                        var anchorBounds = m_elementManager.GetLayoutBoundsForDataIndex(suggestedAnchorIndex);
                        anchorPosition = MinorMajorPoint(0, (float)MajorStart(anchorBounds));
                    }
                }
                else if (needAnchorColumnRevaluation || !isRealizationWindowConnected)
                {
                    if (needAnchorColumnRevaluation)
                    {
                        REPEATER_TRACE_INFO("%*s: \tNeedAnchorColumnReevaluation \n", context.Indent, layoutId);
                    }

                    if (!isRealizationWindowConnected)
                    {
                        REPEATER_TRACE_INFO("%*s: \tDisconnected Window \n", context.Indent, layoutId);
                    }

                    // The anchor is based on the realization window because a connected ItemsRepeater might intersect the realization window
                    // but not the visible window. In that situation, we still need to produce a valid anchor.
                    var anchorInfo = m_algorithmCallbacks.Algorithm_GetAnchorForRealizationRect(availableSize, context);
                    anchorIndex    = anchorInfo.Index;
                    anchorPosition = MinorMajorPoint(0, (float)(anchorInfo.Offset));
                }
                else
                {
                    REPEATER_TRACE_INFO("%*s: \tConnected Window - picking first realized element as anchor \n", context.Indent, layoutId);
                    // No suggestion - just pick first in realized range
                    anchorIndex = m_elementManager.GetDataIndexFromRealizedRangeIndex(0);
                    var firstElementBounds = m_elementManager.GetLayoutBoundsForRealizedIndex(0);
                    anchorPosition = new Point(firstElementBounds.X, firstElementBounds.Y);
                }
            }

            REPEATER_TRACE_INFO("%*s: \tPicked anchor:%d \n", context.Indent, layoutId, anchorIndex);
            global::System.Diagnostics.Debug.Assert(anchorIndex == -1 || m_elementManager.IsIndexValidInData(anchorIndex));
            m_firstRealizedDataIndexInsideRealizationWindow = m_lastRealizedDataIndexInsideRealizationWindow = anchorIndex;
            if (m_elementManager.IsIndexValidInData(anchorIndex))
            {
                if (!m_elementManager.IsDataIndexRealized(anchorIndex))
                {
                    // Disconnected, throw everything and create new anchor
                    REPEATER_TRACE_INFO("%*s Disconnected Window - throwing away all realized elements \n", context.Indent, layoutId);
                    m_elementManager.ClearRealizedRange();

                    var anchor = m_context.GetOrCreateElementAt(anchorIndex, ElementRealizationOptions.ForceCreate | ElementRealizationOptions.SuppressAutoRecycle);
                    m_elementManager.Add(anchor, anchorIndex);
                }

                var anchorElement = m_elementManager.GetRealizedElement(anchorIndex);
                var desiredSize   = MeasureElement(anchorElement, anchorIndex, availableSize, m_context);
                var layoutBounds  = new Rect(anchorPosition.X, anchorPosition.Y, desiredSize.Width, desiredSize.Height);
                m_elementManager.SetLayoutBoundsForDataIndex(anchorIndex, layoutBounds);

                REPEATER_TRACE_INFO("%*s: \tLayout bounds of anchor %d are (%.0f,%.0f,%.0f,%.0f). \n",
                                    context.Indent,
                                    layoutId,
                                    anchorIndex,
                                    layoutBounds.X, layoutBounds.Y, layoutBounds.Width, layoutBounds.Height);
            }
            else
            {
                // Throw everything away
                REPEATER_TRACE_INFO("%*s \tAnchor index is not valid - throwing away all realized elements \n",
                                    context.Indent,
                                    layoutId);
                m_elementManager.ClearRealizedRange();
            }

            // TODO: Perhaps we can track changes in the property setter
            m_lastAvailableSize = availableSize;
            m_lastItemSpacing   = minItemSpacing;

            return(anchorIndex);
        }
Beispiel #6
0
        protected override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize)
        {
            if (this.MinItemSize == Size.Empty)
            {
                var firstElement = context.GetOrCreateElementAt(0);
                firstElement.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));

                // setting the member value directly to skip invalidating layout
                this._minItemSize = firstElement.DesiredSize;
            }

            // Determine which rows need to be realized.  We know every row will have the same height and
            // only contain 3 items.  Use that to determine the index for the first and last item that
            // will be within that realization rect.
            var firstRowIndex = Math.Max(
                (int)(context.RealizationRect.Y / (this.MinItemSize.Height + this.RowSpacing)) - 1,
                0);
            var lastRowIndex = Math.Min(
                (int)(context.RealizationRect.Bottom / (this.MinItemSize.Height + this.RowSpacing)) + 1,
                (int)(context.ItemCount / 3));

            // Determine which items will appear on those rows and what the rect will be for each item
            var state = context.LayoutState as ActivityFeedLayoutState;

            state.LayoutRects.Clear();

            // Save the index of the first realized item.  We'll use it as a starting point during arrange.
            state.FirstRealizedIndex = firstRowIndex * 3;

            // ideal item width that will expand/shrink to fill available space
            double desiredItemWidth = Math.Max(this.MinItemSize.Width, (availableSize.Width - this.ColumnSpacing * 3) / 4);

            // Foreach item between the first and last index,
            //     Call GetElementOrCreateElementAt which causes an element to either be realized or retrieved
            //       from a recycle pool
            //     Measure the element using an appropriate size
            //
            // Any element that was previously realized which we don't retrieve in this pass (via a call to
            // GetElementOrCreateAt) will be automatically cleared and set aside for later re-use.
            // Note: While this work fine, it does mean that more elements than are required may be
            // created because it isn't until after our MeasureOverride completes that the unused elements
            // will be recycled and available to use.  We could avoid this by choosing to track the first/last
            // index from the previous layout pass.  The diff between the previous range and current range
            // would represent the elements that we can pre-emptively make available for re-use by calling
            // context.RecycleElement(element).
            for (int rowIndex = firstRowIndex; rowIndex < lastRowIndex; rowIndex++)
            {
                int firstItemIndex      = rowIndex * 3;
                var boundsForCurrentRow = CalculateLayoutBoundsForRow(rowIndex, desiredItemWidth);

                for (int columnIndex = 0; columnIndex < 3; columnIndex++)
                {
                    var index     = firstItemIndex + columnIndex;
                    var rect      = boundsForCurrentRow[index % 3];
                    var container = context.GetOrCreateElementAt(index);

                    container.Measure(
                        new Size(boundsForCurrentRow[columnIndex].Width, boundsForCurrentRow[columnIndex].Height));

                    state.LayoutRects.Add(boundsForCurrentRow[columnIndex]);
                }
            }

            // Calculate and return the size of all the content (realized or not) by figuring out
            // what the bottom/right position of the last item would be.
            var extentHeight = ((int)(context.ItemCount / 3) - 1) * (this.MinItemSize.Height + this.RowSpacing) + this.MinItemSize.Height;

            // Report this as the desired size for the layout
            return(new Size(desiredItemWidth * 4 + this.ColumnSpacing * 2, extentHeight));
        }
Beispiel #7
0
 public UIElement this[int index] => m_context.GetOrCreateElementAt(index, ElementRealizationOptions.None);