• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/grid/grid_scroll/grid_scroll_layout_algorithm.h"
17 
18 #include "base/log/event_report.h"
19 #include "base/log/log_wrapper.h"
20 #include "base/utils/feature_param.h"
21 #include "core/components_ng/pattern/grid/grid_utils.h"
22 #include "core/components_ng/pattern/grid/irregular/grid_layout_utils.h"
23 #include "core/components_ng/pattern/scrollable/scrollable_utils.h"
24 #include "core/components_ng/pattern/text_field/text_field_manager.h"
25 #include "core/components_ng/property/templates_parser.h"
26 
27 namespace OHOS::Ace::NG {
28 namespace {
AddCacheItemsInFront(int32_t startIdx,LayoutWrapper * host,int32_t cacheCnt,std::list<GridPreloadItem> & buildList)29 void AddCacheItemsInFront(
30     int32_t startIdx, LayoutWrapper* host, int32_t cacheCnt, std::list<GridPreloadItem>& buildList)
31 {
32     for (int32_t i = 1; i <= cacheCnt; ++i) {
33         int32_t item = startIdx - i;
34         if (item < 0) {
35             break;
36         }
37         if (!host->GetChildByIndex(item, true)) {
38             buildList.emplace_back(item, true);
39         }
40     }
41 }
42 } // namespace
43 
Measure(LayoutWrapper * layoutWrapper)44 void GridScrollLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
45 {
46     wrapper_ = layoutWrapper;
47     auto gridLayoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
48     CHECK_NULL_VOID(gridLayoutProperty);
49 
50     // Pre-recycle
51     ScrollableUtils::RecycleItemsOutOfBoundary(
52         info_.axis_, info_.currentOffset_ - info_.prevOffset_, info_.startIndex_, info_.endIndex_, layoutWrapper);
53 
54     // Step1: Decide size of Grid
55     Axis axis = info_.axis_;
56     frameSize_ = CreateIdealSize(
57         gridLayoutProperty->GetLayoutConstraint().value(), axis, gridLayoutProperty->GetMeasureType(), true);
58     auto layoutPolicy = gridLayoutProperty->GetLayoutPolicyProperty();
59     auto isMainWrap = false;
60     if (layoutPolicy.has_value()) {
61         auto isVertical = axis == Axis::VERTICAL;
62         auto widthLayoutPolicy = layoutPolicy.value().widthLayoutPolicy_.value_or(LayoutCalPolicy::NO_MATCH);
63         auto heightLayoutPolicy = layoutPolicy.value().heightLayoutPolicy_.value_or(LayoutCalPolicy::NO_MATCH);
64         auto isMainFix = (isVertical ? heightLayoutPolicy : widthLayoutPolicy) == LayoutCalPolicy::FIX_AT_IDEAL_SIZE;
65         isMainWrap = (isVertical ? heightLayoutPolicy : widthLayoutPolicy) == LayoutCalPolicy::WRAP_CONTENT;
66         if (isMainFix) {
67             frameSize_.SetMainSize(Infinity<float>(), axis);
68         }
69         auto layoutPolicySize = ConstrainIdealSizeByLayoutPolicy(
70             gridLayoutProperty->GetLayoutConstraint().value(), widthLayoutPolicy, heightLayoutPolicy, axis);
71         frameSize_.UpdateIllegalSizeWithCheck(layoutPolicySize.ConvertToSizeT());
72     }
73     if (NearZero(GetMainAxisSize(frameSize_, axis))) {
74         TAG_LOGW(AceLogTag::ACE_GRID, "size of main axis value is 0, please check");
75         return;
76     }
77     bool matchChildren = GreaterOrEqualToInfinity(GetMainAxisSize(frameSize_, axis)) || isMainWrap;
78     syncLoad_ = gridLayoutProperty->GetSyncLoad().value_or(!FeatureParam::IsSyncLoadEnabled()) || matchChildren ||
79                 info_.targetIndex_.has_value() || !NearEqual(info_.currentOffset_, info_.prevOffset_);
80     layoutWrapper->GetGeometryNode()->SetFrameSize(frameSize_);
81     MinusPaddingToSize(gridLayoutProperty->CreatePaddingAndBorder(), frameSize_);
82     info_.contentEndPadding_ = ScrollableUtils::CheckHeightExpansion(gridLayoutProperty, axis);
83     frameSize_.AddHeight(info_.contentEndPadding_);
84     auto&& safeAreaOpts = gridLayoutProperty->GetSafeAreaExpandOpts();
85     expandSafeArea_ = safeAreaOpts && safeAreaOpts->Expansive();
86     auto host = layoutWrapper->GetHostNode();
87     if (host) {
88         auto pipeline = host->GetContext();
89         if (pipeline && pipeline->GetPixelRoundMode() == PixelRoundMode::PIXEL_ROUND_AFTER_MEASURE) {
90             frameSize_.SetWidth(round(frameSize_.Width()));
91             frameSize_.SetHeight(round(frameSize_.Height()));
92         }
93     }
94 
95     InitialItemsCrossSize(gridLayoutProperty, frameSize_, info_.GetChildrenCount());
96 
97     // Step2: Measure children that can be displayed in viewport of Grid
98     float mainSize = GetMainAxisSize(frameSize_, axis);
99     float crossSize = GetCrossAxisSize(frameSize_, axis);
100     if (!NearEqual(mainSize, info_.lastMainSize_)) {
101         UpdateOffsetOnVirtualKeyboardHeightChange(layoutWrapper, mainSize);
102         UpdateOffsetOnHeightChangeDuringAnimation(layoutWrapper, mainSize);
103         info_.ResetPositionFlags();
104     }
105     FillGridViewportAndMeasureChildren(mainSize, crossSize, layoutWrapper);
106 
107     if (gridLayoutProperty->GetAlignItems().value_or(GridItemAlignment::DEFAULT) == GridItemAlignment::STRETCH) {
108         GridLayoutBaseAlgorithm::AdjustChildrenHeight(layoutWrapper);
109     }
110     // update layout info.
111     info_.prevOffset_ = info_.currentOffset_;
112     // update cache info.
113     const int32_t cacheCnt =
114         static_cast<int32_t>(gridLayoutProperty->GetCachedCountValue(info_.defCachedCount_) * crossCount_);
115     layoutWrapper->SetCacheCount(cacheCnt);
116 
117     info_.lastMainSize_ = mainSize;
118     info_.lastCrossSize_ = crossSize;
119     AdaptToChildMainSize(layoutWrapper, gridLayoutProperty, mainSize, frameSize_, matchChildren);
120 
121     // reset offsetEnd after scroll to moveToEndLineIndex_
122     info_.offsetEnd_ = moveToEndLineIndex_ > 0 ? (info_.endIndex_ + 1 >= info_.GetChildrenCount()) : info_.offsetEnd_;
123 
124     if (SystemProperties::GetGridCacheEnabled()) {
125         if (measureInNextFrame_) {
126             return;
127         }
128         const bool sync = gridLayoutProperty->GetShowCachedItemsValue(false);
129         if (sync) {
130             SyncPreload(
131                 layoutWrapper, gridLayoutProperty->GetCachedCountValue(info_.defCachedCount_), crossSize, mainSize);
132             return;
133         }
134 
135         FillCacheLineAtEnd(mainSize, crossSize, layoutWrapper);
136         AddCacheItemsInFront(info_.startIndex_, layoutWrapper, cacheCnt, predictBuildList_);
137         if (!predictBuildList_.empty()) {
138             PreloadItems(layoutWrapper);
139             predictBuildList_.clear();
140         }
141     }
142 }
143 
PreloadItems(LayoutWrapper * layoutWrapper)144 void GridScrollLayoutAlgorithm::PreloadItems(LayoutWrapper* layoutWrapper)
145 {
146     GridLayoutUtils::PreloadGridItems(layoutWrapper->GetHostNode()->GetPattern<GridPattern>(),
147         std::move(predictBuildList_),
148         [param = GridPredictLayoutParam { cachedChildConstraint_, itemsCrossSize_, crossGap_ }](
149             const RefPtr<FrameNode>& host, int32_t itemIdx) {
150             CHECK_NULL_RETURN(host, false);
151             return PredictBuildItem(*host, itemIdx, param);
152         });
153 }
154 
UpdateOffsetOnVirtualKeyboardHeightChange(LayoutWrapper * layoutWrapper,float mainSize)155 void GridScrollLayoutAlgorithm::UpdateOffsetOnVirtualKeyboardHeightChange(LayoutWrapper* layoutWrapper, float mainSize)
156 {
157     if (GreatOrEqual(mainSize, info_.lastMainSize_)) {
158         return;
159     }
160     // only need to offset vertical grid
161     if (info_.axis_ != Axis::VERTICAL) {
162         return;
163     }
164 
165     auto grid = layoutWrapper->GetHostNode();
166     CHECK_NULL_VOID(grid);
167     auto focusHub = grid->GetFocusHub();
168     CHECK_NULL_VOID(focusHub);
169     // textField not in Grid
170     if (!focusHub->IsCurrentFocus()) {
171         return;
172     }
173 
174     auto context = grid->GetContext();
175     CHECK_NULL_VOID(context);
176     auto textFieldManager = AceType::DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
177     CHECK_NULL_VOID(textFieldManager);
178     // only when textField is onFocus
179     auto focused = textFieldManager->GetOnFocusTextField().Upgrade();
180     CHECK_NULL_VOID(focused);
181     auto position = textFieldManager->GetClickPosition().GetY();
182     auto gridOffset = grid->GetTransformRelativeOffset();
183     auto offset = mainSize + gridOffset.GetY() - position;
184     if (LessOrEqual(offset, 0.0)) {
185         // negative offset to scroll down
186         auto lineHeight = info_.GetAverageLineHeight();
187         if (GreatNotEqual(lineHeight, 0)) {
188             offset = floor(offset / lineHeight) * lineHeight;
189         }
190         info_.currentOffset_ += offset;
191         TAG_LOGI(AceLogTag::ACE_GRID, "update offset on virtual keyboard height change, %{public}f", offset);
192     }
193 }
194 
AdaptToChildMainSize(LayoutWrapper * layoutWrapper,RefPtr<GridLayoutProperty> & gridLayoutProperty,float mainSize,SizeF idealSize,bool matchChildren)195 void GridScrollLayoutAlgorithm::AdaptToChildMainSize(LayoutWrapper* layoutWrapper,
196     RefPtr<GridLayoutProperty>& gridLayoutProperty, float mainSize, SizeF idealSize, bool matchChildren)
197 {
198     if (!matchChildren) {
199         // grid with columnsTemplate/rowsTemplate and maxCount
200         if (!gridLayoutProperty->HasMaxCount()) {
201             return;
202         }
203         std::optional<CalcLength> mainAxisIdealSize;
204         const auto& selfLayoutConstraint = gridLayoutProperty->GetCalcLayoutConstraint();
205         if (selfLayoutConstraint && selfLayoutConstraint->selfIdealSize.has_value()) {
206             mainAxisIdealSize = axis_ == Axis::HORIZONTAL ? selfLayoutConstraint->selfIdealSize->Width()
207                                                           : selfLayoutConstraint->selfIdealSize->Height();
208         }
209 
210         if (mainAxisIdealSize.has_value()) {
211             return;
212         }
213     }
214 
215     auto lengthOfItemsInViewport = info_.GetTotalHeightOfItemsInView(mainGap_);
216     auto gridMainSize = std::min(lengthOfItemsInViewport, mainSize);
217     gridMainSize = std::max(gridMainSize, GetMainAxisSize(gridLayoutProperty->GetLayoutConstraint()->minSize, axis_));
218     idealSize.SetMainSize(gridMainSize, axis_);
219     AddPaddingToSize(gridLayoutProperty->CreatePaddingAndBorder(), idealSize);
220     layoutWrapper->GetGeometryNode()->SetFrameSize(idealSize);
221     info_.lastMainSize_ = gridMainSize;
222     TAG_LOGI(AceLogTag::ACE_GRID, "gridMainSize:%{public}f", gridMainSize);
223 }
224 
UpdateOffsetOnHeightChangeDuringAnimation(LayoutWrapper * layoutWrapper,float mainSize)225 void GridScrollLayoutAlgorithm::UpdateOffsetOnHeightChangeDuringAnimation(LayoutWrapper* layoutWrapper, float mainSize)
226 {
227     // If only the height of the Grid is changed, keep the prevOffset_ and currentOffset_ equal.
228     ResetOffsetWhenHeightChanged();
229     auto host = layoutWrapper->GetHostNode();
230     CHECK_NULL_VOID(host);
231     auto pattern = host->GetPattern<GridPattern>();
232     CHECK_NULL_VOID(pattern);
233     if (pattern->IsScrollableSpringMotionRunning()) {
234         if (info_.reachStart_) {
235             return;
236         }
237         float totalHeight = info_.GetContentHeight(mainGap_);
238         if (info_.lastMainSize_ < totalHeight) {
239             info_.currentOffset_ += (mainSize - info_.lastMainSize_);
240             return;
241         }
242         if (info_.lastMainSize_ >= totalHeight && mainSize < info_.lastMainSize_ && mainSize <= totalHeight) {
243             info_.currentOffset_ += mainSize - totalHeight;
244         }
245     }
246 }
247 
Layout(LayoutWrapper * layoutWrapper)248 void GridScrollLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
249 {
250     auto props = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
251     CHECK_NULL_VOID(props);
252     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
253     auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
254     MinusPaddingToSize(padding, size);
255     childFrameOffset_ = OffsetF(0.0f, padding.top.value_or(0.0f));
256     childFrameOffset_ += OffsetF(0.0f, info_.currentOffset_, axis_);
257     bool isRtl = layoutWrapper->GetLayoutProperty()->GetNonAutoLayoutDirection() == TextDirection::RTL;
258     int32_t startIndex = -1;
259     int32_t endIndex = -1;
260     if (info_.hasMultiLineItem_) {
261         layoutWrapper->RemoveAllChildInRenderTree();
262     }
263     LargeItemForwardLineHeight(info_.startMainLineIndex_, layoutWrapper);
264     const int32_t cacheCount = props->GetCachedCountValue(info_.defCachedCount_);
265     if (!props->HasCachedCount()) {
266         info_.UpdateDefaultCachedCount();
267     }
268     int32_t cacheStart = 0;
269     int32_t cacheEnd = 0; // number of cache items at tail
270     const bool showCached = props->GetShowCachedItemsValue(false);
271 
272     const int32_t start = info_.startMainLineIndex_ - cacheCount;
273     const int32_t end = info_.endMainLineIndex_ + cacheCount;
274     float mainPos = -info_.GetHeightInRange(start, info_.startMainLineIndex_, mainGap_);
275     for (auto i = start; i <= end; ++i) {
276         const bool inRange = i >= info_.startMainLineIndex_ && i <= info_.endMainLineIndex_;
277         const bool isCache = !showCached && !inRange;
278         const auto& line = info_.gridMatrix_.find(i);
279         if (line == info_.gridMatrix_.end()) {
280             continue;
281         }
282 
283         auto prevLineOffset = OffsetF(0.0f, mainPos, axis_);
284         if (line->second.empty()) {
285             TAG_LOGW(AceLogTag::ACE_GRID, "line %{public}d should not be empty, please check.", line->first);
286             continue;
287         }
288         int32_t itemIdex = -1;
289         float lineHeight = info_.lineHeightMap_[line->first];
290         Alignment align = axis_ == Axis::VERTICAL ? Alignment::TOP_CENTER : Alignment::CENTER_LEFT;
291         if (props->GetPositionProperty()) {
292             align = props->GetPositionProperty()->GetAlignment().value_or(align);
293         }
294         for (auto iter = line->second.begin(); iter != line->second.end(); ++iter) {
295             // If item index is the same, must be the same GridItem, need't layout again.
296             if (itemIdex == iter->second) {
297                 continue;
298             }
299             itemIdex = iter->second;
300             auto crossIter = itemsCrossPosition_.find(itemIdex);
301             if (crossIter == itemsCrossPosition_.end()) {
302                 crossIter = itemsCrossPosition_.emplace(itemIdex, ComputeItemCrossPosition(iter->first)).first;
303             }
304             auto crossOffset = crossIter->second;
305             auto offset = childFrameOffset_ + prevLineOffset;
306             offset = CalculateLargeItemOffset(offset, itemIdex, i, iter->first);
307             if (axis_ == Axis::VERTICAL) {
308                 offset.SetX(crossOffset);
309             } else {
310                 offset.SetY(crossOffset);
311             }
312             auto wrapper = isCache ? layoutWrapper->GetChildByIndex(itemIdex, true)
313                                    : layoutWrapper->GetOrCreateChildByIndex(itemIdex);
314             if (!wrapper) {
315                 continue;
316             }
317             if (inRange) {
318                 startIndex = startIndex == -1 ? itemIdex : std::min(startIndex, itemIdex);
319                 endIndex = std::max(itemIdex, endIndex);
320             }
321             auto frSize = itemsCrossSize_.find(iter->first);
322             if (frSize == itemsCrossSize_.end()) {
323                 continue;
324             }
325             if (i < info_.startMainLineIndex_) {
326                 ++cacheStart;
327             } else if (i > info_.endMainLineIndex_) {
328                 ++cacheEnd;
329             }
330             SizeF blockSize = SizeF(frSize->second, lineHeight, axis_);
331             auto translate = OffsetF(0.0f, 0.0f);
332             auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
333             translate = Alignment::GetAlignPosition(blockSize, childSize, align);
334 
335             if (isRtl) {
336                 offset.SetX(size.Width() - offset.GetX() - childSize.Width());
337             }
338             offset += OffsetF(padding.left.value_or(0.0f), 0.0f);
339             wrapper->GetGeometryNode()->SetMarginFrameOffset(offset + translate);
340             const bool forceLayout = info_.hasMultiLineItem_ || expandSafeArea_ ||
341                                      wrapper->CheckNeedForceMeasureAndLayout() || wrapper->IsIgnoreOptsValid();
342             if (!isCache && forceLayout) {
343                 wrapper->Layout();
344             } else {
345                 SyncGeometry(wrapper);
346             }
347             measuredItems_.erase(itemIdex);
348             auto frameNode = DynamicCast<FrameNode>(wrapper);
349             if (frameNode) {
350                 frameNode->MarkAndCheckNewOpIncNode(axis_);
351             }
352             auto gridItemProp = DynamicCast<GridItemLayoutProperty>(wrapper->GetLayoutProperty());
353             CHECK_NULL_CONTINUE(gridItemProp);
354             gridItemProp->UpdateIndex(itemIdex);
355             gridItemProp->UpdateMainIndex(line->first);
356             gridItemProp->UpdateCrossIndex(iter->first);
357             UpdateRealGridItemPositionInfo(wrapper, line->first, iter->first);
358         }
359         mainPos += info_.lineHeightMap_[line->first] + mainGap_;
360     }
361     info_.totalHeightOfItemsInView_ = info_.GetTotalHeightOfItemsInView(mainGap_);
362 
363     if (startIndex == -1 && endIndex == -1) {
364         startIndex = endIndex = info_.GetChildrenCount();
365     }
366     ClearUnlayoutedItems(layoutWrapper);
367     if (!info_.hasMultiLineItem_) {
368         if (!showCached || !info_.reachEnd_) {
369             auto cache = CalculateCachedCount(layoutWrapper, cacheCount);
370             cacheStart = cache.first;
371             cacheEnd = cache.second; // only use counting method when last line not completely filled
372         }
373         LostChildFocusToSelf(layoutWrapper, startIndex - cacheStart, endIndex + cacheEnd);
374         layoutWrapper->SetActiveChildRange(startIndex, endIndex, cacheStart, cacheEnd, showCached);
375         info_.times_ = (info_.times_ + 1) % GRID_CHECK_INTERVAL;
376         if (info_.times_ == 0) {
377             info_.CheckGridMatrix(cacheCount);
378         }
379     }
380     UpdateOverlay(layoutWrapper);
381 }
382 
ClearUnlayoutedItems(LayoutWrapper * layoutWrapper)383 void GridScrollLayoutAlgorithm::ClearUnlayoutedItems(LayoutWrapper* layoutWrapper)
384 {
385     for (int32_t itemIndex : measuredItems_) {
386         auto wrapper = layoutWrapper->GetChildByIndex(itemIndex);
387         if (!wrapper) {
388             continue;
389         }
390 
391         auto frameNode = AceType::DynamicCast<FrameNode>(wrapper);
392         if (frameNode) {
393             frameNode->ClearSubtreeLayoutAlgorithm();
394         }
395     }
396 }
397 
SyncGeometry(RefPtr<LayoutWrapper> & wrapper)398 void GridScrollLayoutAlgorithm::SyncGeometry(RefPtr<LayoutWrapper>& wrapper)
399 {
400     CHECK_NULL_VOID(wrapper);
401     auto host = wrapper->GetHostNode();
402     CHECK_NULL_VOID(host);
403     host->ForceSyncGeometryNode();
404 }
405 
InitialItemsCrossSize(const RefPtr<GridLayoutProperty> & layoutProperty,const SizeF & frameSize,int32_t childrenCount)406 void GridScrollLayoutAlgorithm::InitialItemsCrossSize(
407     const RefPtr<GridLayoutProperty>& layoutProperty, const SizeF& frameSize, int32_t childrenCount)
408 {
409     itemsCrossSize_.clear();
410     auto rowsTemplate = layoutProperty->GetRowsTemplate().value_or("");
411     auto columnsTemplate = layoutProperty->GetColumnsTemplate().value_or("");
412     axis_ = columnsTemplate.empty() ? Axis::HORIZONTAL : Axis::VERTICAL;
413     auto scale = layoutProperty->GetLayoutConstraint()->scaleProperty;
414     auto rowsGap = ConvertToPx(layoutProperty->GetRowsGap().value_or(0.0_vp), scale, frameSize.Height()).value_or(0);
415     auto columnsGap =
416         ConvertToPx(layoutProperty->GetColumnsGap().value_or(0.0_vp), scale, frameSize.Width()).value_or(0);
417     mainGap_ = axis_ == Axis::HORIZONTAL ? columnsGap : rowsGap;
418     crossGap_ = axis_ == Axis::VERTICAL ? columnsGap : rowsGap;
419     auto padding = layoutProperty->CreatePaddingAndBorder();
420     crossPaddingOffset_ = axis_ == Axis::HORIZONTAL ? padding.top.value_or(0) : 0.0f;
421 
422     auto crossSize = frameSize.CrossSize(axis_);
423     std::vector<double> crossLens;
424     std::pair<std::vector<double>, double> cross;
425     if (!rowsTemplate.empty()) {
426         cross = ParseTemplateArgs(GridUtils::ParseArgs(rowsTemplate), crossSize, crossGap_, childrenCount);
427     } else {
428         cross = ParseTemplateArgs(GridUtils::ParseArgs(columnsTemplate), crossSize, crossGap_, childrenCount);
429     }
430     crossLens = cross.first;
431     crossGap_ = cross.second;
432 
433     if (crossLens.empty()) {
434         crossLens.push_back(crossSize);
435     }
436 
437     if (crossCount_ != crossLens.size()) {
438         crossCount_ = crossLens.size();
439         info_.crossCount_ = static_cast<int32_t>(crossCount_);
440     }
441 
442     int32_t index = 0;
443     for (const auto& len : crossLens) {
444         itemsCrossSize_.try_emplace(index, len);
445         ++index;
446     }
447 }
448 
FillGridViewportAndMeasureChildren(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)449 void GridScrollLayoutAlgorithm::FillGridViewportAndMeasureChildren(
450     float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
451 {
452     auto host = layoutWrapper->GetHostNode();
453     CHECK_NULL_VOID(host);
454     auto gridPattern = host->GetPattern<GridPattern>();
455     CHECK_NULL_VOID(gridPattern);
456     itemsCrossPosition_.clear();
457     UpdateGridLayoutInfo(layoutWrapper, mainSize);
458     if (info_.targetIndex_.has_value()) {
459         // Complete the gridLayoutInfo to get a complete set of data from 0 to targetIndex for the GridView. Make sure
460         // that the index of the matrix_ and heightMap_ is incremented from 0 to targetIndex and sequentially
461         SupplyAllData2ZeroIndex(mainSize, crossSize, layoutWrapper);
462     }
463     if (enableSkipping_) {
464         SkipLargeOffset(mainSize, layoutWrapper);
465     }
466 
467     if (!info_.lastCrossCount_) {
468         info_.lastCrossCount_ = crossCount_;
469         reason_ = GridReloadReason::INIT;
470     }
471 
472     CheckReset(mainSize, crossSize, layoutWrapper);
473 
474     UpdateCurrentOffsetForJumpTo(mainSize);
475     info_.jumpIndex_ = EMPTY_JUMP_INDEX;
476     info_.scrollAlign_ = ScrollAlign::AUTO;
477 
478     // Step1: Measure [GridItem] that has been recorded to [gridMatrix_]
479     float mainLength = MeasureRecordedItems(mainSize, crossSize, layoutWrapper);
480 
481     // Step2: When done measure items in record, request new items to fill blank at end
482     FillBlankAtEnd(mainSize, crossSize, layoutWrapper, mainLength);
483     if (info_.reachEnd_) { // If it reaches end when [FillBlankAtEnd], modify [currentOffset_]
484         ModifyCurrentOffsetWhenReachEnd(mainSize, layoutWrapper);
485     }
486 
487     // Step3: Check if need to fill blank at start (in situation of grid items moving down)
488     auto haveNewLineAtStart = FillBlankAtStart(mainSize, crossSize, layoutWrapper);
489     if (info_.reachStart_) {
490         auto offset = info_.currentOffset_;
491         if ((NonNegative(offset) && !canOverScrollStart_) || (NonPositive(offset) && !canOverScrollEnd_)) {
492             info_.currentOffset_ = 0.0;
493             info_.prevOffset_ = 0.0;
494         }
495         if (!haveNewLineAtStart) {
496             if (canOverScrollStart_) {
497                 info_.UpdateEndIndex(offset, mainSize, mainGap_);
498             }
499             layoutWrapper->GetHostNode()->ChildrenUpdatedFrom(-1);
500             return;
501         }
502         // we need lastline if blank at start is not fully filled when start line is shorter
503         mainLength -= offset;
504         currentMainLineIndex_ = info_.endMainLineIndex_;
505         if (UseCurrentLines(mainSize, crossSize, layoutWrapper, mainLength)) {
506             FillBlankAtEnd(mainSize, crossSize, layoutWrapper, mainLength);
507             if (info_.reachEnd_) {
508                 ModifyCurrentOffsetWhenReachEnd(mainSize, layoutWrapper);
509             }
510         }
511     } else {
512         info_.UpdateEndIndex(info_.currentOffset_, mainSize, mainGap_);
513     }
514     layoutWrapper->GetHostNode()->ChildrenUpdatedFrom(-1);
515     if (info_.targetIndex_.has_value()) {
516         info_.targetIndex_.reset();
517     } else {
518         if (info_.extraOffset_.has_value()) {
519             info_.UpdateStartIndexForExtralOffset(mainGap_, mainSize);
520             ACE_SCOPED_TRACE(
521                 "UpdateStartIndexForExtralOffset startIndex:%d, endIndex:%d, currentOffset:%f, mainSize:%f, mainGap:%f",
522                 info_.startIndex_, info_.endIndex_, info_.currentOffset_, mainSize, mainGap_);
523         }
524     }
525 }
526 
ReloadToStartIndex(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)527 void GridScrollLayoutAlgorithm::ReloadToStartIndex(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
528 {
529     const int32_t currentItemIndex = info_.startIndex_;
530     // adjust startMainLine based on the new cross count
531     UpdateMainLineOnReload(currentItemIndex);
532     auto firstItem = GetStartingItem(layoutWrapper, currentItemIndex);
533     info_.startIndex_ = firstItem;
534     currentMainLineIndex_ = (firstItem == 0 ? 0 : info_.startMainLineIndex_) - 1;
535     info_.endIndex_ = firstItem - 1;
536     TAG_LOGI(AceLogTag::ACE_GRID,
537         "data reload begin, firstItem:%{public}d, currentItemIndex:%{public}d, reason:%{public}s", firstItem,
538         currentItemIndex, GetReloadReasonStr(reason_).c_str());
539     while (info_.endIndex_ < currentItemIndex) {
540         auto lineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, false);
541         if (LessNotEqual(lineHeight, 0.0)) {
542             info_.reachEnd_ = true;
543             break;
544         }
545         if (measureInNextFrame_) {
546             break;
547         }
548     }
549     info_.startMainLineIndex_ = currentMainLineIndex_;
550     info_.UpdateStartIndexByStartLine();
551     // FillNewLineBackward sometimes make startIndex_ > currentItemIndex
552     while (info_.startIndex_ > currentItemIndex &&
553            info_.gridMatrix_.find(info_.startMainLineIndex_) != info_.gridMatrix_.end()) {
554         info_.startMainLineIndex_--;
555         info_.UpdateStartIndexByStartLine();
556     }
557     TAG_LOGI(AceLogTag::ACE_GRID, "data reload end, startIndex_:%{public}d, startMainLineIndex_:%{public}d",
558         info_.startIndex_, info_.startMainLineIndex_);
559     reason_ = GridReloadReason::DATA_RELOAD;
560 }
561 
ReloadFromUpdateIdxToStartIndex(float mainSize,float crossSize,int32_t updateLineIndex,LayoutWrapper * layoutWrapper)562 void GridScrollLayoutAlgorithm::ReloadFromUpdateIdxToStartIndex(
563     float mainSize, float crossSize, int32_t updateLineIndex, LayoutWrapper* layoutWrapper)
564 {
565     const int32_t currentItemIndex = info_.startIndex_;
566     auto firstItem = layoutWrapper->GetHostNode()->GetChildrenUpdated();
567     info_.startIndex_ = firstItem;
568     // first "-1" means trying to fill from last line;second "-1" because it will fill next line in FillNewLineBackward
569     currentMainLineIndex_ = std::max(updateLineIndex - 1, 0) - 1;
570     info_.endIndex_ = firstItem - 1;
571 
572     while (info_.endIndex_ < currentItemIndex) {
573         auto lineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, false);
574         if (LessNotEqual(lineHeight, 0.0)) {
575             info_.reachEnd_ = true;
576             break;
577         }
578     }
579     info_.startMainLineIndex_ = currentMainLineIndex_;
580     info_.UpdateStartIndexByStartLine();
581     // FillNewLineBackward sometimes make startIndex_ > currentItemIndex
582     while (info_.startIndex_ > currentItemIndex &&
583            info_.gridMatrix_.find(info_.startMainLineIndex_) != info_.gridMatrix_.end()) {
584         info_.startMainLineIndex_--;
585         info_.UpdateStartIndexByStartLine();
586     }
587     TAG_LOGI(AceLogTag::ACE_GRID, "data reload end, startIndex_:%{public}d, startMainLineIndex_:%{public}d",
588         info_.startIndex_, info_.startMainLineIndex_);
589 }
590 
FillBlankAtStart(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)591 bool GridScrollLayoutAlgorithm::FillBlankAtStart(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
592 {
593     bool fillNewLine = false;
594     // If [currentOffset_] is non-positive, it means no blank at start
595     if (LessOrEqual(info_.currentOffset_, 0.0)) {
596         return fillNewLine;
597     }
598     auto blankAtStart = info_.currentOffset_;
599     while (GreatNotEqual(blankAtStart, 0.0) || info_.startIndex_ > info_.GetChildrenCount() - 1) {
600         float lineHeight = FillNewLineForward(crossSize, mainSize, layoutWrapper);
601         if (GreatOrEqual(lineHeight, 0.0)) {
602             info_.lineHeightMap_[info_.startMainLineIndex_] = lineHeight;
603             blankAtStart -= (lineHeight + mainGap_);
604             fillNewLine = true;
605             if (measureInNextFrame_) {
606                 break;
607             }
608             continue;
609         }
610         info_.reachStart_ = true;
611         break;
612     }
613 
614     FillOneLineForwardWithoutUpdatingStartIndex(crossSize, mainSize, layoutWrapper);
615 
616     info_.currentOffset_ = blankAtStart;
617     info_.prevOffset_ = info_.currentOffset_;
618     return fillNewLine;
619 }
620 
621 // If there is a multi-line item in the current line and its starting line is not within this line,
622 // it may result in an incomplete layout.
FillOneLineForwardWithoutUpdatingStartIndex(float crossSize,float mainSize,LayoutWrapper * layoutWrapper)623 void GridScrollLayoutAlgorithm::FillOneLineForwardWithoutUpdatingStartIndex(
624     float crossSize, float mainSize, LayoutWrapper* layoutWrapper)
625 {
626     if (info_.gridMatrix_.empty()) {
627         return;
628     }
629     auto startLine = info_.gridMatrix_.find(info_.startMainLineIndex_);
630     if (startLine == info_.gridMatrix_.end() || startLine->second.empty()) {
631         return;
632     }
633     if (startLine->second.size() < crossCount_ && info_.startIndex_ > 0) {
634         auto tempStartIndex = info_.startIndex_;
635         auto tempStartMainLineIndex = info_.startMainLineIndex_;
636         auto tempCurrentMainLineIndex = currentMainLineIndex_;
637         auto tempReachStart = info_.reachStart_;
638 
639         float lineHeight = FillNewLineForward(crossSize, mainSize, layoutWrapper);
640         if (GreatOrEqual(lineHeight, 0.0)) {
641             info_.lineHeightMap_[info_.startMainLineIndex_] = lineHeight;
642         }
643 
644         info_.startIndex_ = tempStartIndex;
645         info_.startMainLineIndex_ = tempStartMainLineIndex;
646         currentMainLineIndex_ = tempCurrentMainLineIndex;
647         info_.reachStart_ = tempReachStart;
648     }
649 }
650 
651 // When a moving up event comes, the [currentOffset_] may have been reduced too much than the items really need to
652 // be moved up, so we need to modify [currentOffset_] according to previous position.
ModifyCurrentOffsetWhenReachEnd(float mainSize,LayoutWrapper * layoutWrapper)653 void GridScrollLayoutAlgorithm::ModifyCurrentOffsetWhenReachEnd(float mainSize, LayoutWrapper* layoutWrapper)
654 {
655     auto host = layoutWrapper->GetHostNode();
656     CHECK_NULL_VOID(host);
657     auto gridPattern = host->GetPattern<GridPattern>();
658     CHECK_NULL_VOID(gridPattern);
659     // use original size in order to add end spacing
660     mainSize -= info_.contentEndPadding_;
661     // Step1. Calculate total length of all items with main gap in viewport.
662     // [lengthOfItemsInViewport] must be greater than or equal to viewport height
663     float lengthOfItemsInViewport = info_.GetTotalHeightOfItemsInView(mainGap_);
664     // scroll forward
665     if (LessNotEqual(info_.prevOffset_, info_.currentOffset_)) {
666         if ((NonNegative(info_.currentOffset_) && !canOverScrollStart_) ||
667             (NonPositive(info_.currentOffset_) && !canOverScrollEnd_)) {
668             info_.reachEnd_ = false;
669             return;
670         } else if (!isChildrenUpdated_) {
671             if (LessNotEqual(lengthOfItemsInViewport, mainSize) && NearEqual(mainSize, info_.lastMainSize_)) {
672                 return;
673             }
674         }
675     }
676     // Step2. Calculate real offset that items can only be moved up by.
677     // Hint: [prevOffset_] is a non-positive value
678     if (LessNotEqual(lengthOfItemsInViewport, mainSize) && info_.startIndex_ == 0) {
679         if (((NonNegative(info_.currentOffset_) && !canOverScrollStart_) ||
680                 (NonPositive(info_.currentOffset_) && !canOverScrollEnd_)) ||
681             isChildrenUpdated_) {
682             info_.currentOffset_ = 0;
683             info_.prevOffset_ = 0;
684         }
685         info_.reachStart_ = true;
686         info_.offsetEnd_ = LessOrEqual(info_.currentOffset_ + lengthOfItemsInViewport, mainSize);
687         return;
688     }
689 
690     // last grid item is not fully showed
691     if (GreatNotEqual(info_.currentOffset_ + lengthOfItemsInViewport, mainSize)) {
692         info_.offsetEnd_ = false;
693         return;
694     }
695 
696     if (info_.hasMultiLineItem_ && info_.endIndex_ == info_.GetChildrenCount() - 1) {
697         if (!CheckLastLineItemFullyShowed(layoutWrapper)) {
698             info_.offsetEnd_ = false;
699             return;
700         }
701     }
702 
703     // Step3. modify [currentOffset_]
704     if (!canOverScrollEnd_) {
705         float realOffsetToMoveUp = lengthOfItemsInViewport - mainSize + info_.prevOffset_;
706         info_.currentOffset_ = info_.prevOffset_ - realOffsetToMoveUp;
707         info_.prevOffset_ = info_.currentOffset_;
708     }
709     info_.offsetEnd_ = true;
710 }
711 
FillBlankAtEnd(float mainSize,float crossSize,LayoutWrapper * layoutWrapper,float & mainLength)712 void GridScrollLayoutAlgorithm::FillBlankAtEnd(
713     float mainSize, float crossSize, LayoutWrapper* layoutWrapper, float& mainLength)
714 {
715     // fill current line first
716     FillCurrentLine(mainSize, crossSize, layoutWrapper);
717 
718     if (measureInNextFrame_) {
719         return;
720     }
721 
722     if (GreatNotEqual(mainLength, mainSize)) {
723         if (IsScrollToEndLine()) {
724             TAG_LOGI(AceLogTag::ACE_GRID, "scroll to end line with index:%{public}d", moveToEndLineIndex_);
725             // scrollToIndex(AUTO) on first layout
726             moveToEndLineIndex_ = -1;
727         }
728         return;
729     }
730     // When [mainLength] is still less than [mainSize], do [FillNewLineBackward] repeatedly until filling up the lower
731     // part of the viewport
732     while (LessNotEqual(mainLength, mainSize)) {
733         float lineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, false);
734         if (GreatOrEqual(lineHeight, 0.0)) {
735             mainLength += (lineHeight + mainGap_);
736             if (!syncLoad_ && layoutWrapper->ReachResponseDeadline()) {
737                 measureInNextFrame_ = true;
738                 break;
739             }
740             continue;
741         }
742         info_.reachEnd_ = true;
743         return;
744     };
745     // last line make LessNotEqual(mainLength, mainSize) and continue is reach end too
746     info_.reachEnd_ = info_.endIndex_ == info_.GetChildrenCount() - 1;
747 }
748 
FillCurrentLine(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)749 void GridScrollLayoutAlgorithm::FillCurrentLine(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
750 {
751     auto mainIter = info_.gridMatrix_.find(currentMainLineIndex_);
752     if (mainIter != info_.gridMatrix_.end() && mainIter->second.size() < crossCount_) {
753         bool doneFillCurrentLine = false;
754         auto currentIndex = info_.endIndex_ + 1;
755         cellAveLength_ = -1.0f;
756         lastCross_ = 0;
757         auto childrenCount = info_.GetChildrenCount();
758         for (uint32_t i = mainIter->second.size(); i < crossCount_; i++) {
759             // Step1. Get wrapper of [GridItem]
760             if (currentIndex >= childrenCount) {
761                 break;
762             }
763             auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
764             if (!itemWrapper) {
765                 break;
766             }
767             // Step2. Measure child
768             auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
769             auto childState = MeasureNewChild(frameSize, currentIndex, layoutWrapper, itemWrapper, false);
770             if (childState == -1) {
771                 cellAveLength_ = LessNotEqual(cellAveLength_, 0.0)
772                                      ? info_.lineHeightMap_.find(currentMainLineIndex_ - 1)->second
773                                      : cellAveLength_;
774                 --currentIndex;
775                 break;
776             }
777             measuredItems_.emplace(currentIndex);
778             i += static_cast<uint32_t>(childState) - 1;
779             // Step3. Measure [GridItem]
780             LargeItemLineHeight(itemWrapper);
781             info_.endIndex_ = currentIndex;
782             currentIndex++;
783             doneFillCurrentLine = true;
784             if (!syncLoad_ && layoutWrapper->ReachResponseDeadline()) {
785                 measureInNextFrame_ = true;
786                 break;
787             }
788         }
789         if (doneFillCurrentLine) {
790             info_.lineHeightMap_[currentMainLineIndex_] = cellAveLength_;
791         }
792     }
793 }
794 
CalculateLargeItemOffset(OffsetF currOffset,int32_t itemIndex,int32_t currLineIndex,int32_t currentCrossIndex)795 OffsetF GridScrollLayoutAlgorithm::CalculateLargeItemOffset(
796     OffsetF currOffset, int32_t itemIndex, int32_t currLineIndex, int32_t currentCrossIndex)
797 {
798     OffsetF offset = currOffset;
799     for (int32_t lastCrossIndex = currLineIndex - 1; lastCrossIndex >= 0; lastCrossIndex--) {
800         auto lastGridMatrixIter = info_.gridMatrix_.find(lastCrossIndex);
801         if (lastGridMatrixIter == info_.gridMatrix_.end()) {
802             continue;
803         }
804         const auto& lastGridItemRecord = lastGridMatrixIter->second;
805         auto lastLineCrossItem = lastGridItemRecord.find(currentCrossIndex);
806         if (lastLineCrossItem == lastGridItemRecord.end()) {
807             continue;
808         }
809         if (lastLineCrossItem->second == itemIndex) {
810             offset -= axis_ == Axis::VERTICAL ? OffsetF(0, info_.lineHeightMap_[lastCrossIndex] + mainGap_)
811                                               : OffsetF(info_.lineHeightMap_[lastCrossIndex] + mainGap_, 0.0);
812         } else {
813             break;
814         }
815     }
816 
817     return offset;
818 }
819 
NeedAdjust(const RefPtr<GridItemLayoutProperty> & itemLayoutProperty)820 bool GridScrollLayoutAlgorithm::NeedAdjust(const RefPtr<GridItemLayoutProperty>& itemLayoutProperty)
821 {
822     bool needAdjust = false;
823     auto main = axis_ == Axis::VERTICAL ? mainCount_ : crossCount_;
824     auto cross = axis_ == Axis::VERTICAL ? crossCount_ : mainCount_;
825     if (itemLayoutProperty->GetRowStart().has_value()) {
826         currentItemRowStart_ = itemLayoutProperty->GetRowStart().value_or(-1);
827         if ((currentItemRowStart_ < 0) || (currentItemRowStart_ >= static_cast<int32_t>(main))) {
828             needAdjust = true;
829         }
830     }
831     if (itemLayoutProperty->GetRowEnd().has_value()) {
832         currentItemRowEnd_ = itemLayoutProperty->GetRowEnd().value_or(-1);
833         if ((currentItemRowEnd_ < 0) || (currentItemRowEnd_ >= static_cast<int32_t>(main))) {
834             needAdjust = true;
835         }
836     }
837     if (itemLayoutProperty->GetColumnStart().has_value()) {
838         currentItemColStart_ = itemLayoutProperty->GetColumnStart().value_or(-1);
839         if ((currentItemColStart_ < 0) || (currentItemColStart_ >= static_cast<int32_t>(cross))) {
840             needAdjust = true;
841         }
842     }
843     if (itemLayoutProperty->GetColumnEnd().has_value()) {
844         currentItemColEnd_ = itemLayoutProperty->GetColumnEnd().value_or(-1);
845         if ((currentItemColEnd_ < 0) || (currentItemColEnd_ >= static_cast<int32_t>(cross))) {
846             needAdjust = true;
847         }
848     }
849     return needAdjust;
850 }
851 
AdjustRowColSpan(const RefPtr<LayoutWrapper> & itemLayoutWrapper,LayoutWrapper *,int32_t)852 void GridScrollLayoutAlgorithm::AdjustRowColSpan(
853     const RefPtr<LayoutWrapper>& itemLayoutWrapper, LayoutWrapper* /* layoutWrapper */, int32_t /* itemIndex */)
854 {
855     auto itemLayoutProperty = DynamicCast<GridItemLayoutProperty>(itemLayoutWrapper->GetLayoutProperty());
856     CHECK_NULL_VOID(itemLayoutProperty);
857     bool needAdjust = false;
858     currentItemRowSpan_ = 1;
859     currentItemColSpan_ = 1;
860     currentItemRowStart_ = -1;
861     currentItemColStart_ = -1;
862     currentItemColEnd_ = -1;
863     currentItemRowEnd_ = -1;
864     needAdjust = NeedAdjust(itemLayoutProperty);
865     if (!needAdjust) {
866         currentItemRowSpan_ = std::max(currentItemRowEnd_ - currentItemRowStart_ + 1, 1);
867         currentItemColSpan_ = std::max(currentItemColEnd_ - currentItemColStart_ + 1, 1);
868     } else {
869         currentItemRowStart_ = -1;
870         currentItemColStart_ = -1;
871         currentItemColEnd_ = -1;
872         currentItemRowEnd_ = -1;
873     }
874     if ((currentItemRowStart_ == -1 && currentItemRowEnd_ != -1) ||
875         (currentItemRowEnd_ == -1 && currentItemRowStart_ != -1) ||
876         (currentItemColStart_ == -1 && currentItemColEnd_ != -1) ||
877         (currentItemColEnd_ == -1 && currentItemColStart_ != -1)) {
878         currentItemRowSpan_ = 1;
879         currentItemColSpan_ = 1;
880         currentItemRowStart_ = -1;
881         currentItemColStart_ = -1;
882         currentItemColEnd_ = -1;
883         currentItemRowEnd_ = -1;
884     }
885     if (currentItemRowSpan_ > 1 || currentItemColSpan_ > 1) {
886         info_.hasBigItem_ = true;
887     }
888     int32_t mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
889     if (mainSpan > 1) {
890         info_.hasMultiLineItem_ = true;
891     }
892 
893     itemLayoutProperty->UpdateRealRowSpan(currentItemRowSpan_);
894     itemLayoutProperty->UpdateRealColumnSpan(currentItemColSpan_);
895 }
896 
LargeItemLineHeight(const RefPtr<LayoutWrapper> & itemWrapper)897 void GridScrollLayoutAlgorithm::LargeItemLineHeight(const RefPtr<LayoutWrapper>& itemWrapper)
898 {
899     AdjustRowColSpan(itemWrapper, nullptr, 0);
900     auto mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
901     auto itemSize = itemWrapper->GetGeometryNode()->GetMarginFrameSize();
902     if (mainSpan == 1) {
903         cellAveLength_ = std::max(GetMainAxisSize(itemSize, info_.axis_), cellAveLength_);
904     }
905 
906     if (mainSpan > 1) {
907         cellAveLength_ =
908             std::max((GetMainAxisSize(itemSize, info_.axis_) - (mainGap_ * (mainSpan - 1))) / mainSpan, cellAveLength_);
909     }
910 }
911 
IsIndexInMatrix(int32_t index,int32_t & startLine)912 bool GridScrollLayoutAlgorithm::IsIndexInMatrix(int32_t index, int32_t& startLine)
913 {
914     auto iter = std::find_if(info_.gridMatrix_.begin(), info_.gridMatrix_.end(),
915         [index, &startLine](const std::pair<int32_t, std::map<int32_t, int32_t>>& item) {
916             for (auto& subitem : item.second) {
917                 if (subitem.second == index) {
918                     startLine = item.first;
919                     return true;
920                 }
921             }
922             return false;
923         });
924     return (iter != info_.gridMatrix_.end());
925 }
926 
GetTargetIndexInfoWithBenchMark(LayoutWrapper * layoutWrapper,bool isTargetBackward,int32_t targetIndex)927 void GridScrollLayoutAlgorithm::GetTargetIndexInfoWithBenchMark(
928     LayoutWrapper* layoutWrapper, bool isTargetBackward, int32_t targetIndex)
929 {
930     int32_t benchmarkIndex =
931         (isTargetBackward && !info_.gridMatrix_.empty()) ? info_.gridMatrix_.rbegin()->second.rbegin()->second + 1 : 0;
932     int32_t mainStartIndex =
933         (isTargetBackward && !info_.gridMatrix_.empty()) ? info_.gridMatrix_.rbegin()->first + 1 : 0;
934     int32_t currentIndex = benchmarkIndex;
935     int32_t headOfMainStartLine = currentIndex;
936     auto childrenCount = info_.GetChildrenCount();
937 
938     while (currentIndex < targetIndex) {
939         int32_t crossGridReserve = info_.crossCount_;
940         /* go through a new line */
941         while ((crossGridReserve > 0) && (currentIndex <= targetIndex)) {
942             if (currentIndex >= childrenCount) {
943                 return;
944             }
945             auto currentWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex, false);
946             CHECK_NULL_VOID(currentWrapper);
947             auto layoutProperty = DynamicCast<GridItemLayoutProperty>(currentWrapper->GetLayoutProperty());
948             CHECK_NULL_VOID(layoutProperty);
949             auto gridSpan = layoutProperty->GetCrossSpan(info_.axis_);
950             if (crossGridReserve >= gridSpan) {
951                 crossGridReserve -= gridSpan;
952             } else if (info_.crossCount_ >= gridSpan) {
953                 ++mainStartIndex;
954                 headOfMainStartLine = currentIndex;
955                 crossGridReserve = info_.crossCount_ - gridSpan;
956             }
957             ++currentIndex;
958         }
959         if (currentIndex > targetIndex) {
960             break;
961         }
962         ++mainStartIndex;
963         headOfMainStartLine = currentIndex;
964     }
965     info_.startMainLineIndex_ = mainStartIndex;
966     info_.startIndex_ = headOfMainStartLine;
967     info_.endIndex_ = headOfMainStartLine - 1;
968     info_.prevOffset_ = 0;
969     info_.currentOffset_ = 0;
970     info_.ResetPositionFlags();
971     info_.gridMatrix_.clear();
972     info_.lineHeightMap_.clear();
973     info_.irregularItemsPosition_.clear();
974 }
975 
UpdateGridLayoutInfo(LayoutWrapper * layoutWrapper,float mainSize)976 void GridScrollLayoutAlgorithm::UpdateGridLayoutInfo(LayoutWrapper* layoutWrapper, float mainSize)
977 {
978     /* 1. Have gotten info_.startMainLineIndex_ and directly jump to it */
979     if (info_.jumpIndex_ < 0 && info_.jumpIndex_ != LAST_ITEM) {
980         return;
981     }
982     auto childrenCount = info_.GetChildrenCount();
983     if (info_.jumpIndex_ == LAST_ITEM) {
984         info_.jumpIndex_ = childrenCount - 1;
985     }
986     /* 2. Need to find out the startMainLineIndex according to startIndex */
987     int32_t targetIndex = info_.jumpIndex_;
988     /* 2.1 invalid targetIndex */
989     childrenCount = HasLayoutOptions(layoutWrapper) ? childrenCount + info_.repeatDifference_ : childrenCount;
990     if (childrenCount <= targetIndex) {
991         return;
992     }
993 
994     canOverScrollStart_ = false;
995     canOverScrollEnd_ = false;
996     switch (info_.scrollAlign_) {
997         case ScrollAlign::START:
998         case ScrollAlign::END:
999         case ScrollAlign::CENTER:
1000             ScrollToIndexStart(layoutWrapper, targetIndex);
1001             return;
1002         default:
1003             ScrollToIndexAuto(layoutWrapper, mainSize, targetIndex);
1004     }
1005 }
1006 
IsScrollToEndLine() const1007 bool GridScrollLayoutAlgorithm::IsScrollToEndLine() const
1008 {
1009     return moveToEndLineIndex_ > 0 && info_.endIndex_ >= moveToEndLineIndex_;
1010 }
1011 
IsEndLineInScreenWithGap(int32_t targetLine,float totalViewHeight,float mainSize) const1012 bool GridScrollLayoutAlgorithm::IsEndLineInScreenWithGap(
1013     int32_t targetLine, float totalViewHeight, float mainSize) const
1014 {
1015     return targetLine == info_.endMainLineIndex_ && LessOrEqual(totalViewHeight + info_.currentOffset_, mainSize);
1016 }
1017 
ScrollToIndexAuto(LayoutWrapper * layoutWrapper,float mainSize,int32_t targetIndex)1018 void GridScrollLayoutAlgorithm::ScrollToIndexAuto(LayoutWrapper* layoutWrapper, float mainSize, int32_t targetIndex)
1019 {
1020     int32_t startLine = 0;
1021     if (IsIndexInMatrix(targetIndex, startLine)) {
1022         auto& info = info_;
1023         if (startLine == info.startMainLineIndex_ && info.startMainLineIndex_ == info.endMainLineIndex_) {
1024             // startLine occupies the whole viewport
1025             return;
1026         }
1027         if (startLine < info_.endMainLineIndex_ && startLine > info_.startMainLineIndex_) {
1028             return;
1029         }
1030 
1031         if (startLine >= info_.endMainLineIndex_) {
1032             auto totalViewHeight = info_.GetTotalHeightOfItemsInView(mainGap_);
1033             if (IsEndLineInScreenWithGap(startLine, totalViewHeight, mainSize)) {
1034                 return;
1035             }
1036             // When ScrollAlign::AUTO and startLine is greater than endMainLineIndex, the effect of
1037             // ScrollToIndex is the same as ScrollAlign::END.
1038             info_.scrollAlign_ = ScrollAlign::END;
1039         }
1040 
1041         // startLine <= info_.startMainLineIndex_
1042         info_.startMainLineIndex_ = startLine;
1043         info_.UpdateStartIndexByStartLine();
1044         info_.prevOffset_ = 0;
1045         info_.currentOffset_ = 0;
1046         info_.ResetPositionFlags();
1047         return;
1048     }
1049 
1050     /* 2.3 targetIndex is out of the matrix */
1051     bool isTargetBackward = true;
1052     if (!info_.gridMatrix_.empty()) {
1053         if (targetIndex < info_.gridMatrix_.begin()->second.begin()->second) {
1054             isTargetBackward = false;
1055         } else if (targetIndex > info_.gridMatrix_.rbegin()->second.rbegin()->second) {
1056             isTargetBackward = true;
1057         } else {
1058             return;
1059         }
1060     }
1061     auto grid = layoutWrapper->GetHostNode();
1062     CHECK_NULL_VOID(grid);
1063     grid->ChildrenUpdatedFrom(0);
1064     reason_ = GridReloadReason::SCROLL_TO_INDEX;
1065     GetTargetIndexInfoWithBenchMark(layoutWrapper, isTargetBackward, targetIndex);
1066     moveToEndLineIndex_ = isTargetBackward ? targetIndex : moveToEndLineIndex_;
1067 }
1068 
ScrollToIndexStart(LayoutWrapper * layoutWrapper,int32_t targetIndex)1069 void GridScrollLayoutAlgorithm::ScrollToIndexStart(LayoutWrapper* layoutWrapper, int32_t targetIndex)
1070 {
1071     int32_t startLine = 0;
1072     /* targetIndex is in the matrix */
1073     if (IsIndexInMatrix(targetIndex, startLine)) {
1074         if (startLine == info_.startMainLineIndex_) {
1075             info_.prevOffset_ = info_.currentOffset_;
1076             info_.currentOffset_ = 0;
1077             info_.ResetPositionFlags();
1078             return;
1079         }
1080 
1081         info_.startMainLineIndex_ = startLine;
1082         info_.UpdateStartIndexByStartLine();
1083         info_.prevOffset_ = 0;
1084         info_.currentOffset_ = 0;
1085         info_.ResetPositionFlags();
1086         return;
1087     }
1088     /* targetIndex is out of the matrix */
1089     bool isTargetBackward = true;
1090     if (!info_.gridMatrix_.empty()) {
1091         if (targetIndex < info_.gridMatrix_.begin()->second.begin()->second) {
1092             isTargetBackward = false;
1093         } else if (targetIndex > info_.gridMatrix_.rbegin()->second.rbegin()->second) {
1094             isTargetBackward = true;
1095         } else {
1096             return;
1097         }
1098     }
1099     auto grid = layoutWrapper->GetHostNode();
1100     CHECK_NULL_VOID(grid);
1101     grid->ChildrenUpdatedFrom(0);
1102     reason_ = GridReloadReason::SCROLL_TO_INDEX;
1103     GetTargetIndexInfoWithBenchMark(layoutWrapper, isTargetBackward, targetIndex);
1104 }
1105 
UpdateCurrentOffsetForJumpTo(float mainSize)1106 void GridScrollLayoutAlgorithm::UpdateCurrentOffsetForJumpTo(float mainSize)
1107 {
1108     if (info_.scrollAlign_ == ScrollAlign::CENTER || info_.scrollAlign_ == ScrollAlign::END) {
1109         int32_t startLine = 0;
1110         /* targetIndex is in the matrix */
1111         if (IsIndexInMatrix(info_.jumpIndex_, startLine)) {
1112             // scroll to end of the screen
1113             info_.currentOffset_ = mainSize - info_.lineHeightMap_[startLine] - info_.contentEndPadding_;
1114             // scroll to center of the screen
1115             if (info_.scrollAlign_ == ScrollAlign::CENTER) {
1116                 info_.currentOffset_ /= 2;
1117             }
1118             info_.prevOffset_ = info_.currentOffset_;
1119         } else {
1120             /* targetIndex is out of the matrix */
1121             TAG_LOGW(AceLogTag::ACE_GRID, "can not find jumpIndex in Grid Matrix :%{public}d", info_.jumpIndex_);
1122         }
1123     }
1124     if (info_.extraOffset_.has_value() && !info_.targetIndex_.has_value()) {
1125         info_.currentOffset_ += info_.extraOffset_.value();
1126         info_.prevOffset_ = info_.currentOffset_;
1127     }
1128 }
1129 
MeasureRecordedItems(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)1130 float GridScrollLayoutAlgorithm::MeasureRecordedItems(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
1131 {
1132     currentMainLineIndex_ = info_.startMainLineIndex_ - 1;
1133     float mainLength = info_.currentOffset_;
1134     // already at start line, do not use offset for mainLength
1135     if (info_.startMainLineIndex_ == 0 && GreatNotEqual(mainLength, 0)) {
1136         mainLength = 0;
1137     }
1138     UseCurrentLines(mainSize, crossSize, layoutWrapper, mainLength);
1139     return mainLength;
1140 }
1141 
1142 namespace {
OneLineMovesOffViewportFromAbove(float mainLength,float lineHeight)1143 inline bool OneLineMovesOffViewportFromAbove(float mainLength, float lineHeight)
1144 {
1145     return LessNotEqual(mainLength, 0.0) || (NearZero(mainLength) && GreatNotEqual(lineHeight, 0.0f));
1146 }
1147 } // namespace
1148 
1149 namespace {
UpdateStartIndexByStartLine(GridLayoutInfo & info_)1150 inline void UpdateStartIndexByStartLine(GridLayoutInfo& info_)
1151 {
1152     auto startLine = info_.gridMatrix_.find(info_.startMainLineIndex_);
1153     if (startLine != info_.gridMatrix_.end() && !startLine->second.empty()) {
1154         info_.startIndex_ = startLine->second.begin()->second;
1155         return;
1156     }
1157 
1158     if (info_.endIndex_ < info_.childrenCount_ - 1) {
1159         info_.startIndex_ = info_.endIndex_ + 1;
1160     }
1161 }
1162 } // namespace
1163 
MeasureExistingLine(int32_t line,float & mainLength,int32_t & endIdx)1164 bool GridScrollLayoutAlgorithm::MeasureExistingLine(int32_t line, float& mainLength, int32_t& endIdx)
1165 {
1166     auto it = info_.gridMatrix_.find(line);
1167     if (it == info_.gridMatrix_.end() || info_.lineHeightMap_.find(line) == info_.lineHeightMap_.end()) {
1168         return false;
1169     }
1170     int32_t idx = -1;
1171     cellAveLength_ = -1.0f;
1172     for (const auto& cell : it->second) {
1173         if (idx == cell.second) {
1174             continue;
1175         }
1176         idx = cell.second;
1177         if (idx == -1) {
1178             // move from another grid
1179             continue;
1180         }
1181         auto item = wrapper_->GetOrCreateChildByIndex(idx);
1182         if (!item) {
1183             break;
1184         }
1185         AdjustRowColSpan(item, wrapper_, idx);
1186         auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1187         if (crossStart == -1) {
1188             MeasureChildPlaced(frameSize_, idx, cell.first, wrapper_, item);
1189         } else {
1190             MeasureChildPlaced(frameSize_, idx, crossStart, wrapper_, item);
1191         }
1192         measuredItems_.emplace(idx);
1193         // Record end index. When fill new line, the [endIndex_] will be the first item index to request
1194         LargeItemLineHeight(item);
1195         endIdx = std::max(idx, endIdx);
1196         info_.endIndex_ = endIdx;
1197     }
1198 
1199     if (NonNegative(cellAveLength_)) { // Means at least one item has been measured
1200         info_.lineHeightMap_[line] = cellAveLength_;
1201         mainLength += cellAveLength_ + mainGap_;
1202     }
1203     // If a line moves up out of viewport, update [startIndex_], [currentOffset_] and [startMainLineIndex_]
1204     if (OneLineMovesOffViewportFromAbove(mainLength, cellAveLength_)) {
1205         info_.currentOffset_ = mainLength;
1206         info_.prevOffset_ = info_.currentOffset_;
1207         info_.startMainLineIndex_ = line + 1;
1208         UpdateStartIndexByStartLine(info_);
1209     }
1210     return true;
1211 }
1212 
UseCurrentLines(float mainSize,float crossSize,LayoutWrapper * layoutWrapper,float & mainLength)1213 bool GridScrollLayoutAlgorithm::UseCurrentLines(
1214     float mainSize, float crossSize, LayoutWrapper* layoutWrapper, float& mainLength)
1215 {
1216     bool runOutOfRecord = false;
1217     // Measure grid items row by row
1218     int32_t tempEndIndex = -1;
1219     while (LessNotEqual(mainLength, mainSize)) {
1220         if (!MeasureExistingLine(++currentMainLineIndex_, mainLength, tempEndIndex)) {
1221             runOutOfRecord = true;
1222             break;
1223         }
1224     }
1225     // Case 1. if this while-loop breaks due to running out of records, the [currentMainLineIndex_] is larger by 1 than
1226     // real main line index, so reduce 1.
1227     // Case 2. if this while-loop stops due to false result of [LessNotEqual(mainLength, mainSize)], the
1228     // [currentMainLineIndex_] is exactly the real main line index. Update [endMainLineIndex_] when the recorded items
1229     // are done measured.
1230     info_.endMainLineIndex_ = runOutOfRecord ? --currentMainLineIndex_ : currentMainLineIndex_;
1231     // reset reachEnd_ if any line at bottom is out of viewport
1232     // last line make LessNotEqual(mainLength, mainSize) and continue is reach end too
1233     info_.reachEnd_ = info_.endIndex_ == info_.GetChildrenCount() - 1;
1234     if (info_.reachEnd_) {
1235         if (LessNotEqual(info_.GetTotalHeightOfItemsInView(mainGap_) + info_.currentOffset_, mainSize)) {
1236             // skip clearing cache if overScrolling
1237             return runOutOfRecord;
1238         }
1239     } else {
1240         info_.offsetEnd_ = false;
1241     }
1242     return runOutOfRecord;
1243 }
1244 
SkipLargeOffset(float mainSize,LayoutWrapper * layoutWrapper)1245 void GridScrollLayoutAlgorithm::SkipLargeOffset(float mainSize, LayoutWrapper* layoutWrapper)
1246 {
1247     auto gridLayoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
1248     CHECK_NULL_VOID(gridLayoutProperty);
1249     auto cacheCount = gridLayoutProperty->GetCachedCountValue(info_.defCachedCount_);
1250     cacheCount = std::max(cacheCount, 1);
1251     SkipForwardLines(cacheCount * mainSize, layoutWrapper);
1252     SkipBackwardLines(cacheCount * mainSize, layoutWrapper);
1253 }
1254 
SkipForwardLines(float mainSize,LayoutWrapper * layoutWrapper)1255 void GridScrollLayoutAlgorithm::SkipForwardLines(float mainSize, LayoutWrapper* layoutWrapper)
1256 {
1257     if (!GreatOrEqual(info_.currentOffset_ - info_.prevOffset_, mainSize)) {
1258         return;
1259     }
1260     // skip lines in matrix
1261     while (GreatOrEqual(info_.currentOffset_, mainSize)) {
1262         auto line = info_.gridMatrix_.find(info_.startMainLineIndex_ - 1);
1263         if (line == info_.gridMatrix_.end()) {
1264             break;
1265         }
1266         auto lineHeight = info_.lineHeightMap_.find(info_.startMainLineIndex_ - 1);
1267         if (lineHeight == info_.lineHeightMap_.end()) {
1268             break;
1269         }
1270         --info_.startMainLineIndex_;
1271         info_.startIndex_ = line->second.begin()->second;
1272         info_.currentOffset_ -= lineHeight->second + mainGap_;
1273         info_.currentHeight_ -= lineHeight->second + mainGap_;
1274     }
1275 
1276     // skip lines not in matrix
1277     if (GreatOrEqual(info_.currentOffset_, mainSize) && info_.startIndex_ > 0) {
1278         if (!info_.hasBigItem_) {
1279             SkipRegularLines(true);
1280         } else {
1281             SkipIrregularLines(layoutWrapper, true);
1282         }
1283         info_.startIndex_ = std::clamp(info_.startIndex_, 0, info_.GetChildrenCount() - 1);
1284         TAG_LOGI(AceLogTag::ACE_GRID, "estimatedIndex:%{public}d", info_.startIndex_);
1285         auto grid = layoutWrapper->GetHostNode();
1286         CHECK_NULL_VOID(grid);
1287         grid->ChildrenUpdatedFrom(0);
1288         reason_ = GridReloadReason::SKIP_LARGE_OFFSET;
1289     }
1290 }
1291 
SkipBackwardLines(float mainSize,LayoutWrapper * layoutWrapper)1292 void GridScrollLayoutAlgorithm::SkipBackwardLines(float mainSize, LayoutWrapper* layoutWrapper)
1293 {
1294     if (!GreatOrEqual(info_.prevOffset_ - info_.currentOffset_, mainSize)) {
1295         return;
1296     }
1297 
1298     if (!SkipLargeLineHeightLines(mainSize)) {
1299         return;
1300     }
1301 
1302     // grid size change from big to small
1303     info_.UpdateEndLine(mainSize, mainGap_);
1304 
1305     // skip lines in matrix
1306     while (GreatOrEqual(-info_.currentOffset_, mainSize)) {
1307         auto line = info_.gridMatrix_.find(info_.endMainLineIndex_ + 1);
1308         if (line == info_.gridMatrix_.end()) {
1309             break;
1310         }
1311         auto lineHeight = info_.lineHeightMap_.find(info_.endMainLineIndex_ + 1);
1312         if (lineHeight == info_.lineHeightMap_.end()) {
1313             break;
1314         }
1315         ++info_.startMainLineIndex_;
1316         ++info_.endMainLineIndex_;
1317         info_.currentOffset_ += lineHeight->second + mainGap_;
1318         info_.currentHeight_ += lineHeight->second + mainGap_;
1319     }
1320     info_.UpdateStartIndexByStartLine();
1321 
1322     // skip lines not in matrix
1323     if (GreatOrEqual(-info_.currentOffset_, mainSize)) {
1324         if (!info_.hasBigItem_) {
1325             SkipRegularLines(false);
1326         } else {
1327             SkipIrregularLines(layoutWrapper, false);
1328         }
1329         info_.startIndex_ = std::clamp(info_.startIndex_, 0, info_.GetChildrenCount() - 1);
1330         TAG_LOGI(AceLogTag::ACE_GRID, "estimatedIndex:%{public}d, currentOffset:%{public}f", info_.startIndex_,
1331             info_.currentOffset_);
1332         auto grid = layoutWrapper->GetHostNode();
1333         CHECK_NULL_VOID(grid);
1334         grid->ChildrenUpdatedFrom(0);
1335         reason_ = GridReloadReason::SKIP_LARGE_OFFSET;
1336     }
1337 }
1338 
SkipRegularLines(bool forward)1339 void GridScrollLayoutAlgorithm::SkipRegularLines(bool forward)
1340 {
1341     auto lineHeight = info_.GetAverageLineHeight() + mainGap_;
1342     if (LessOrEqual(lineHeight, 0.0)) {
1343         return;
1344     }
1345     int32_t estimatedLines = info_.currentOffset_ / lineHeight;
1346     if (forward && info_.startIndex_ < estimatedLines * static_cast<int32_t>(crossCount_)) {
1347         info_.startIndex_ = 0;
1348         info_.currentOffset_ = 0;
1349     } else {
1350         info_.startIndex_ -= estimatedLines * static_cast<int32_t>(crossCount_);
1351         info_.currentOffset_ -= lineHeight * estimatedLines;
1352     }
1353 }
1354 
SkipIrregularLines(LayoutWrapper * layoutWrapper,bool forward)1355 void GridScrollLayoutAlgorithm::SkipIrregularLines(LayoutWrapper* layoutWrapper, bool forward)
1356 {
1357     auto grid = layoutWrapper->GetHostNode();
1358     CHECK_NULL_VOID(grid);
1359     auto pattern = grid->GetPattern<GridPattern>();
1360     CHECK_NULL_VOID(pattern);
1361     auto averageHeight = pattern->GetAverageHeight();
1362     if (LessOrEqual(averageHeight, 0.0)) {
1363         return;
1364     }
1365     int32_t estimatedIndex = (info_.currentOffset_) / averageHeight;
1366     info_.startIndex_ = std::min(info_.startIndex_ - estimatedIndex, info_.GetChildrenCount());
1367     info_.currentOffset_ = info_.prevOffset_;
1368 }
1369 
FillNewLineForward(float crossSize,float mainSize,LayoutWrapper * layoutWrapper)1370 float GridScrollLayoutAlgorithm::FillNewLineForward(float crossSize, float mainSize, LayoutWrapper* layoutWrapper)
1371 {
1372     // To make the code more convenient to read, we name a param in situation of vertical, for example:
1373     // 1. [lineHight] means height of a row when the Grid is vertical;
1374     // 2. [lineHight] means width of a column when the Grid is horizontal;
1375     // Other params are also named according to this principle.
1376     cellAveLength_ = -1.0f;
1377     auto currentIndex = info_.startIndex_;
1378     if (info_.startMainLineIndex_ - 1 < 0) {
1379         if (currentIndex == 0) {
1380             return cellAveLength_;
1381         }
1382         // add more than one line
1383         UpdateMatrixForAddedItems();
1384     }
1385     info_.startMainLineIndex_--;
1386     bool doneCreateNewLine = false;
1387     auto gridMatrixIter = info_.gridMatrix_.find(info_.startMainLineIndex_);
1388     if (gridMatrixIter == info_.gridMatrix_.end()) {
1389         AddForwardLines(currentIndex, crossSize, mainSize, layoutWrapper);
1390     }
1391     gridMatrixIter = info_.gridMatrix_.find(info_.startMainLineIndex_);
1392     if (gridMatrixIter == info_.gridMatrix_.end()) {
1393         return cellAveLength_;
1394     }
1395 
1396     // need to obtain the item node in order and by step one in LazyLayoutWrapperBuilder::OnGetOrCreateWrapperByIndex
1397     for (auto itemIter = gridMatrixIter->second.rbegin(); itemIter != gridMatrixIter->second.rend(); ++itemIter) {
1398         currentIndex = itemIter->second;
1399 
1400         // Step1. Get wrapper of [GridItem]
1401         auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1402         if (!itemWrapper) {
1403             break;
1404         }
1405         // Step2. Measure child
1406         auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
1407         AdjustRowColSpan(itemWrapper, layoutWrapper, currentIndex);
1408         auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1409         if (crossStart == -1) {
1410             MeasureChildPlaced(frameSize, currentIndex, itemIter->first, layoutWrapper, itemWrapper);
1411         } else {
1412             MeasureChildPlaced(frameSize, currentIndex, crossStart, layoutWrapper, itemWrapper);
1413         }
1414         measuredItems_.emplace(currentIndex);
1415         // Step3. Measure [GridItem]
1416         LargeItemLineHeight(itemWrapper);
1417         info_.startIndex_ = currentIndex;
1418     }
1419 
1420     doneCreateNewLine = GreatOrEqual(cellAveLength_, 0.0);
1421     // If it fails to create new line when [FillNewLineForward] is called, it means that it reaches start
1422     info_.reachStart_ = !doneCreateNewLine;
1423 
1424     return cellAveLength_;
1425 }
1426 
UpdateMatrixForAddedItems()1427 void GridScrollLayoutAlgorithm::UpdateMatrixForAddedItems()
1428 {
1429     decltype(info_.lineHeightMap_) gridLineHeightMap(std::move(info_.lineHeightMap_));
1430     decltype(info_.gridMatrix_) gridMatrix(std::move(info_.gridMatrix_));
1431     for (const auto& item : gridMatrix) {
1432         info_.gridMatrix_[item.first + 1] = item.second;
1433     }
1434     for (const auto& item : gridLineHeightMap) {
1435         info_.lineHeightMap_[item.first + 1] = item.second;
1436     }
1437     info_.startMainLineIndex_ = info_.startMainLineIndex_ + 1;
1438     info_.endMainLineIndex_ = info_.endMainLineIndex_ + 1;
1439     TAG_LOGI(AceLogTag::ACE_GRID, "add more than one line startMainLineIndex_:%{public}d", info_.startMainLineIndex_);
1440 }
1441 
AddForwardLines(int32_t currentIndex,float crossSize,float mainSize,LayoutWrapper * layoutWrapper)1442 void GridScrollLayoutAlgorithm::AddForwardLines(
1443     int32_t currentIndex, float crossSize, float mainSize, LayoutWrapper* layoutWrapper)
1444 {
1445     auto endMainLineIndex = info_.endMainLineIndex_;
1446     auto endIndex = info_.endIndex_;
1447     auto firstItem = GetStartingItem(layoutWrapper, currentIndex - 1);
1448     auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(firstItem);
1449     CHECK_NULL_VOID(itemWrapper);
1450     AdjustRowColSpan(itemWrapper, layoutWrapper, firstItem);
1451     auto mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
1452     auto measureNumber = 0;
1453     currentMainLineIndex_ = (firstItem == 0 ? 0 : info_.startMainLineIndex_) - 1;
1454     info_.endIndex_ = firstItem - 1;
1455     // firstItem may be more than one line ahead, use new matrix to save and merge to old matrix
1456     decltype(info_.lineHeightMap_) gridLineHeightMap(std::move(info_.lineHeightMap_));
1457     decltype(info_.gridMatrix_) gridMatrix(std::move(info_.gridMatrix_));
1458     bool addLine = false;
1459     float newLineHeight = -1.0f;
1460     while (info_.endIndex_ < currentIndex - 1 || mainSpan > measureNumber) {
1461         newLineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, true);
1462         measureNumber++;
1463         if (LessNotEqual(newLineHeight, 0.0)) {
1464             info_.reachEnd_ = true;
1465             break;
1466         }
1467         addLine = true;
1468         if (!syncLoad_ && layoutWrapper->ReachResponseDeadline()) {
1469             measureInNextFrame_ = true;
1470             break;
1471         }
1472     }
1473     if (!addLine) {
1474         return;
1475     }
1476     // merge matrix
1477     auto forwardLines = info_.endMainLineIndex_ - info_.startMainLineIndex_;
1478     if (forwardLines >= 0) {
1479         auto begin = info_.gridMatrix_.begin()->first;
1480         if (info_.endMainLineIndex_ - begin <= begin) {
1481             for (auto i = begin; i <= info_.endMainLineIndex_; i++) {
1482                 gridMatrix.emplace(i - forwardLines, std::move(info_.gridMatrix_[i]));
1483                 gridLineHeightMap.emplace(i - forwardLines, info_.lineHeightMap_[i]);
1484             }
1485             gridMatrix.swap(info_.gridMatrix_);
1486             gridLineHeightMap.swap(info_.lineHeightMap_);
1487 
1488             MergeRemainingLines(gridMatrix, forwardLines);
1489         } else {
1490             for (auto i = info_.startMainLineIndex_ + 1; i <= gridMatrix.rbegin()->first; i++) {
1491                 info_.gridMatrix_.emplace(forwardLines + i, std::move(gridMatrix[i]));
1492                 info_.lineHeightMap_.emplace(forwardLines + i, gridLineHeightMap[i]);
1493             }
1494         }
1495     } else {
1496         // delete more than one line items
1497         for (auto i = info_.startMainLineIndex_ + 1; i <= gridMatrix.rbegin()->first; i++) {
1498             info_.gridMatrix_.emplace(forwardLines + i, std::move(gridMatrix[i]));
1499             info_.lineHeightMap_.emplace(forwardLines + i, gridLineHeightMap[i]);
1500         }
1501     }
1502 
1503     info_.startMainLineIndex_ = info_.endMainLineIndex_ - (forwardLines > 0 ? forwardLines : 0);
1504     info_.endMainLineIndex_ = endMainLineIndex + (forwardLines < 0 ? forwardLines : 0);
1505     info_.endIndex_ = endIndex;
1506     TAG_LOGI(AceLogTag::ACE_GRID,
1507         "after load forward:start main line %{public}d end main line %{public}d, new line height:%{public}f, "
1508         "gridMainSize:%{public}f",
1509         info_.startMainLineIndex_, info_.endMainLineIndex_, newLineHeight, mainSize);
1510 }
1511 
FillNewLineBackward(float crossSize,float mainSize,LayoutWrapper * layoutWrapper,bool reverse)1512 float GridScrollLayoutAlgorithm::FillNewLineBackward(
1513     float crossSize, float mainSize, LayoutWrapper* layoutWrapper, bool reverse)
1514 {
1515     // To make the code more convenient to read, we name a param in situation of vertical, for example:
1516     // 1. [lineHight] means height of a row when the Grid is vertical;
1517     // 2. [lineHight] means width of a column when the Grid is horizontal;
1518     // Other params are also named according to this principle.
1519     cellAveLength_ = -1.0f;
1520     if (IsScrollToEndLine()) {
1521         TAG_LOGI(AceLogTag::ACE_GRID, "scroll to end line with index:%{public}d", moveToEndLineIndex_);
1522         // scrollToIndex(AUTO) on first layout
1523         moveToEndLineIndex_ = -1;
1524         return cellAveLength_;
1525     }
1526     auto currentIndex = info_.endIndex_ + 1;
1527     currentMainLineIndex_++; // if it fails to fill a new line backward, do [currentMainLineIndex_--]
1528     if (info_.gridMatrix_.find(currentMainLineIndex_) != info_.gridMatrix_.end()) {
1529         cellAveLength_ = info_.lineHeightMap_.find(currentMainLineIndex_ - 1)->second;
1530     }
1531     lastCross_ = 0;
1532     bool doneFillLine = false;
1533 
1534     for (uint32_t i = 0; i < crossCount_; i++) {
1535         // already finish first line forward
1536         if ((reverse && currentIndex >= info_.startIndex_)) {
1537             break;
1538         }
1539         if (currentIndex >= info_.GetChildrenCount()) {
1540             if (currentIndex == 0) {
1541                 layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1542                 TAG_LOGW(ACE_GRID, "total item count is 0");
1543             }
1544             break;
1545         }
1546         // Step1. Get wrapper of [GridItem]
1547         auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1548         if (!itemWrapper) {
1549             if (currentIndex < info_.GetChildrenCount()) {
1550                 TAG_LOGW(ACE_GRID, "can not get item at:%{public}d, total items:%{public}d", currentIndex,
1551                     info_.GetChildrenCount());
1552                 std::string subErrorType = "can not get item at:" + std::to_string(currentIndex) +
1553                                            ", total items:" + std::to_string(info_.GetChildrenCount());
1554                 EventReport::ReportScrollableErrorEvent("Grid", ScrollableErrorType::GET_CHILD_FAILED, subErrorType);
1555             }
1556             LargeItemNextLineHeight(currentMainLineIndex_, layoutWrapper);
1557             break;
1558         }
1559         // Step2. Measure child
1560         auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
1561         auto crossSpan = MeasureNewChild(frameSize, currentIndex, layoutWrapper, itemWrapper, false);
1562         if (crossSpan < 0) {
1563             cellAveLength_ = LessNotEqual(cellAveLength_, 0.0)
1564                                  ? info_.lineHeightMap_.find(currentMainLineIndex_ - 1)->second
1565                                  : cellAveLength_;
1566             --currentIndex;
1567             break;
1568         }
1569         measuredItems_.emplace(currentIndex);
1570         i = static_cast<uint32_t>(lastCross_ - 1);
1571         // Step3. Measure [GridItem]
1572         LargeItemLineHeight(itemWrapper);
1573 
1574         info_.endIndex_ = currentIndex;
1575         currentIndex++;
1576         doneFillLine = true;
1577         if (!reverse && !syncLoad_ && layoutWrapper->ReachResponseDeadline()) {
1578             measureInNextFrame_ = true;
1579             break;
1580         }
1581     }
1582 
1583     if (doneFillLine || info_.gridMatrix_.find(currentMainLineIndex_) != info_.gridMatrix_.end()) {
1584         info_.lineHeightMap_[currentMainLineIndex_] = cellAveLength_;
1585         info_.endMainLineIndex_ = currentMainLineIndex_;
1586     } else {
1587         currentMainLineIndex_--;
1588     }
1589     return cellAveLength_;
1590 }
1591 
LargeItemNextLineHeight(int32_t currentLineIndex,LayoutWrapper * layoutWrapper)1592 void GridScrollLayoutAlgorithm::LargeItemNextLineHeight(int32_t currentLineIndex, LayoutWrapper* layoutWrapper)
1593 {
1594     auto gridMatrixIter = info_.gridMatrix_.find(currentLineIndex);
1595     auto currentIndex = 0;
1596     if (gridMatrixIter != info_.gridMatrix_.end()) {
1597         for (auto itemIter = gridMatrixIter->second.rbegin(); itemIter != gridMatrixIter->second.rend(); ++itemIter) {
1598             currentIndex = itemIter->second;
1599             auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1600             if (!itemWrapper) {
1601                 break;
1602             }
1603             LargeItemLineHeight(itemWrapper);
1604         }
1605     }
1606 }
1607 
LargeItemForwardLineHeight(int32_t currentLineIndex,LayoutWrapper * layoutWrapper)1608 void GridScrollLayoutAlgorithm::LargeItemForwardLineHeight(int32_t currentLineIndex, LayoutWrapper* layoutWrapper)
1609 {
1610     auto lineIndex = currentLineIndex;
1611     auto gridMatrixIter = info_.gridMatrix_.find(lineIndex);
1612     if (gridMatrixIter == info_.gridMatrix_.end()) {
1613         return;
1614     }
1615     auto currentIndex = -1;
1616 
1617     lineIndex = CalculateLineIndexForLargeItem(gridMatrixIter, currentIndex, lineIndex, layoutWrapper);
1618     CalculateLineHeightForLargeItem(lineIndex, currentLineIndex, gridMatrixIter, layoutWrapper);
1619 }
1620 
CalculateLineIndexForLargeItem(std::map<int32_t,std::map<int32_t,int32_t>>::iterator gridMatrixIter,int32_t currentIndex,int32_t lineIndex,LayoutWrapper * layoutWrapper)1621 int32_t GridScrollLayoutAlgorithm::CalculateLineIndexForLargeItem(
1622     std::map<int32_t, std::map<int32_t, int32_t>>::iterator gridMatrixIter, int32_t currentIndex, int32_t lineIndex,
1623     LayoutWrapper* layoutWrapper)
1624 {
1625     for (const auto& gridItemRecord : gridMatrixIter->second) {
1626         if (currentIndex == gridItemRecord.second || gridItemRecord.second == -1) {
1627             continue;
1628         }
1629         currentIndex = gridItemRecord.second;
1630         auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1631         if (!itemWrapper) {
1632             break;
1633         }
1634         AdjustRowColSpan(itemWrapper, layoutWrapper, currentIndex);
1635         for (int32_t lastCrossIndex = lineIndex - 1; lastCrossIndex >= 0; lastCrossIndex--) {
1636             auto lastGridMatrixIter = info_.gridMatrix_.find(lastCrossIndex);
1637             if (lastGridMatrixIter == info_.gridMatrix_.end()) {
1638                 continue;
1639             }
1640             auto lastGridItemRecord = lastGridMatrixIter->second;
1641             auto lastLineCrossItem = lastGridItemRecord.find(gridItemRecord.first);
1642             if (lastLineCrossItem == lastGridItemRecord.end()) {
1643                 continue;
1644             }
1645             if (lastLineCrossItem->second == currentIndex) {
1646                 lineIndex--;
1647             } else {
1648                 break;
1649             }
1650         }
1651     }
1652     return lineIndex;
1653 }
1654 
CalculateLineHeightForLargeItem(int32_t lineIndex,int32_t currentLineIndex,std::map<int32_t,std::map<int32_t,int32_t>>::iterator gridMatrixIter,LayoutWrapper * layoutWrapper)1655 void GridScrollLayoutAlgorithm::CalculateLineHeightForLargeItem(int32_t lineIndex, int32_t currentLineIndex,
1656     std::map<int32_t, std::map<int32_t, int32_t>>::iterator gridMatrixIter, LayoutWrapper* layoutWrapper)
1657 {
1658     for (int32_t i = lineIndex; i <= currentLineIndex; i++) {
1659         auto currentGridMatrixIter = info_.gridMatrix_.find(i);
1660         if (currentGridMatrixIter == info_.gridMatrix_.end()) {
1661             continue;
1662         }
1663         auto currentIndex = 0;
1664         cellAveLength_ = -1.0f;
1665         for (auto itemIter = gridMatrixIter->second.rbegin(); itemIter != gridMatrixIter->second.rend(); ++itemIter) {
1666             if (currentIndex == itemIter->second) {
1667                 continue;
1668             }
1669             currentIndex = itemIter->second;
1670             auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1671             if (!itemWrapper) {
1672                 break;
1673             }
1674             LargeItemLineHeight(itemWrapper);
1675             auto line = info_.lineHeightMap_.find(i);
1676             if (line == info_.lineHeightMap_.end() || line->second < cellAveLength_) {
1677                 info_.lineHeightMap_[i] = cellAveLength_;
1678             }
1679         }
1680     }
1681 }
1682 
CreateChildConstraint(float mainSize,float crossSize,const RefPtr<GridLayoutProperty> & gridLayoutProperty,int32_t crossStart,int32_t crossSpan) const1683 LayoutConstraintF GridScrollLayoutAlgorithm::CreateChildConstraint(float mainSize, float crossSize,
1684     const RefPtr<GridLayoutProperty>& gridLayoutProperty, int32_t crossStart, int32_t crossSpan) const
1685 {
1686     float itemMainSize =
1687         gridLayoutProperty->IsConfiguredScrollable() ? Infinity<float>() : mainSize / static_cast<float>(mainCount_);
1688 
1689     auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
1690     float itemCrossSize = GridUtils::GetCrossGap(gridLayoutProperty, frameSize, axis_) * (crossSpan - 1);
1691     for (int32_t index = 0; index < crossSpan; ++index) {
1692         int32_t crossIndex = (crossStart + index) % static_cast<int32_t>(crossCount_);
1693         if (crossIndex >= 0 && crossIndex < static_cast<int32_t>(itemsCrossSize_.size())) {
1694             itemCrossSize += GetOrDefault(itemsCrossSize_, crossIndex, 0.0f);
1695         }
1696     }
1697 
1698     SizeF itemIdealSize =
1699         gridLayoutProperty->IsVertical() ? SizeF(itemCrossSize, itemMainSize) : SizeF(itemMainSize, itemCrossSize);
1700     auto itemConstraint = gridLayoutProperty->CreateChildConstraint();
1701 
1702     // The percent size of GridItem is based on the fraction size, for e.g., if a GridItem has width of "50%" in Grid
1703     // configured with columnsTemplate = "1fr 1fr", rowsTemplate = "1fr 1fr",
1704     // then the GridItem width = [width of 1fr] * 50%,
1705     // [itemFractionCount] is now only in direction of cross axis
1706     float widthPercentBase =
1707         GreatOrEqual(crossCount_, Infinity<uint32_t>()) ? itemConstraint.percentReference.Width() : itemCrossSize;
1708     float heightPercentBase = GreatOrEqual(mainCount_, Infinity<uint32_t>())
1709                                   ? itemConstraint.percentReference.Height()
1710                                   : itemConstraint.percentReference.Height() / static_cast<float>(mainCount_);
1711     if (axis_ == Axis::VERTICAL) {
1712         itemConstraint.percentReference = SizeF(widthPercentBase, itemConstraint.percentReference.Height());
1713     } else {
1714         itemConstraint.percentReference = SizeF(itemConstraint.percentReference.Width(), heightPercentBase);
1715     }
1716     itemConstraint.maxSize = itemIdealSize;
1717     itemConstraint.parentIdealSize.SetCrossSize(itemCrossSize, axis_);
1718     itemConstraint.UpdateIllegalSelfMarginSizeWithCheck(axis_ == Axis::VERTICAL
1719                                                             ? OptionalSizeF(itemCrossSize, std::nullopt)
1720                                                             : OptionalSizeF(std::nullopt, itemCrossSize));
1721     return itemConstraint;
1722 }
1723 
GetNextGrid(int32_t & curMain,int32_t & curCross,bool reverse) const1724 bool GridScrollLayoutAlgorithm::GetNextGrid(int32_t& curMain, int32_t& curCross, bool reverse) const
1725 {
1726     if (!reverse) {
1727         ++curCross;
1728         if (curCross >= static_cast<int32_t>(crossCount_)) {
1729             return false;
1730         }
1731         return true;
1732     }
1733 
1734     --curCross;
1735     if (curCross < 0) {
1736         return false;
1737     }
1738     return true;
1739 }
1740 
MeasureNewChild(const SizeF & frameSize,int32_t itemIndex,LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & childLayoutWrapper,bool reverse)1741 int32_t GridScrollLayoutAlgorithm::MeasureNewChild(const SizeF& frameSize, int32_t itemIndex,
1742     LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& childLayoutWrapper, bool reverse)
1743 {
1744     auto mainCount = static_cast<int32_t>(mainCount_);
1745     auto crossCount = static_cast<int32_t>(crossCount_);
1746     AdjustRowColSpan(childLayoutWrapper, layoutWrapper, itemIndex);
1747     auto mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
1748     auto crossSpan = axis_ == Axis::VERTICAL ? currentItemColSpan_ : currentItemRowSpan_;
1749     auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1750     if (crossSpan > crossCount) {
1751         TAG_LOGW(AceLogTag::ACE_GRID,
1752             "item %{public}d can not be placed in grid: cross count:%{public}d, cross span:%{public}d", itemIndex,
1753             crossCount, crossSpan);
1754         return crossSpan;
1755     }
1756     int32_t mainIndex = currentMainLineIndex_;
1757 
1758     if (crossStart >= 0 && crossStart < crossCount) {
1759         if (crossStart < lastCross_) {
1760             return -1;
1761         } else if (CheckGridPlaced(itemIndex, mainIndex, crossStart, mainSpan, crossSpan)) {
1762             MeasureChild(layoutWrapper, frameSize, childLayoutWrapper, crossStart, crossSpan);
1763             itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(crossStart));
1764         } else {
1765             return -1;
1766         }
1767     } else {
1768         int32_t crossIndex = crossStart >= 0 ? crossStart : lastCross_;
1769 
1770         while (!CheckGridPlaced(itemIndex, mainIndex, crossIndex, mainSpan, crossSpan)) {
1771             if (GetNextGrid(mainIndex, crossIndex, reverse) == false) {
1772                 return -1;
1773             }
1774             if (mainIndex >= mainCount || crossIndex >= crossCount) {
1775                 break;
1776             }
1777         }
1778 
1779         MeasureChild(layoutWrapper, frameSize, childLayoutWrapper, crossIndex, crossSpan);
1780         itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(crossIndex));
1781     }
1782     if (mainSpan > 1 || crossSpan > 1) {
1783         for (int i = mainIndex; i < mainSpan; i++) {
1784             info_.irregularLines_[i] = true;
1785         }
1786     }
1787     return crossSpan;
1788 }
1789 
MeasureChildPlaced(const SizeF & frameSize,int32_t itemIndex,int32_t crossStart,LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & childLayoutWrapper)1790 int32_t GridScrollLayoutAlgorithm::MeasureChildPlaced(const SizeF& frameSize, int32_t itemIndex, int32_t crossStart,
1791     LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& childLayoutWrapper)
1792 {
1793     AdjustRowColSpan(childLayoutWrapper, layoutWrapper, itemIndex);
1794     auto crossSpan = axis_ == Axis::VERTICAL ? currentItemColSpan_ : currentItemRowSpan_;
1795     if (static_cast<uint32_t>(crossStart + crossSpan) > crossCount_) {
1796         TAG_LOGI(AceLogTag::ACE_GRID, "item %{public}d cross not enough, start:%{public}d, span:%{public}d", itemIndex,
1797             crossStart, crossSpan);
1798         return 0;
1799     }
1800     auto mainSpan = axis_ == Axis::HORIZONTAL ? currentItemColSpan_ : currentItemRowSpan_;
1801     if (crossSpan > 1 || mainSpan > 1) {
1802         for (int i = currentMainLineIndex_; i < mainSpan; i++) {
1803             info_.irregularLines_[i] = true;
1804         }
1805     }
1806 
1807     MeasureChild(layoutWrapper, frameSize, childLayoutWrapper, crossStart, crossSpan);
1808     itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(crossStart));
1809     return crossSpan;
1810 }
1811 
CheckNeedMeasure(const RefPtr<LayoutWrapper> & layoutWrapper,const LayoutConstraintF & layoutConstraint) const1812 bool GridScrollLayoutAlgorithm::CheckNeedMeasure(
1813     const RefPtr<LayoutWrapper>& layoutWrapper, const LayoutConstraintF& layoutConstraint) const
1814 {
1815     auto childLayoutProperty = AceType::DynamicCast<GridItemLayoutProperty>(layoutWrapper->GetLayoutProperty());
1816     if (childLayoutProperty && childLayoutProperty->GetNeedStretch() && info_.clearStretch_) {
1817         childLayoutProperty->SetNeedStretch(false);
1818         if (axis_ == Axis::VERTICAL) {
1819             childLayoutProperty->ClearUserDefinedIdealSize(false, true);
1820         } else {
1821             childLayoutProperty->ClearUserDefinedIdealSize(true, false);
1822         }
1823         return true;
1824     }
1825 
1826     if (expandSafeArea_ || layoutWrapper->CheckNeedForceMeasureAndLayout() || layoutWrapper->IsIgnoreOptsValid()) {
1827         return true;
1828     }
1829 
1830     auto geometryNode = layoutWrapper->GetGeometryNode();
1831     CHECK_NULL_RETURN(geometryNode, true);
1832     auto constraint = geometryNode->GetParentLayoutConstraint();
1833     CHECK_NULL_RETURN(constraint, true);
1834     return constraint->maxSize != layoutConstraint.maxSize ||
1835            constraint->percentReference != layoutConstraint.percentReference ||
1836            constraint->selfIdealSize.CrossSize(axis_) != layoutConstraint.selfIdealSize.CrossSize(axis_);
1837 }
1838 
MeasureChild(LayoutWrapper * layoutWrapper,const SizeF & frameSize,const RefPtr<LayoutWrapper> & childLayoutWrapper,int32_t crossStart,int32_t crossSpan)1839 void GridScrollLayoutAlgorithm::MeasureChild(LayoutWrapper* layoutWrapper, const SizeF& frameSize,
1840     const RefPtr<LayoutWrapper>& childLayoutWrapper, int32_t crossStart, int32_t crossSpan)
1841 {
1842     auto gridLayoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
1843     auto mainSize = GetMainAxisSize(frameSize, info_.axis_);
1844     auto crossSize = GetCrossAxisSize(frameSize, info_.axis_);
1845     auto childConstraint = CreateChildConstraint(mainSize, crossSize, gridLayoutProperty, crossStart, crossSpan);
1846     if (!CheckNeedMeasure(childLayoutWrapper, childConstraint)) {
1847         return;
1848     }
1849     auto childLayoutProperty = childLayoutWrapper->GetLayoutProperty();
1850     if (!childLayoutProperty) {
1851         childLayoutWrapper->Measure(childConstraint);
1852         return;
1853     }
1854 
1855     auto oldConstraint = childLayoutProperty->GetLayoutConstraint();
1856     if (oldConstraint.has_value() && !NearEqual(GetCrossAxisSize(oldConstraint.value().maxSize, axis_),
1857                                          GetCrossAxisSize(childConstraint.maxSize, axis_))) {
1858         auto layoutAlgorithmWrapper = childLayoutWrapper->GetLayoutAlgorithm();
1859         if (layoutAlgorithmWrapper->SkipMeasure()) {
1860             layoutAlgorithmWrapper->SetNeedMeasure();
1861             if (layoutAlgorithmWrapper->GetLayoutAlgorithm() == nullptr) {
1862                 layoutAlgorithmWrapper->SetLayoutAlgorithm(
1863                     childLayoutWrapper->GetHostNode()->GetPattern()->CreateLayoutAlgorithm());
1864             }
1865         }
1866     }
1867     childLayoutWrapper->Measure(childConstraint);
1868 }
1869 
CheckGridPlaced(int32_t index,int32_t main,int32_t cross,int32_t mainSpan,int32_t crossSpan)1870 bool GridScrollLayoutAlgorithm::CheckGridPlaced(
1871     int32_t index, int32_t main, int32_t cross, int32_t mainSpan, int32_t crossSpan)
1872 {
1873     // If start position is already exist in gridMatrix, place grid item fail.
1874     auto mainIter = info_.gridMatrix_.find(main);
1875     if (mainIter != info_.gridMatrix_.end()) {
1876         auto crossIter = mainIter->second.find(cross);
1877         if (crossIter != mainIter->second.end()) {
1878             return false;
1879         }
1880     }
1881 
1882     // If cross length of grid item if out of range,  place grid item fail.
1883     if (cross + crossSpan > static_cast<int32_t>(crossCount_)) {
1884         return false;
1885     }
1886 
1887     // If any grid item is already exist in gridMatrix, place grid item fail.
1888     for (int32_t i = 0; i < mainSpan; i++) {
1889         mainIter = info_.gridMatrix_.find(i + main);
1890         if (mainIter == info_.gridMatrix_.end()) {
1891             continue;
1892         }
1893         for (int32_t j = 0; j < crossSpan; j++) {
1894             auto crossIter = mainIter->second.find(j + cross);
1895             if (crossIter != mainIter->second.end()) {
1896                 return false;
1897             }
1898         }
1899     }
1900 
1901     // Padding grid matrix for grid item's range.
1902     for (int32_t i = main; i < main + mainSpan; ++i) {
1903         std::map<int32_t, int32_t> mainMap;
1904         auto iter = info_.gridMatrix_.find(i);
1905         if (iter != info_.gridMatrix_.end()) {
1906             mainMap = iter->second;
1907         }
1908         for (int32_t j = cross; j < cross + crossSpan; ++j) {
1909             mainMap.emplace(std::make_pair(j, index));
1910         }
1911         info_.gridMatrix_[i] = mainMap;
1912     }
1913     lastCross_ = cross + crossSpan;
1914 
1915     return true;
1916 }
1917 
ComputeItemCrossPosition(int32_t crossStart) const1918 float GridScrollLayoutAlgorithm::ComputeItemCrossPosition(int32_t crossStart) const
1919 {
1920     float position = 0.0f;
1921     for (int32_t index = 0; index < crossStart; ++index) {
1922         if (index >= 0 && index < static_cast<int32_t>(itemsCrossSize_.size())) {
1923             position += GetOrDefault(itemsCrossSize_, index, 0.0f);
1924         }
1925     }
1926     position += crossStart * crossGap_ + crossPaddingOffset_;
1927     return position;
1928 }
1929 
GetStartingItem(LayoutWrapper * layoutWrapper,int32_t currentIndex)1930 int32_t GridScrollLayoutAlgorithm::GetStartingItem(LayoutWrapper* layoutWrapper, int32_t currentIndex)
1931 {
1932     int32_t firstIndex = 0;
1933     currentIndex = currentIndex < info_.GetChildrenCount() ? currentIndex : info_.GetChildrenCount() - 1;
1934     auto index = currentIndex;
1935     if (info_.hasBigItem_) {
1936         while (index > 0) {
1937             auto childLayoutWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1938             if (!childLayoutWrapper) {
1939                 TAG_LOGW(AceLogTag::ACE_GRID, "item [%{public}d] does not exist, reload to [0]", index);
1940                 break;
1941             }
1942 
1943             AdjustRowColSpan(childLayoutWrapper, layoutWrapper, index);
1944             auto crossIndex = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1945             if (crossIndex == 0) {
1946                 firstIndex = index;
1947                 break;
1948             }
1949             --index;
1950         }
1951     } else {
1952         while (index > 0) {
1953             // need to obtain the item node in order and by step one
1954             auto childLayoutWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1955             if (!childLayoutWrapper) {
1956                 TAG_LOGW(AceLogTag::ACE_GRID, "item [%{public}d] does not exist, reload to [0]", index);
1957                 break;
1958             }
1959             AdjustRowColSpan(childLayoutWrapper, layoutWrapper, index);
1960             auto crossIndex = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1961             // Grid may change from no big item to has big item
1962             if (crossIndex >= 0) {
1963                 info_.hasBigItem_ = true;
1964                 return GetStartingItem(layoutWrapper, currentIndex);
1965             }
1966             if (index % info_.crossCount_ == 0) {
1967                 firstIndex = index;
1968                 break;
1969             }
1970             --index;
1971         }
1972     }
1973     return firstIndex;
1974 }
1975 
SupplyAllData2ZeroIndex(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)1976 void GridScrollLayoutAlgorithm::SupplyAllData2ZeroIndex(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
1977 {
1978     // Save the global variable at this moment.
1979     auto tempGridLayoutInfo = info_;
1980 
1981     // When the data is supplied again, there is an update of the original global variable info_. Therefore,
1982     // each time you supply the data, you must re-complete the data based on the current screen data
1983     auto startLineIndex = tempGridLayoutInfo.startMainLineIndex_;
1984     auto startIndex = tempGridLayoutInfo.startIndex_;
1985     auto endLineIndex = tempGridLayoutInfo.endMainLineIndex_;
1986     auto endIndex = tempGridLayoutInfo.endIndex_;
1987     auto targetIndex = tempGridLayoutInfo.targetIndex_;
1988     // Remove redundant data that is visible off-screen. This is the key to completing data accurately
1989     DeleteItemsOutOfScope(info_.lineHeightMap_, startLineIndex, endLineIndex);
1990     DeleteItemsOutOfScope(info_.gridMatrix_, startLineIndex, endLineIndex);
1991 
1992     // The continuous grid information is saved and used in the GridPattern to calculate the scroll distance
1993     // Complete all data with indexes from startIndex to 0
1994     if (startIndex > 0) {
1995         // The start line when completing the data
1996         currentMainLineIndex_ = startLineIndex;
1997         float lineHeight = 0.0f;
1998         do {
1999             lineHeight = FillNewLineForward(crossSize, mainSize, layoutWrapper);
2000         } while (GreatOrEqual(lineHeight, 0.0));
2001     }
2002 
2003     // Complete the data from endIndex+1 to targetIndex_
2004     auto lineHeight = 0.f;
2005     if (endIndex < targetIndex) {
2006         // The start line when completing the data
2007         currentMainLineIndex_ = endLineIndex;
2008         int32_t targetLineIndex = 0;
2009         do {
2010             lineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, false);
2011         } while (!(LessNotEqual(lineHeight, 0.0) || IsIndexInMatrix(targetIndex.value(), targetLineIndex)));
2012     }
2013 
2014     if (info_.extraOffset_.has_value() && Negative(info_.extraOffset_.value())) {
2015         auto extraOffset = -info_.extraOffset_.value();
2016         info_.GetLineIndexByIndex(targetIndex.value(), currentMainLineIndex_);
2017         lineHeight = info_.lineHeightMap_[currentMainLineIndex_];
2018         auto heightForExtraOffset = lineHeight + mainGap_;
2019         while (GreatOrEqual(extraOffset, heightForExtraOffset) && !Negative(lineHeight)) {
2020             lineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, false);
2021             heightForExtraOffset += (lineHeight + mainGap_);
2022         }
2023         ACE_SCOPED_TRACE(
2024             "SupplyAllData2ZeroIndex, extraOffset_:%f, heightForExtraOffset:%f, LineIndexForExtraOffset:%d",
2025             extraOffset, heightForExtraOffset, currentMainLineIndex_);
2026     }
2027 
2028     // Once the data is completed, the global variables need to be returned
2029     infoCopy_ = std::make_unique<GridLayoutInfo>(info_);
2030     info_ = tempGridLayoutInfo;
2031 }
2032 
FillCacheLineAtEnd(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)2033 void GridScrollLayoutAlgorithm::FillCacheLineAtEnd(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
2034 {
2035     auto gridLayoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
2036     auto cacheCount = gridLayoutProperty->GetCachedCountValue(info_.defCachedCount_);
2037     if (info_.reachEnd_ || cacheCount == 0) {
2038         return;
2039     }
2040     auto tempEndIndex = info_.endIndex_;
2041     auto tempEndMainLineIndex = info_.endMainLineIndex_;
2042     auto tempCurrentMainLineIndex = currentMainLineIndex_;
2043     currentMainLineIndex_ = info_.endMainLineIndex_;
2044 
2045     for (; currentMainLineIndex_ <= tempEndMainLineIndex + cacheCount; currentMainLineIndex_++) {
2046         float lineHeight = FillNewCacheLineBackward(crossSize, mainSize, layoutWrapper, currentMainLineIndex_);
2047         if (LessNotEqual(lineHeight, 0.0)) {
2048             break;
2049         }
2050     }
2051     info_.endIndex_ = tempEndIndex;
2052     info_.endMainLineIndex_ = tempEndMainLineIndex;
2053     currentMainLineIndex_ = tempCurrentMainLineIndex;
2054 
2055     if (!predictBuildList_.empty()) {
2056         CreateCachedChildConstraint(layoutWrapper, mainSize, crossSize);
2057     }
2058 }
2059 
FillNewCacheLineBackward(float crossSize,float mainSize,LayoutWrapper * layoutWrapper,int32_t currentLine)2060 float GridScrollLayoutAlgorithm::FillNewCacheLineBackward(
2061     float crossSize, float mainSize, LayoutWrapper* layoutWrapper, int32_t currentLine)
2062 {
2063     // To make the code more convenient to read, we name a param in situation of vertical, for example:
2064     // 1. [lineHight] means height of a row when the Grid is vertical;
2065     // 2. [lineHight] means width of a column when the Grid is horizontal;
2066     // Other params are also named according to this principle.
2067     cellAveLength_ = -1.0f;
2068     auto currentIndex = info_.endIndex_ + 1;
2069 
2070     // if it fails to fill a new line backward, do [currentLine--]
2071     auto line = info_.gridMatrix_.find(currentLine);
2072     auto childrenCount = info_.GetChildrenCount();
2073     if (info_.gridMatrix_.find(currentLine) != info_.gridMatrix_.end()) {
2074         if (line->second.size() < crossCount_) {
2075             lastCross_ = 0;
2076             for (const auto& elem : line->second) {
2077                 if (elem.second > info_.endIndex_) {
2078                     info_.endIndex_ = elem.second;
2079                 }
2080             }
2081             auto currentIndex = info_.endIndex_ + 1;
2082             for (uint32_t i = line->second.size(); i < crossCount_; i++) {
2083                 // Step1. Get wrapper of [GridItem]
2084                 CHECK_NULL_RETURN(currentIndex < childrenCount, -1.0f);
2085                 auto itemWrapper = layoutWrapper->GetChildByIndex(currentIndex, true);
2086                 if (GridUtils::CheckNeedCacheLayout(itemWrapper)) {
2087                     for (uint32_t y = i; y < crossCount_ && currentIndex < childrenCount; y++) {
2088                         predictBuildList_.emplace_back(currentIndex++);
2089                     }
2090                     if (GreatOrEqual(cellAveLength_, 0.0f) &&
2091                         info_.lineHeightMap_.find(currentLine) == info_.lineHeightMap_.end()) {
2092                         info_.lineHeightMap_[currentLine] = cellAveLength_;
2093                     }
2094                     return -1.0f;
2095                 }
2096                 // Step2. Measure child
2097                 auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
2098                 auto childState = MeasureCachedChild(frameSize, currentIndex, layoutWrapper, itemWrapper);
2099                 if (childState == -1) {
2100                     cellAveLength_ = LessNotEqual(cellAveLength_, 0.0)
2101                                          ? info_.lineHeightMap_.find(currentLine - 1)->second
2102                                          : cellAveLength_;
2103                     --currentIndex;
2104                     break;
2105                 }
2106                 i += static_cast<uint32_t>(childState) - 1;
2107                 // Step3. Measure [GridItem]
2108                 LargeItemLineHeight(itemWrapper);
2109                 info_.endIndex_ = currentIndex;
2110                 currentIndex++;
2111             }
2112         }
2113         CompleteItemCrossPosition(layoutWrapper, line->second);
2114         for (const auto& elem : line->second) {
2115             if (elem.second > info_.endIndex_) {
2116                 info_.endIndex_ = elem.second;
2117             }
2118         }
2119         if (NonNegative(cellAveLength_)) {
2120             info_.lineHeightMap_[currentLine] = cellAveLength_;
2121             return cellAveLength_;
2122         }
2123         return GetOrDefault(info_.lineHeightMap_, currentLine, -1.f);
2124     }
2125 
2126     lastCross_ = 0;
2127     bool doneFillLine = false;
2128 
2129     for (uint32_t i = 0; i < crossCount_; i++) {
2130         if (currentIndex >= childrenCount) {
2131             break;
2132         }
2133         // Step1. Get wrapper of [GridItem]
2134         auto itemWrapper = layoutWrapper->GetChildByIndex(currentIndex, true);
2135         if (GridUtils::CheckNeedCacheLayout(itemWrapper)) {
2136             for (uint32_t x = i; x < crossCount_ && currentIndex < childrenCount; x++) {
2137                 predictBuildList_.emplace_back(currentIndex++);
2138             }
2139             if (GreatOrEqual(cellAveLength_, 0.0f) &&
2140                 info_.lineHeightMap_.find(currentLine) == info_.lineHeightMap_.end()) {
2141                 info_.lineHeightMap_[currentLine] = cellAveLength_;
2142             }
2143             return -1.0f;
2144         }
2145         // // Step2. Measure child
2146         auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
2147         auto crossSpan = MeasureCachedChild(frameSize, currentIndex, layoutWrapper, itemWrapper);
2148         if (crossSpan < 0) {
2149             cellAveLength_ =
2150                 LessNotEqual(cellAveLength_, 0.0) ? info_.lineHeightMap_.find(currentLine - 1)->second : cellAveLength_;
2151             --currentIndex;
2152             break;
2153         }
2154         i = static_cast<uint32_t>(lastCross_ - 1);
2155         // // Step3. Measure [GridItem]
2156         LargeItemLineHeight(itemWrapper);
2157 
2158         info_.endIndex_ = currentIndex;
2159         currentIndex++;
2160         doneFillLine = true;
2161     }
2162 
2163     if (doneFillLine || info_.gridMatrix_.find(currentLine) != info_.gridMatrix_.end()) {
2164         info_.lineHeightMap_[currentLine] = cellAveLength_;
2165         info_.endMainLineIndex_ = currentLine;
2166     } else {
2167         return -1.0f;
2168     }
2169     return cellAveLength_;
2170 }
2171 
MeasureCachedChild(const SizeF & frameSize,int32_t itemIndex,LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & childLayoutWrapper)2172 int32_t GridScrollLayoutAlgorithm::MeasureCachedChild(const SizeF& frameSize, int32_t itemIndex,
2173     LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& childLayoutWrapper)
2174 {
2175     auto mainCount = static_cast<int32_t>(mainCount_);
2176     auto crossCount = static_cast<int32_t>(crossCount_);
2177     AdjustRowColSpan(childLayoutWrapper, layoutWrapper, itemIndex);
2178     auto mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
2179     auto crossSpan = axis_ == Axis::VERTICAL ? currentItemColSpan_ : currentItemRowSpan_;
2180     auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
2181     if (crossSpan > crossCount) {
2182         return crossSpan;
2183     }
2184     int32_t mainIndex = currentMainLineIndex_;
2185 
2186     if (crossStart >= 0 && crossStart < crossCount) {
2187         if (crossStart < lastCross_) {
2188             return -1;
2189         } else if (CheckGridPlaced(itemIndex, mainIndex, crossStart, mainSpan, crossSpan)) {
2190             itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(crossStart));
2191         } else {
2192             return -1;
2193         }
2194     } else {
2195         int32_t crossIndex = crossStart >= 0 ? crossStart : lastCross_;
2196 
2197         while (!CheckGridPlaced(itemIndex, mainIndex, crossIndex, mainSpan, crossSpan)) {
2198             if (GetNextGrid(mainIndex, crossIndex, false) == false) {
2199                 return -1;
2200             }
2201             if (mainIndex >= mainCount || crossIndex >= crossCount) {
2202                 break;
2203             }
2204         }
2205 
2206         itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(crossIndex));
2207     }
2208     if (crossSpan > 1 || mainSpan > 1) {
2209         for (int i = currentMainLineIndex_; i < mainSpan; i++) {
2210             info_.irregularLines_[i] = true;
2211         }
2212     }
2213     return crossSpan;
2214 }
2215 
CompleteItemCrossPosition(LayoutWrapper * layoutWrapper,const std::map<int32_t,int32_t> & items)2216 void GridScrollLayoutAlgorithm::CompleteItemCrossPosition(
2217     LayoutWrapper* layoutWrapper, const std::map<int32_t, int32_t>& items)
2218 {
2219     for (auto&& item : items) {
2220         auto currentIndex = item.second;
2221         auto itemWrapper = layoutWrapper->GetChildByIndex(currentIndex, true);
2222         if (!itemWrapper) {
2223             if (predictBuildList_.back().idx < currentIndex) {
2224                 predictBuildList_.emplace_front(currentIndex);
2225             } else if (predictBuildList_.front().idx > currentIndex) {
2226                 predictBuildList_.emplace_back(currentIndex);
2227             }
2228         }
2229         itemsCrossPosition_.try_emplace(currentIndex, ComputeItemCrossPosition(item.first));
2230     }
2231 }
2232 
2233 namespace {
GenerateCacheItemConstraint(const GridItemLayoutProperty & itemProp,Axis axis,const GridPredictLayoutParam & param)2234 LayoutConstraintF GenerateCacheItemConstraint(
2235     const GridItemLayoutProperty& itemProp, Axis axis, const GridPredictLayoutParam& param)
2236 {
2237     auto constraint = param.layoutConstraint;
2238     int32_t crossSpan = itemProp.GetCrossSpan(axis);
2239     int32_t crossStart = itemProp.GetCrossStart(axis);
2240     if (crossSpan > 1) {
2241         float itemCrossSize = param.crossGap * (crossSpan - 1);
2242         for (int32_t index = 0; index < crossSpan; ++index) {
2243             int32_t crossIndex = (crossStart + index) % static_cast<int32_t>(param.itemsCrossSizes.size());
2244             if (crossIndex >= 0 && crossIndex < static_cast<int32_t>(param.itemsCrossSizes.size())) {
2245                 itemCrossSize += GetOrDefault(param.itemsCrossSizes, crossIndex, 0.0f);
2246             }
2247         }
2248         constraint.maxSize.SetCrossSize(itemCrossSize, axis);
2249         constraint.selfIdealSize.SetCrossSize(itemCrossSize, axis);
2250     }
2251     return constraint;
2252 }
2253 
2254 /* revert layout range in GridLayoutInfo when this object destructs */
2255 class TempLayoutRange {
2256 public:
TempLayoutRange(GridLayoutInfo & info)2257     explicit TempLayoutRange(GridLayoutInfo& info)
2258         : subStart_(info.startIndex_), subStartLine_(info.startMainLineIndex_), subEnd_(info.endIndex_),
2259           subEndLine_(info.endMainLineIndex_), info_(info)
2260     {}
~TempLayoutRange()2261     ~TempLayoutRange()
2262     {
2263         const int32_t diff = CalcDiff();
2264         info_.startIndex_ = subStart_;
2265         info_.startMainLineIndex_ = subStartLine_ - diff;
2266         info_.endIndex_ = subEnd_;
2267         info_.endMainLineIndex_ = subEndLine_ - diff;
2268     }
2269 
2270     const int32_t subStart_;
2271     const int32_t subStartLine_;
2272     const int32_t subEnd_;
2273     const int32_t subEndLine_;
2274 
2275 private:
2276     GridLayoutInfo& info_;
2277 
2278     /**
2279      * @brief after SyncPreload, need adjust startMainLineIdx and endMainLineIdx according to startIndex_.
2280      * Because in AddForwardLines, it may change the gridMatrix_ and make the original start/endMainLineIdx invalid.
2281      */
CalcDiff()2282     int32_t CalcDiff()
2283     {
2284         if (subStartLine_ > subEndLine_) {
2285             // need not adjust when there has no items in viewport.
2286             return 0;
2287         }
2288         auto newStartMainLineIdx = subStartLine_;
2289         info_.GetLineIndexByIndex(subStart_, newStartMainLineIdx);
2290         return subStartLine_ - newStartMainLineIdx;
2291     }
2292 
2293     ACE_DISALLOW_COPY_AND_MOVE(TempLayoutRange);
2294 };
2295 } // namespace
2296 
SyncPreload(LayoutWrapper * wrapper,int32_t cacheLineCnt,float crossSize,float mainSize)2297 void GridScrollLayoutAlgorithm::SyncPreload(
2298     LayoutWrapper* wrapper, int32_t cacheLineCnt, float crossSize, float mainSize)
2299 {
2300     TempLayoutRange scope(info_);
2301     for (int32_t i = 0; i < cacheLineCnt; ++i) {
2302         FillNewLineForward(crossSize, mainSize, wrapper);
2303 
2304         float len = 0.0f;
2305         int32_t endIdx = info_.endIndex_;
2306         int32_t line = scope.subEndLine_ + i + 1;
2307         if (!MeasureExistingLine(line, len, endIdx)) {
2308             currentMainLineIndex_ = line - 1;
2309             FillCurrentLine(mainSize, crossSize, wrapper);
2310             // avoid fill another line when current line is not full
2311             if (measureInNextFrame_) {
2312                 break;
2313             }
2314             FillNewLineBackward(crossSize, mainSize, wrapper, false);
2315         }
2316         if (measureInNextFrame_) {
2317             break;
2318         }
2319     }
2320 }
2321 
PredictBuildItem(FrameNode & host,int32_t itemIdx,const GridPredictLayoutParam & param)2322 bool GridScrollLayoutAlgorithm::PredictBuildItem(FrameNode& host, int32_t itemIdx, const GridPredictLayoutParam& param)
2323 {
2324     // build callback
2325     auto wrapper = host.GetOrCreateChildByIndex(itemIdx, false, true);
2326     CHECK_NULL_RETURN(wrapper, false);
2327     auto itemProperty = DynamicCast<GridItemLayoutProperty>(wrapper->GetLayoutProperty());
2328     CHECK_NULL_RETURN(itemProperty, false);
2329     const Axis axis = host.GetPattern<GridPattern>()->GetAxis();
2330 
2331     auto constraint = GenerateCacheItemConstraint(*itemProperty, axis, param);
2332     wrapper->SetActive(false);
2333     auto frameNode = wrapper->GetHostNode();
2334     CHECK_NULL_RETURN(frameNode, false);
2335     frameNode->GetGeometryNode()->SetParentLayoutConstraint(constraint);
2336     FrameNode::ProcessOffscreenNode(frameNode);
2337     return true;
2338 }
2339 
CreateCachedChildConstraint(LayoutWrapper * layoutWrapper,float mainSize,float crossSize)2340 void GridScrollLayoutAlgorithm::CreateCachedChildConstraint(
2341     LayoutWrapper* layoutWrapper, float mainSize, float crossSize)
2342 {
2343     auto gridLayoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
2344     cachedChildConstraint_ = CreateChildConstraint(mainSize, crossSize, gridLayoutProperty, 0, 1);
2345 }
2346 
UpdateMainLineOnReload(int32_t startIdx)2347 void GridScrollLayoutAlgorithm::UpdateMainLineOnReload(int32_t startIdx)
2348 {
2349     auto& info = info_;
2350     if (!info.hasBigItem_) {
2351         info.startMainLineIndex_ = startIdx / info.crossCount_;
2352     }
2353 }
2354 
GetResetMode(LayoutWrapper * layoutWrapper,int32_t updateIdx)2355 std::pair<bool, bool> GridScrollLayoutAlgorithm::GetResetMode(LayoutWrapper* layoutWrapper, int32_t updateIdx)
2356 {
2357     std::pair<bool, bool> res = { false, false };
2358     auto host = layoutWrapper->GetHostNode();
2359     CHECK_NULL_RETURN(host, res);
2360     auto pattern = host->GetPattern<GridPattern>();
2361     CHECK_NULL_RETURN(pattern, res);
2362     // avoid reset during overScroll
2363     if ((pattern->IsScrollableSpringMotionRunning() && info_.IsOutOfEnd(mainGap_, false))
2364         || updateIdx == -1) {
2365         return res;
2366     }
2367     bool outOfMatrix = false;
2368     if (updateIdx != -1 && updateIdx < info_.startIndex_) {
2369         int32_t startLine = 0;
2370         outOfMatrix = !IsIndexInMatrix(updateIdx, startLine);
2371     }
2372     auto hasOptions = HasLayoutOptions(layoutWrapper);
2373     return { !info_.hasBigItem_ || outOfMatrix || hasOptions, info_.hasBigItem_ && !outOfMatrix && !hasOptions };
2374 }
2375 
CheckReset(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)2376 void GridScrollLayoutAlgorithm::CheckReset(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
2377 {
2378     int32_t updateIdx = layoutWrapper->GetHostNode()->GetChildrenUpdated();
2379     // [resetFromStart,resetFromUpdate]
2380     std::pair<bool, bool> resetMode = GetResetMode(layoutWrapper, updateIdx);
2381     if (info_.lastCrossCount_ != crossCount_ || resetMode.first || info_.IsResetted()) {
2382         if (info_.lastCrossCount_ != crossCount_) {
2383             reason_ = GridReloadReason::CROSS_COUNT_CHANGE;
2384         }
2385         info_.lastCrossCount_ = crossCount_;
2386         info_.lineHeightMap_.clear();
2387         info_.gridMatrix_.clear();
2388         info_.irregularItemsPosition_.clear();
2389         info_.endIndex_ = -1;
2390         info_.endMainLineIndex_ = 0;
2391         info_.prevOffset_ = info_.currentOffset_;
2392         info_.ResetPositionFlags();
2393         info_.clearStretch_ = true;
2394         isChildrenUpdated_ = true;
2395         info_.hasMultiLineItem_ = false;
2396         info_.times_ = 0;
2397         ResetFocusedIndex(layoutWrapper);
2398         if (info_.GetChildrenCount() > 0) {
2399             ReloadToStartIndex(mainSize, crossSize, layoutWrapper);
2400         } else {
2401             info_.startIndex_ = 0;
2402             info_.startMainLineIndex_ = 0;
2403         }
2404         if (IsScrollToEndLine()) {
2405             info_.currentOffset_ = mainSize - info_.lineHeightMap_[info_.endMainLineIndex_];
2406             info_.prevOffset_ = info_.currentOffset_;
2407         }
2408     } else if (resetMode.second) {
2409         isChildrenUpdated_ = true;
2410         info_.irregularItemsPosition_.clear();
2411         info_.ResetPositionFlags();
2412         info_.clearStretch_ = true;
2413         info_.prevOffset_ = info_.currentOffset_;
2414         ResetFocusedIndex(layoutWrapper);
2415         auto it = info_.FindInMatrix(updateIdx);
2416         it = info_.FindStartLineInMatrix(it, updateIdx);
2417         if (it != info_.gridMatrix_.end()) {
2418             const int32_t line = it->first;
2419             info_.ClearMatrixToEnd(updateIdx, line);
2420             info_.ClearHeightsFromMatrix(line);
2421             if (updateIdx <= info_.startIndex_) {
2422                 ReloadFromUpdateIdxToStartIndex(mainSize, crossSize, line, layoutWrapper);
2423             }
2424         }
2425     }
2426 }
2427 
CheckLastLineItemFullyShowed(LayoutWrapper * layoutWrapper)2428 bool GridScrollLayoutAlgorithm::CheckLastLineItemFullyShowed(LayoutWrapper* layoutWrapper)
2429 {
2430     auto lastLine = info_.gridMatrix_.find(info_.endMainLineIndex_);
2431     if (lastLine != info_.gridMatrix_.end()) {
2432         for (const auto [corssIndex, itemIndex] : lastLine->second) {
2433             auto itemWrapper = layoutWrapper->GetChildByIndex(itemIndex);
2434             if (!itemWrapper) {
2435                 continue;
2436             }
2437             auto itemLayoutProperty = DynamicCast<GridItemLayoutProperty>(itemWrapper->GetLayoutProperty());
2438             if (!itemLayoutProperty) {
2439                 continue;
2440             }
2441             if (itemLayoutProperty->GetMainSpan(axis_) == 1) {
2442                 continue;
2443             }
2444             auto it = info_.FindStartLineInMatrix(lastLine, itemIndex);
2445             if (it == info_.gridMatrix_.end()) {
2446                 continue;
2447             }
2448             int32_t startLine = it->first;
2449             if (startLine + itemLayoutProperty->GetMainSpan(axis_) > info_.endMainLineIndex_ + 1) {
2450                 return false;
2451             }
2452         }
2453     }
2454     return true;
2455 }
2456 
IsIrregularLine(int32_t lineIndex) const2457 bool GridScrollLayoutAlgorithm::IsIrregularLine(int32_t lineIndex) const
2458 {
2459     auto irregular = info_.irregularLines_.find(lineIndex);
2460     if (irregular != info_.irregularLines_.end() && irregular->second) {
2461         return true;
2462     }
2463     return false;
2464 }
2465 
ResetOffsetWhenHeightChanged()2466 void GridScrollLayoutAlgorithm::ResetOffsetWhenHeightChanged()
2467 {
2468     if (scrollSource_ == SCROLL_FROM_NONE) {
2469         info_.prevOffset_ = info_.currentOffset_;
2470     }
2471 }
2472 
MergeRemainingLines(std::map<int32_t,std::map<int32_t,int32_t>> matrix,int32_t forwardLines)2473 void GridScrollLayoutAlgorithm::MergeRemainingLines(
2474     std::map<int32_t, std::map<int32_t, int32_t>> matrix, int32_t forwardLines)
2475 {
2476     for (const auto& line : matrix) {
2477         if (line.second.empty()) {
2478             continue;
2479         }
2480         for (const auto& [crossIndex, index] : line.second) {
2481             info_.gridMatrix_[line.first - forwardLines][crossIndex] = index;
2482         }
2483     }
2484 }
2485 
SkipLargeLineHeightLines(float mainSize)2486 bool GridScrollLayoutAlgorithm::SkipLargeLineHeightLines(float mainSize)
2487 {
2488     bool needSkip = false;
2489     for (int32_t line = info_.startMainLineIndex_; line <= info_.endMainLineIndex_; line++) {
2490         auto iter = info_.lineHeightMap_.find(line);
2491         if (iter != info_.lineHeightMap_.end() && iter->second >= mainSize) {
2492             needSkip = true;
2493             break;
2494         }
2495     }
2496     if (needSkip) {
2497         auto totalViewHeight = info_.GetTotalHeightOfItemsInView(mainGap_);
2498         auto needSkipHeight = totalViewHeight + info_.prevOffset_ + mainGap_;
2499         if (GreatOrEqual(needSkipHeight, -info_.currentOffset_)) {
2500             return false;
2501         }
2502 
2503         auto endLine = info_.gridMatrix_.find(info_.endMainLineIndex_ + 1);
2504         if (endLine != info_.gridMatrix_.end() && !endLine->second.empty()) {
2505             info_.currentOffset_ += needSkipHeight;
2506             info_.endMainLineIndex_++;
2507             info_.startMainLineIndex_ = info_.endMainLineIndex_;
2508         }
2509     }
2510     return true;
2511 }
2512 
HasLayoutOptions(LayoutWrapper * layoutWrapper)2513 bool GridScrollLayoutAlgorithm::HasLayoutOptions(LayoutWrapper* layoutWrapper)
2514 {
2515     auto gridLayoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
2516     CHECK_NULL_RETURN(gridLayoutProperty, false);
2517     return gridLayoutProperty->GetLayoutOptions().has_value();
2518 }
2519 } // namespace OHOS::Ace::NG
2520