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