protected AbstractViewsBase GetViewsHolderOfClosestItemToViewportPoint( ICollection <UILoopSmartItem> viewsHolders, Func <RectTransform, float, RectTransform, float, float> getDistanceFn, float viewportPoint01, float itemPoint01, out float distance ) { UILoopSmartItem result = null; float minDistance = float.MaxValue; float curDistance; foreach (var vh in viewsHolders) { curDistance = Mathf.Abs(getDistanceFn(vh.root, itemPoint01, _Collocation.viewport, viewportPoint01)); if (curDistance < minDistance) { result = vh; minDistance = curDistance; } } distance = minDistance; return(result); }
internal float ChangeItemSizeAndUpdateContentSizeAccordingly(UILoopSmartItem viewsHolder, int cellIndex, float requestedSize, bool itemEndEdgeStationary, bool rebuild = true) { float resolvedSize; if (viewsHolder == null) { resolvedSize = requestedSize; } else { if (viewsHolder.root == null) { throw new UnityException("God bless: shouldn't happen if implemented according to documentation/examples"); // shouldn't happen if implemented according to documentation/examples } RectTransform.Edge edge; float realInsetToSet; if (itemEndEdgeStationary) { edge = endEdge; realInsetToSet = GetItemInferredRealInsetFromParentEnd(cellIndex); } else { edge = startEdge; realInsetToSet = GetItemInferredRealInsetFromParentStart(cellIndex); } viewsHolder.root.SetInsetAndSizeFromParentEdgeWithCurrentAnchors(_SourceParams.content, edge, realInsetToSet, requestedSize); resolvedSize = _GetRTCurrentSizeFn(viewsHolder.root); //viewsHolder.cachedSize = resolvedSize; } _ItemsDesc.BeginChangingItemsSizes(cellIndex); _ItemsDesc[cellIndex] = resolvedSize; _ItemsDesc.EndChangingItemsSizes(); OnCumulatedSizesOfAllItemsChanged(itemEndEdgeStationary, rebuild); return(resolvedSize); }
protected virtual void OnItemWidthChangedPreTwinPass(UILoopSmartItem viewsHolder) { }
protected virtual void OnBeforeRecycleOrDisableViewsHolder(UILoopSmartItem inRecycleBinOrVisible, int newItemIndex) { }
protected virtual bool ShouldDestroyRecyclableItem(UILoopSmartItem inRecycleBin, bool isInExcess) { return(isInExcess); }
protected virtual bool IsRecyclable(UILoopSmartItem potentiallyRecyclable, int indexOfItemThatWillBecomeVisible, float heightOfItemThatWillBecomeVisible) { return(true); }
protected abstract void UpdateCellView(UILoopSmartItem newOrRecycled);
public float RequestChangeItemSizeAndUpdateLayout(UILoopSmartItem withVH, float requestedSize, bool itemEndEdgeStationary = false, bool computeVisibility = true) { return(RequestChangeItemSizeAndUpdateLayout(withVH.ItemIndex, requestedSize, itemEndEdgeStationary, computeVisibility)); }
/// <summary>The very core of <see cref="SmartScrollView{TParams, UILoopSmartItem}"/>. You must be really brave if you think about trying to understand it :)</summary> void ComputeVisibility(double abstractDelta) { bool negativeScroll = abstractDelta <= 0d; //bool verticalScroll = _Params.scrollRect.vertical; // Viewport constant values float vpSize = _InternalState.viewportSize; // Content panel constant values float ctSpacing = _InternalState.spacing, ctPadTransvStart = _InternalState.transversalPaddingContentStart; // Items constant values float allItemsTransversalSizes = _ItemsDesc.itemsConstantTransversalSize; // Items variable values UILoopSmartItem nlvHolder = null; //int currentLVItemIndex; int currentLVcellIndex; double negCurrentVrtInsetFromCTSToUseForNLV_posCurrentVrtInsetFromCTEToUseForNLV; //RectTransform.Edge negStartEdge_posEndEdge; RectTransform.Edge transvStartEdge = _InternalState.transvStartEdge; int endcellIndex, neg1_posMinus1, //negMinus1_pos1, neg1_pos0, neg0_pos1; if (negativeScroll) { neg1_posMinus1 = 1; } else { neg1_posMinus1 = -1; } neg1_pos0 = (neg1_posMinus1 + 1) / 2; neg0_pos1 = 1 - neg1_pos0; currentLVcellIndex = neg0_pos1 * (_ItemsDesc.itemsCount - 1) - neg1_posMinus1; bool thereWereVisibletems = _VisibleItemsCount > 0; endcellIndex = neg1_pos0 * (_ItemsDesc.itemsCount - 1); double ctVrtInsetFromVPS = _InternalState.ContentPanelVirtualInsetFromViewportStart; double negCTVrtInsetFromVPS_posCTVrtInsetFromVPE = negativeScroll ? ctVrtInsetFromVPS : (-_InternalState.contentPanelVirtualSize + _InternalState.viewportSize - ctVrtInsetFromVPS); UILoopSmartItem startingLVHolder = null; if (thereWereVisibletems) { int startingLVHolderIndex; startingLVHolderIndex = neg1_pos0 * (_VisibleItemsCount - 1); startingLVHolder = _VisibleItems[startingLVHolderIndex]; currentLVcellIndex = startingLVHolder.cellIndex; bool currentIsOutside; //RectTransform curRecCandidateRT; float curRecCandidateSizePlusSpacing; // vItemHolder is: // first in _VisibleItems, if negativeScroll // last in _VisibleItems, else int curRecCandidateVHIndex = neg0_pos1 * (_VisibleItemsCount - 1); UILoopSmartItem curRecCandidateVH = _VisibleItems[curRecCandidateVHIndex]; double curInsetFromParentEdge = negativeScroll ? _InternalState.GetItemVirtualInsetFromParentStartUsingcellIndex(curRecCandidateVH.cellIndex) : _InternalState.GetItemVirtualInsetFromParentEndUsingcellIndex(curRecCandidateVH.cellIndex); while (true) { curRecCandidateSizePlusSpacing = _ItemsDesc[curRecCandidateVH.cellIndex] + ctSpacing; currentIsOutside = negCTVrtInsetFromVPS_posCTVrtInsetFromVPE + (curInsetFromParentEdge + curRecCandidateSizePlusSpacing) <= 0d; if (currentIsOutside) { _RecyclableItems.Add(curRecCandidateVH); _VisibleItems.RemoveAt(curRecCandidateVHIndex); --_VisibleItemsCount; if (_VisibleItemsCount == 0) { break; } } else { break; } curRecCandidateVHIndex -= neg0_pos1; curInsetFromParentEdge += curRecCandidateSizePlusSpacing; curRecCandidateVH = _VisibleItems[curRecCandidateVHIndex]; } } double currentItemVrtInset_negStart_posEnd = double.PositiveInfinity; int estimatedAVGVisibleItems = -1; if (Math.Abs(abstractDelta) > 10000d && // huge jumps need optimization (estimatedAVGVisibleItems = (int)Math.Round(Math.Min(_InternalState.viewportSize / ((_Collocation.DefaultItemSize + _InternalState.spacing)), _AVGVisibleItemsCount))) < _ItemsDesc.itemsCount ) { int estimatedIndexInViewOfNewFirstVisible = (int) Math.Round( (1d - _InternalState.GetVirtualAbstractNormalizedScrollPosition()) * ((_ItemsDesc.itemsCount - 1) - neg1_pos0 * estimatedAVGVisibleItems) ); double negCTVrtAmountBeforeVP_posCTVrtAmountAfterVP = Math.Max(-negCTVrtInsetFromVPS_posCTVrtInsetFromVPE, 0d); int initialEstimatedIndexInViewOfNewFirstVisible = estimatedIndexInViewOfNewFirstVisible; int index = initialEstimatedIndexInViewOfNewFirstVisible; float itemSize = _ItemsDesc[index]; double negInsetStart_posInsetEnd = neg0_pos1 * (_InternalState.contentPanelVirtualSize - itemSize) + neg1_posMinus1 * _InternalState.GetItemVirtualInsetFromParentStartUsingcellIndex(index); while (negInsetStart_posInsetEnd <= /*minEstimatedItemInsetFrom_negStart_posEnd*/ negCTVrtAmountBeforeVP_posCTVrtAmountAfterVP - itemSize) { index += neg1_posMinus1; itemSize = _ItemsDesc[index]; negInsetStart_posInsetEnd += itemSize + _InternalState.spacing; } if (index == initialEstimatedIndexInViewOfNewFirstVisible) { // Executes at least once while (negInsetStart_posInsetEnd > /*minEstimatedItemInsetFrom_negStart_posEnd*/ negCTVrtAmountBeforeVP_posCTVrtAmountAfterVP - itemSize) { index -= neg1_posMinus1; itemSize = _ItemsDesc[index]; negInsetStart_posInsetEnd -= itemSize + _InternalState.spacing; } negInsetStart_posInsetEnd += itemSize + _InternalState.spacing; } else // index bigger (lesser, if pos scroll) than initial { index -= neg1_posMinus1; } if (!thereWereVisibletems || negativeScroll && index > currentLVcellIndex || !negativeScroll && index < currentLVcellIndex // analogous explanation for pos scroll ) { //Debug.Log("est=" + estimatedIndexInViewOfNewFirstVisible + ", def=" + currentLVcellIndex + ", actual=" + index); currentLVcellIndex = index; currentItemVrtInset_negStart_posEnd = negInsetStart_posInsetEnd; } } if (double.IsInfinity(currentItemVrtInset_negStart_posEnd) && currentLVcellIndex != endcellIndex) { int nextValueOf_NLVIndexInView = currentLVcellIndex + neg1_posMinus1; // next value in the loop below if (negativeScroll) { currentItemVrtInset_negStart_posEnd = _InternalState.GetItemVirtualInsetFromParentStartUsingcellIndex(nextValueOf_NLVIndexInView); } else { currentItemVrtInset_negStart_posEnd = _InternalState.GetItemVirtualInsetFromParentEndUsingcellIndex(nextValueOf_NLVIndexInView); } } do { if (currentLVcellIndex == endcellIndex) { break; } //int nlvIndex = currentLVcellIndex; // TODO pending removal int nlvIndexInView = currentLVcellIndex; float nlvSize; bool breakBigLoop = false, negNLVCandidateIsBeforeVP_posNLVCandidateIsAfterVP; // before vpStart, if negative scroll; after vpEnd, else do { nlvIndexInView += neg1_posMinus1; nlvSize = _ItemsDesc[nlvIndexInView]; negCurrentVrtInsetFromCTSToUseForNLV_posCurrentVrtInsetFromCTEToUseForNLV = currentItemVrtInset_negStart_posEnd; negNLVCandidateIsBeforeVP_posNLVCandidateIsAfterVP = negCTVrtInsetFromVPS_posCTVrtInsetFromVPE + (negCurrentVrtInsetFromCTSToUseForNLV_posCurrentVrtInsetFromCTEToUseForNLV + nlvSize) <= 0d; if (negNLVCandidateIsBeforeVP_posNLVCandidateIsAfterVP) { if (nlvIndexInView == endcellIndex) // all items are outside viewport => abort { breakBigLoop = true; break; } } else { if (negCTVrtInsetFromVPS_posCTVrtInsetFromVPE + negCurrentVrtInsetFromCTSToUseForNLV_posCurrentVrtInsetFromCTEToUseForNLV > vpSize) { breakBigLoop = true; break; } break; } currentItemVrtInset_negStart_posEnd += nlvSize + _InternalState.spacing; }while (true); if (breakBigLoop) { break; } int nlvRealIndex = _ItemsDesc.GetItemRealIndexFromViewIndex(nlvIndexInView); int i = 0; UILoopSmartItem potentiallyRecyclable; while (true) { if (i < _RecyclableItems.Count) { potentiallyRecyclable = _RecyclableItems[i]; if (IsRecyclable(potentiallyRecyclable, nlvRealIndex, nlvSize)) { OnBeforeRecycleOrDisableViewsHolder(potentiallyRecyclable, nlvRealIndex); _RecyclableItems.RemoveAt(i); nlvHolder = potentiallyRecyclable; break; } ++i; } else { // Found no recyclable view with the requested height nlvHolder = CreateCellView(nlvRealIndex); break; } } // Add it in list at [end] _VisibleItems.Insert(neg1_pos0 * _VisibleItemsCount, nlvHolder); ++_VisibleItemsCount; // Update its index nlvHolder.ItemIndex = nlvRealIndex; nlvHolder.cellIndex = nlvIndexInView; //// Cache its height //nlvHolder.cachedSize = _ItemsDescriptor.itemsSizes[nlvIndexInView]; // Make sure it's parented to content panel RectTransform nlvRT = nlvHolder.root; nlvRT.SetParent(_Collocation.content, false); // Update its views UpdateCellView(nlvHolder); // Make sure it's GO is activated nlvHolder.root.gameObject.SetActive(true); nlvRT.anchorMin = nlvRT.anchorMax = _InternalState.constantAnchorPosForAllItems; double currentVirtualInsetFromCTSToUseForNLV = neg0_pos1 * (_InternalState.contentPanelVirtualSize - nlvSize) + neg1_posMinus1 * negCurrentVrtInsetFromCTSToUseForNLV_posCurrentVrtInsetFromCTEToUseForNLV; nlvRT.SetInsetAndSizeFromParentEdgeWithCurrentAnchors( _Collocation.content, _InternalState.startEdge, _InternalState.ConvertItemInsetFromParentStart_FromVirtualToReal(currentVirtualInsetFromCTSToUseForNLV), nlvSize ); nlvRT.SetInsetAndSizeFromParentEdgeWithCurrentAnchors(_Collocation.content, transvStartEdge, ctPadTransvStart, allItemsTransversalSizes); currentLVcellIndex = nlvIndexInView; currentItemVrtInset_negStart_posEnd += nlvSize + _InternalState.spacing; }while (true); if (_VisibleItemsCount > _ItemsDesc.maxVisibleItemsSeenSinceLastScrollViewSizeChange) { _ItemsDesc.maxVisibleItemsSeenSinceLastScrollViewSizeChange = _VisibleItemsCount; } GameObject go; UILoopSmartItem vh; for (int i = 0; i < _RecyclableItems.Count;) { vh = _RecyclableItems[i]; go = vh.root.gameObject; if (go.activeSelf) { OnBeforeRecycleOrDisableViewsHolder(vh, -1); // -1 means it'll be disabled, not re-used ATM } go.SetActive(false); if (ShouldDestroyRecyclableItem(vh, GetNumExcessObjects() > 0)) { GameObject.Destroy(go); _RecyclableItems.RemoveAt(i); ++_ItemsDesc.destroyedItemsSinceLastScrollViewSizeChange; } else { ++i; } } _AVGVisibleItemsCount = _AVGVisibleItemsCount * .9d + _VisibleItemsCount * .1d; }