• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     if (mainIter != gridLayoutInfo_.gridMatrix_.end() && mainIter->second.size() < crossCount_) {
620         auto currentIndex = gridLayoutInfo_.endIndex_ + 1;
621         cellAveLength_ = -1.0f;
622         bool hasNormalItem = false;
623         lastCross_ = 0;
624         for (uint32_t i = mainIter->second.size(); i < crossCount_; i++) {
625             // Step1. Get wrapper of [GridItem]
626             auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
627             if (!itemWrapper) {
628                 break;
629             }
630             // Step2. Measure child
631             auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
632             auto childState = MeasureNewChild(frameSize, currentIndex, layoutWrapper, itemWrapper, false);
633             if (childState == -1) {
634                 cellAveLength_ = LessNotEqual(cellAveLength_, 0.0)
635                                      ? gridLayoutInfo_.lineHeightMap_.find(currentMainLineIndex_ - 1)->second
636                                      : cellAveLength_;
637                 --currentIndex;
638                 break;
639             }
640             i += static_cast<uint32_t>(childState) - 1;
641             // Step3. Measure [GridItem]
642             LargeItemLineHeight(itemWrapper, hasNormalItem);
643             gridLayoutInfo_.endIndex_ = currentIndex;
644             currentIndex++;
645         }
646     }
647 
648     if (GreatNotEqual(mainLength, mainSize)) {
649         if (IsScrollToEndLine()) {
650             TAG_LOGI(AceLogTag::ACE_GRID, "scroll to end line with index:%{public}d", moveToEndLineIndex_);
651             // scrollToIndex(AUTO) on first layout
652             moveToEndLineIndex_ = -1;
653         }
654         return;
655     }
656     // When [mainLength] is still less than [mainSize], do [FillNewLineBackward] repeatedly until filling up the lower
657     // part of the viewport
658     while (LessNotEqual(mainLength, mainSize)) {
659         float lineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, false);
660         if (GreatOrEqual(lineHeight, 0.0)) {
661             mainLength += (lineHeight + mainGap_);
662             continue;
663         }
664         gridLayoutInfo_.reachEnd_ = true;
665         return;
666     };
667     // last line make LessNotEqual(mainLength, mainSize) and continue is reach end too
668     gridLayoutInfo_.reachEnd_ = gridLayoutInfo_.endIndex_ == gridLayoutInfo_.childrenCount_ - 1;
669 }
670 
CalculateLargeItemOffset(OffsetF currOffset,int32_t itemIndex,int32_t currLineIndex,int32_t currentCrossIndex)671 OffsetF GridScrollLayoutAlgorithm::CalculateLargeItemOffset(
672     OffsetF currOffset, int32_t itemIndex, int32_t currLineIndex, int32_t currentCrossIndex)
673 {
674     OffsetF offset = currOffset;
675     for (int32_t lastCrossIndex = currLineIndex - 1; lastCrossIndex >= 0; lastCrossIndex--) {
676         auto lastGridMatrixIter = gridLayoutInfo_.gridMatrix_.find(lastCrossIndex);
677         if (lastGridMatrixIter == gridLayoutInfo_.gridMatrix_.end()) {
678             continue;
679         }
680         const auto& lastGridItemRecord = lastGridMatrixIter->second;
681         auto lastLineCrossItem = lastGridItemRecord.find(currentCrossIndex);
682         if (lastLineCrossItem == lastGridItemRecord.end()) {
683             continue;
684         }
685         if (lastLineCrossItem->second == itemIndex) {
686             offset -= axis_ == Axis::VERTICAL ? OffsetF(0, gridLayoutInfo_.lineHeightMap_[lastCrossIndex] + mainGap_)
687                                               : OffsetF(gridLayoutInfo_.lineHeightMap_[lastCrossIndex] + mainGap_, 0.0);
688         } else {
689             break;
690         }
691     }
692 
693     return offset;
694 }
695 
NeedAdjust(const RefPtr<GridItemLayoutProperty> & itemLayoutProperty)696 bool GridScrollLayoutAlgorithm::NeedAdjust(const RefPtr<GridItemLayoutProperty>& itemLayoutProperty)
697 {
698     bool needAdjust = false;
699     auto main = axis_ == Axis::VERTICAL ? mainCount_ : crossCount_;
700     auto cross = axis_ == Axis::VERTICAL ? crossCount_ : mainCount_;
701     if (itemLayoutProperty->GetRowStart().has_value()) {
702         currentItemRowStart_ = itemLayoutProperty->GetRowStart().value_or(-1);
703         if ((currentItemRowStart_ < 0) || (currentItemRowStart_ >= static_cast<int32_t>(main))) {
704             needAdjust = true;
705         }
706     }
707     if (itemLayoutProperty->GetRowEnd().has_value()) {
708         currentItemRowEnd_ = itemLayoutProperty->GetRowEnd().value_or(-1);
709         if ((currentItemRowEnd_ < 0) || (currentItemRowEnd_ >= static_cast<int32_t>(main))) {
710             needAdjust = true;
711         }
712     }
713     if (itemLayoutProperty->GetColumnStart().has_value()) {
714         currentItemColStart_ = itemLayoutProperty->GetColumnStart().value_or(-1);
715         if ((currentItemColStart_ < 0) || (currentItemColStart_ >= static_cast<int32_t>(cross))) {
716             needAdjust = true;
717         }
718     }
719     if (itemLayoutProperty->GetColumnEnd().has_value()) {
720         currentItemColEnd_ = itemLayoutProperty->GetColumnEnd().value_or(-1);
721         if ((currentItemColEnd_ < 0) || (currentItemColEnd_ >= static_cast<int32_t>(cross))) {
722             needAdjust = true;
723         }
724     }
725     return needAdjust;
726 }
727 
AdjustRowColSpan(const RefPtr<LayoutWrapper> & itemLayoutWrapper,LayoutWrapper *,int32_t)728 void GridScrollLayoutAlgorithm::AdjustRowColSpan(
729     const RefPtr<LayoutWrapper>& itemLayoutWrapper, LayoutWrapper* /* layoutWrapper */, int32_t /* itemIndex */)
730 {
731     auto itemLayoutProperty = DynamicCast<GridItemLayoutProperty>(itemLayoutWrapper->GetLayoutProperty());
732     CHECK_NULL_VOID(itemLayoutProperty);
733     bool needAdjust = false;
734     currentItemRowSpan_ = 1;
735     currentItemColSpan_ = 1;
736     currentItemRowStart_ = -1;
737     currentItemColStart_ = -1;
738     currentItemColEnd_ = -1;
739     currentItemRowEnd_ = -1;
740     needAdjust = NeedAdjust(itemLayoutProperty);
741     if (!needAdjust) {
742         currentItemRowSpan_ = std::max(currentItemRowEnd_ - currentItemRowStart_ + 1, 1);
743         currentItemColSpan_ = std::max(currentItemColEnd_ - currentItemColStart_ + 1, 1);
744     } else {
745         currentItemRowStart_ = -1;
746         currentItemColStart_ = -1;
747         currentItemColEnd_ = -1;
748         currentItemRowEnd_ = -1;
749     }
750     if ((currentItemRowStart_ == -1 && currentItemRowEnd_ != -1) ||
751         (currentItemRowEnd_ == -1 && currentItemRowStart_ != -1) ||
752         (currentItemColStart_ == -1 && currentItemColEnd_ != -1) ||
753         (currentItemColEnd_ == -1 && currentItemColStart_ != -1)) {
754         currentItemRowSpan_ = 1;
755         currentItemColSpan_ = 1;
756         currentItemRowStart_ = -1;
757         currentItemColStart_ = -1;
758         currentItemColEnd_ = -1;
759         currentItemRowEnd_ = -1;
760     }
761     if (currentItemRowSpan_ > 1 || currentItemColSpan_ > 1) {
762         gridLayoutInfo_.hasBigItem_ = true;
763     }
764 
765     int32_t mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
766     if (mainSpan > 1) {
767         gridLayoutInfo_.hasMultiLineItem_ = true;
768     }
769 
770     itemLayoutProperty->UpdateRealRowSpan(currentItemRowSpan_);
771     itemLayoutProperty->UpdateRealColumnSpan(currentItemColSpan_);
772 }
773 
LargeItemLineHeight(const RefPtr<LayoutWrapper> & itemWrapper,bool & hasNormalItem)774 void GridScrollLayoutAlgorithm::LargeItemLineHeight(const RefPtr<LayoutWrapper>& itemWrapper, bool& hasNormalItem)
775 {
776     AdjustRowColSpan(itemWrapper, nullptr, 0);
777     auto mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
778     auto itemSize = itemWrapper->GetGeometryNode()->GetMarginFrameSize();
779     if (mainSpan == 1) {
780         cellAveLength_ = std::max(GetMainAxisSize(itemSize, gridLayoutInfo_.axis_), cellAveLength_);
781         hasNormalItem = true;
782     }
783 
784     if ((mainSpan > 1) && !hasNormalItem) {
785         cellAveLength_ =
786             std::max((GetMainAxisSize(itemSize, gridLayoutInfo_.axis_) - (mainGap_ * (mainSpan - 1))) / mainSpan,
787                 cellAveLength_);
788     }
789 }
790 
IsIndexInMatrix(int32_t index,int32_t & startLine)791 bool GridScrollLayoutAlgorithm::IsIndexInMatrix(int32_t index, int32_t& startLine)
792 {
793     auto iter = std::find_if(gridLayoutInfo_.gridMatrix_.begin(), gridLayoutInfo_.gridMatrix_.end(),
794         [index, &startLine](const std::pair<int32_t, std::map<int32_t, int32_t>>& item) {
795             for (auto& subitem : item.second) {
796                 if (subitem.second == index) {
797                     startLine = item.first;
798                     return true;
799                 }
800             }
801             return false;
802         });
803     return (iter != gridLayoutInfo_.gridMatrix_.end());
804 }
805 
GetTargetIndexInfoWithBenchMark(LayoutWrapper * layoutWrapper,bool isTargetBackward,int32_t targetIndex)806 void GridScrollLayoutAlgorithm::GetTargetIndexInfoWithBenchMark(
807     LayoutWrapper* layoutWrapper, bool isTargetBackward, int32_t targetIndex)
808 {
809     int32_t benchmarkIndex = (isTargetBackward && !gridLayoutInfo_.gridMatrix_.empty())
810                                  ? gridLayoutInfo_.gridMatrix_.rbegin()->second.rbegin()->second + 1
811                                  : 0;
812     int32_t mainStartIndex = (isTargetBackward && !gridLayoutInfo_.gridMatrix_.empty())
813                                  ? gridLayoutInfo_.gridMatrix_.rbegin()->first + 1
814                                  : 0;
815     int32_t currentIndex = benchmarkIndex;
816     int32_t headOfMainStartLine = currentIndex;
817 
818     while (currentIndex < targetIndex) {
819         int32_t crossGridReserve = gridLayoutInfo_.crossCount_;
820         /* go through a new line */
821         while ((crossGridReserve > 0) && (currentIndex <= targetIndex)) {
822             auto currentWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex, false);
823             CHECK_NULL_VOID(currentWrapper);
824             auto layoutProperty = DynamicCast<GridItemLayoutProperty>(currentWrapper->GetLayoutProperty());
825             CHECK_NULL_VOID(layoutProperty);
826             auto gridSpan = layoutProperty->GetCrossSpan(gridLayoutInfo_.axis_);
827             if (crossGridReserve >= gridSpan) {
828                 crossGridReserve -= gridSpan;
829             } else if (gridLayoutInfo_.crossCount_ >= gridSpan) {
830                 ++mainStartIndex;
831                 headOfMainStartLine = currentIndex;
832                 crossGridReserve = gridLayoutInfo_.crossCount_ - gridSpan;
833             }
834             ++currentIndex;
835         }
836         if (currentIndex > targetIndex) {
837             break;
838         }
839         ++mainStartIndex;
840         headOfMainStartLine = currentIndex;
841     }
842     gridLayoutInfo_.startMainLineIndex_ = mainStartIndex;
843     gridLayoutInfo_.startIndex_ = headOfMainStartLine;
844     gridLayoutInfo_.endIndex_ = headOfMainStartLine - 1;
845     gridLayoutInfo_.prevOffset_ = 0;
846     gridLayoutInfo_.currentOffset_ = 0;
847     gridLayoutInfo_.ResetPositionFlags();
848     gridLayoutInfo_.gridMatrix_.clear();
849     gridLayoutInfo_.lineHeightMap_.clear();
850     gridLayoutInfo_.irregularItemsPosition_.clear();
851 }
852 
UpdateGridLayoutInfo(LayoutWrapper * layoutWrapper,float mainSize)853 void GridScrollLayoutAlgorithm::UpdateGridLayoutInfo(LayoutWrapper* layoutWrapper, float mainSize)
854 {
855     /* 1. Have gotten gridLayoutInfo_.startMainLineIndex_ and directly jump to it */
856     if (gridLayoutInfo_.jumpIndex_ < 0 && gridLayoutInfo_.jumpIndex_ != LAST_ITEM) {
857         return;
858     }
859     if (gridLayoutInfo_.jumpIndex_ == LAST_ITEM) {
860         gridLayoutInfo_.jumpIndex_ = gridLayoutInfo_.childrenCount_ - 1;
861     }
862     /* 2. Need to find out the startMainLineIndex according to startIndex */
863     int32_t targetIndex = gridLayoutInfo_.jumpIndex_;
864     /* 2.1 invalid targetIndex */
865     if (gridLayoutInfo_.childrenCount_ <= targetIndex) {
866         return;
867     }
868 
869     switch (gridLayoutInfo_.scrollAlign_) {
870         case ScrollAlign::START:
871         case ScrollAlign::END:
872         case ScrollAlign::CENTER:
873             ScrollToIndexStart(layoutWrapper, targetIndex);
874             return;
875         default:
876             ScrollToIndexAuto(layoutWrapper, mainSize, targetIndex);
877     }
878 }
879 
IsScrollToEndLine() const880 bool GridScrollLayoutAlgorithm::IsScrollToEndLine() const
881 {
882     return moveToEndLineIndex_ > 0 && gridLayoutInfo_.endIndex_ >= moveToEndLineIndex_;
883 }
884 
IsEndLineInScreenWithGap(int32_t targetLine,float totalViewHeight,float mainSize) const885 bool GridScrollLayoutAlgorithm::IsEndLineInScreenWithGap(
886     int32_t targetLine, float totalViewHeight, float mainSize) const
887 {
888     return targetLine == gridLayoutInfo_.endMainLineIndex_ &&
889            LessOrEqual(totalViewHeight + gridLayoutInfo_.currentOffset_, mainSize);
890 }
891 
ScrollToIndexAuto(LayoutWrapper * layoutWrapper,float mainSize,int32_t targetIndex)892 void GridScrollLayoutAlgorithm::ScrollToIndexAuto(LayoutWrapper* layoutWrapper, float mainSize, int32_t targetIndex)
893 {
894     int32_t startLine = 0;
895     if (IsIndexInMatrix(targetIndex, startLine)) {
896         auto& info = gridLayoutInfo_;
897         if (startLine == info.startMainLineIndex_ && info.startMainLineIndex_ == info.endMainLineIndex_) {
898             // startLine occupies the whole viewport
899             return;
900         }
901         if (startLine < gridLayoutInfo_.endMainLineIndex_ && startLine > gridLayoutInfo_.startMainLineIndex_) {
902             return;
903         }
904 
905         if (startLine >= gridLayoutInfo_.endMainLineIndex_) {
906             auto totalViewHeight = gridLayoutInfo_.GetTotalHeightOfItemsInView(mainGap_);
907             if (IsEndLineInScreenWithGap(startLine, totalViewHeight, mainSize)) {
908                 return;
909             }
910             // When ScrollAlign::AUTO and startLine is greater than endMainLineIndex, the effect of
911             // ScrollToIndex is the same as ScrollAlign::END.
912             gridLayoutInfo_.scrollAlign_ = ScrollAlign::END;
913         }
914 
915         // startLine <= gridLayoutInfo_.startMainLineIndex_
916         gridLayoutInfo_.startMainLineIndex_ = startLine;
917         gridLayoutInfo_.UpdateStartIndexByStartLine();
918         gridLayoutInfo_.prevOffset_ = 0;
919         gridLayoutInfo_.currentOffset_ = 0;
920         gridLayoutInfo_.ResetPositionFlags();
921         return;
922     }
923 
924     /* 2.3 targetIndex is out of the matrix */
925     bool isTargetBackward = true;
926     if (!gridLayoutInfo_.gridMatrix_.empty()) {
927         if (targetIndex < gridLayoutInfo_.gridMatrix_.begin()->second.begin()->second) {
928             isTargetBackward = false;
929         } else if (targetIndex > gridLayoutInfo_.gridMatrix_.rbegin()->second.rbegin()->second) {
930             isTargetBackward = true;
931         } else {
932             return;
933         }
934     }
935     auto grid = layoutWrapper->GetHostNode();
936     CHECK_NULL_VOID(grid);
937     grid->ChildrenUpdatedFrom(0);
938     GetTargetIndexInfoWithBenchMark(layoutWrapper, isTargetBackward, targetIndex);
939     moveToEndLineIndex_ = isTargetBackward ? targetIndex : moveToEndLineIndex_;
940 }
941 
ScrollToIndexStart(LayoutWrapper * layoutWrapper,int32_t targetIndex)942 void GridScrollLayoutAlgorithm::ScrollToIndexStart(LayoutWrapper* layoutWrapper, int32_t targetIndex)
943 {
944     int32_t startLine = 0;
945     /* targetIndex is in the matrix */
946     if (IsIndexInMatrix(targetIndex, startLine)) {
947         if (startLine == gridLayoutInfo_.startMainLineIndex_) {
948             gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
949             gridLayoutInfo_.currentOffset_ = 0;
950             gridLayoutInfo_.ResetPositionFlags();
951             return;
952         }
953 
954         gridLayoutInfo_.startMainLineIndex_ = startLine;
955         gridLayoutInfo_.UpdateStartIndexByStartLine();
956         gridLayoutInfo_.prevOffset_ = 0;
957         gridLayoutInfo_.currentOffset_ = 0;
958         gridLayoutInfo_.ResetPositionFlags();
959         return;
960     }
961     /* targetIndex is out of the matrix */
962     bool isTargetBackward = true;
963     if (!gridLayoutInfo_.gridMatrix_.empty()) {
964         if (targetIndex < gridLayoutInfo_.gridMatrix_.begin()->second.begin()->second) {
965             isTargetBackward = false;
966         } else if (targetIndex > gridLayoutInfo_.gridMatrix_.rbegin()->second.rbegin()->second) {
967             isTargetBackward = true;
968         } else {
969             return;
970         }
971     }
972     auto grid = layoutWrapper->GetHostNode();
973     CHECK_NULL_VOID(grid);
974     grid->ChildrenUpdatedFrom(0);
975     GetTargetIndexInfoWithBenchMark(layoutWrapper, isTargetBackward, targetIndex);
976 }
977 
UpdateCurrentOffsetForJumpTo(float mainSize)978 void GridScrollLayoutAlgorithm::UpdateCurrentOffsetForJumpTo(float mainSize)
979 {
980     if (gridLayoutInfo_.scrollAlign_ == ScrollAlign::CENTER || gridLayoutInfo_.scrollAlign_ == ScrollAlign::END) {
981         int32_t startLine = 0;
982         /* targetIndex is in the matrix */
983         if (IsIndexInMatrix(gridLayoutInfo_.jumpIndex_, startLine)) {
984             // scroll to end of the screen
985             gridLayoutInfo_.currentOffset_ =
986                 mainSize - gridLayoutInfo_.lineHeightMap_[startLine] - gridLayoutInfo_.contentEndPadding_;
987             // scroll to center of the screen
988             if (gridLayoutInfo_.scrollAlign_ == ScrollAlign::CENTER) {
989                 gridLayoutInfo_.currentOffset_ /= 2;
990             }
991             gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
992         } else {
993             /* targetIndex is out of the matrix */
994             TAG_LOGW(
995                 AceLogTag::ACE_GRID, "can not find jumpIndex in Grid Matrix :%{public}d", gridLayoutInfo_.jumpIndex_);
996         }
997     }
998     if (gridLayoutInfo_.extraOffset_.has_value() && !gridLayoutInfo_.targetIndex_.has_value()) {
999         gridLayoutInfo_.currentOffset_ += gridLayoutInfo_.extraOffset_.value();
1000     }
1001 }
1002 
MeasureRecordedItems(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)1003 float GridScrollLayoutAlgorithm::MeasureRecordedItems(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
1004 {
1005     currentMainLineIndex_ = gridLayoutInfo_.startMainLineIndex_ - 1;
1006     float mainLength = gridLayoutInfo_.currentOffset_;
1007     // already at start line, do not use offset for mainLength
1008     if (gridLayoutInfo_.startMainLineIndex_ == 0 && GreatNotEqual(mainLength, 0)) {
1009         mainLength = 0;
1010     }
1011     UseCurrentLines(mainSize, crossSize, layoutWrapper, mainLength);
1012     return mainLength;
1013 }
1014 
1015 namespace {
OneLineMovesOffViewportFromAbove(float mainLength,float lineHeight)1016 inline bool OneLineMovesOffViewportFromAbove(float mainLength, float lineHeight)
1017 {
1018     return LessNotEqual(mainLength, 0.0) || (NearZero(mainLength) && GreatNotEqual(lineHeight, 0.0f));
1019 }
1020 } // namespace
1021 
MeasureExistingLine(int32_t line,float & mainLength,int32_t & endIdx)1022 bool GridScrollLayoutAlgorithm::MeasureExistingLine(int32_t line, float& mainLength, int32_t& endIdx)
1023 {
1024     auto it = gridLayoutInfo_.gridMatrix_.find(line);
1025     if (it == gridLayoutInfo_.gridMatrix_.end() ||
1026         gridLayoutInfo_.lineHeightMap_.find(line) == gridLayoutInfo_.lineHeightMap_.end()) {
1027         return false;
1028     }
1029     int32_t idx = -1;
1030     bool hasNormalItem = false;
1031     cellAveLength_ = -1.0f;
1032     for (const auto& cell : it->second) {
1033         if (idx == cell.second) {
1034             continue;
1035         }
1036         idx = cell.second;
1037         if (idx == -1) {
1038             // move from another grid
1039             continue;
1040         }
1041         auto item = wrapper_->GetOrCreateChildByIndex(idx);
1042         if (!item) {
1043             break;
1044         }
1045         AdjustRowColSpan(item, wrapper_, idx);
1046         auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1047         if (crossStart == -1) {
1048             MeasureChildPlaced(frameSize_, idx, cell.first, wrapper_, item);
1049         } else {
1050             MeasureChildPlaced(frameSize_, idx, crossStart, wrapper_, item);
1051         }
1052         // Record end index. When fill new line, the [endIndex_] will be the first item index to request
1053         LargeItemLineHeight(item, hasNormalItem);
1054         endIdx = std::max(idx, endIdx);
1055         gridLayoutInfo_.endIndex_ = endIdx;
1056     }
1057 
1058     if (NonNegative(cellAveLength_)) { // Means at least one item has been measured
1059         gridLayoutInfo_.lineHeightMap_[line] = cellAveLength_;
1060         mainLength += cellAveLength_ + mainGap_;
1061     }
1062     // If a line moves up out of viewport, update [startIndex_], [currentOffset_] and [startMainLineIndex_]
1063     if (OneLineMovesOffViewportFromAbove(mainLength, cellAveLength_)) {
1064         gridLayoutInfo_.currentOffset_ = mainLength;
1065         gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
1066         gridLayoutInfo_.startMainLineIndex_ = line + 1;
1067         gridLayoutInfo_.UpdateStartIndexByStartLine();
1068     }
1069     return true;
1070 }
1071 
UseCurrentLines(float mainSize,float crossSize,LayoutWrapper * layoutWrapper,float & mainLength)1072 bool GridScrollLayoutAlgorithm::UseCurrentLines(
1073     float mainSize, float crossSize, LayoutWrapper* layoutWrapper, float& mainLength)
1074 {
1075     bool runOutOfRecord = false;
1076     // Measure grid items row by row
1077     int32_t tempEndIndex = -1;
1078     while (LessNotEqual(mainLength, mainSize)) {
1079         if (!MeasureExistingLine(++currentMainLineIndex_, mainLength, tempEndIndex)) {
1080             runOutOfRecord = true;
1081             break;
1082         }
1083     }
1084     // Case 1. if this while-loop breaks due to running out of records, the [currentMainLineIndex_] is larger by 1 than
1085     // real main line index, so reduce 1.
1086     // Case 2. if this while-loop stops due to false result of [LessNotEqual(mainLength, mainSize)], the
1087     // [currentMainLineIndex_] is exactly the real main line index. Update [endMainLineIndex_] when the recorded items
1088     // are done measured.
1089     gridLayoutInfo_.endMainLineIndex_ = runOutOfRecord ? --currentMainLineIndex_ : currentMainLineIndex_;
1090     // reset reachEnd_ if any line at bottom is out of viewport
1091     // last line make LessNotEqual(mainLength, mainSize) and continue is reach end too
1092     gridLayoutInfo_.reachEnd_ = gridLayoutInfo_.endIndex_ == gridLayoutInfo_.childrenCount_ - 1;
1093     if (!gridLayoutInfo_.reachEnd_) {
1094         gridLayoutInfo_.offsetEnd_ = false;
1095     }
1096     return runOutOfRecord;
1097 }
1098 
SkipLargeOffset(float mainSize,LayoutWrapper * layoutWrapper)1099 void GridScrollLayoutAlgorithm::SkipLargeOffset(float mainSize, LayoutWrapper* layoutWrapper)
1100 {
1101     auto gridLayoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
1102     CHECK_NULL_VOID(gridLayoutProperty);
1103     auto cacheCount = gridLayoutProperty->GetCachedCountValue(gridLayoutInfo_.defCachedCount_);
1104     cacheCount = std::max(cacheCount, 1);
1105     SkipForwardLines(cacheCount * mainSize, layoutWrapper);
1106     SkipBackwardLines(cacheCount * mainSize, layoutWrapper);
1107 }
1108 
SkipForwardLines(float mainSize,LayoutWrapper * layoutWrapper)1109 void GridScrollLayoutAlgorithm::SkipForwardLines(float mainSize, LayoutWrapper* layoutWrapper)
1110 {
1111     if (!GreatOrEqual(gridLayoutInfo_.currentOffset_ - gridLayoutInfo_.prevOffset_, mainSize)) {
1112         return;
1113     }
1114     // skip lines in matrix
1115     while (GreatOrEqual(gridLayoutInfo_.currentOffset_, mainSize)) {
1116         auto line = gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.startMainLineIndex_ - 1);
1117         if (line == gridLayoutInfo_.gridMatrix_.end()) {
1118             break;
1119         }
1120         auto lineHeight = gridLayoutInfo_.lineHeightMap_.find(gridLayoutInfo_.startMainLineIndex_ - 1);
1121         if (lineHeight == gridLayoutInfo_.lineHeightMap_.end()) {
1122             break;
1123         }
1124         gridLayoutInfo_.startMainLineIndex_--;
1125         gridLayoutInfo_.startIndex_ = line->second.begin()->second;
1126         gridLayoutInfo_.currentOffset_ -= lineHeight->second + mainGap_;
1127     }
1128 
1129     // skip lines not in matrix
1130     if (GreatOrEqual(gridLayoutInfo_.currentOffset_, mainSize) && gridLayoutInfo_.startIndex_ > 0) {
1131         if (!gridLayoutInfo_.hasBigItem_) {
1132             SkipRegularLines(true);
1133         } else {
1134             SkipIrregularLines(layoutWrapper, true);
1135         }
1136         gridLayoutInfo_.startIndex_ = std::clamp(gridLayoutInfo_.startIndex_, 0, gridLayoutInfo_.childrenCount_ - 1);
1137         TAG_LOGI(AceLogTag::ACE_GRID, "estimatedIndex:%{public}d", gridLayoutInfo_.startIndex_);
1138         auto grid = layoutWrapper->GetHostNode();
1139         CHECK_NULL_VOID(grid);
1140         grid->ChildrenUpdatedFrom(0);
1141     }
1142 }
1143 
SkipBackwardLines(float mainSize,LayoutWrapper * layoutWrapper)1144 void GridScrollLayoutAlgorithm::SkipBackwardLines(float mainSize, LayoutWrapper* layoutWrapper)
1145 {
1146     if (!GreatOrEqual(gridLayoutInfo_.prevOffset_ - gridLayoutInfo_.currentOffset_, mainSize)) {
1147         return;
1148     }
1149 
1150     if (!SkipLargeLineHeightLines(mainSize)) {
1151         return;
1152     }
1153 
1154     // grid size change from big to small
1155     gridLayoutInfo_.UpdateEndLine(mainSize, mainGap_);
1156 
1157     // skip lines in matrix
1158     while (GreatOrEqual(-gridLayoutInfo_.currentOffset_, mainSize)) {
1159         auto line = gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.endMainLineIndex_ + 1);
1160         if (line == gridLayoutInfo_.gridMatrix_.end()) {
1161             break;
1162         }
1163         auto lineHeight = gridLayoutInfo_.lineHeightMap_.find(gridLayoutInfo_.endMainLineIndex_ + 1);
1164         if (lineHeight == gridLayoutInfo_.lineHeightMap_.end()) {
1165             break;
1166         }
1167         gridLayoutInfo_.startMainLineIndex_++;
1168         gridLayoutInfo_.endMainLineIndex_++;
1169         gridLayoutInfo_.currentOffset_ += lineHeight->second + mainGap_;
1170     }
1171     gridLayoutInfo_.UpdateStartIndexByStartLine();
1172 
1173     // skip lines not in matrix
1174     if (GreatOrEqual(-gridLayoutInfo_.currentOffset_, mainSize)) {
1175         if (!gridLayoutInfo_.hasBigItem_) {
1176             SkipRegularLines(false);
1177         } else {
1178             SkipIrregularLines(layoutWrapper, false);
1179         }
1180         gridLayoutInfo_.startIndex_ = std::clamp(gridLayoutInfo_.startIndex_, 0, gridLayoutInfo_.childrenCount_ - 1);
1181         TAG_LOGI(AceLogTag::ACE_GRID, "estimatedIndex:%{public}d, currentOffset:%{public}f",
1182             gridLayoutInfo_.startIndex_, gridLayoutInfo_.currentOffset_);
1183         auto grid = layoutWrapper->GetHostNode();
1184         CHECK_NULL_VOID(grid);
1185         grid->ChildrenUpdatedFrom(0);
1186     }
1187 }
1188 
SkipRegularLines(bool forward)1189 void GridScrollLayoutAlgorithm::SkipRegularLines(bool forward)
1190 {
1191     auto lineHeight = gridLayoutInfo_.GetAverageLineHeight() + mainGap_;
1192     if (LessOrEqual(lineHeight, 0.0)) {
1193         return;
1194     }
1195     int32_t estimatedLines = gridLayoutInfo_.currentOffset_ / lineHeight;
1196     if (forward && gridLayoutInfo_.startIndex_ < estimatedLines * static_cast<int32_t>(crossCount_)) {
1197         gridLayoutInfo_.startIndex_ = 0;
1198         gridLayoutInfo_.currentOffset_ = 0;
1199     } else {
1200         gridLayoutInfo_.startIndex_ -= estimatedLines * static_cast<int32_t>(crossCount_);
1201         gridLayoutInfo_.currentOffset_ -= lineHeight * estimatedLines;
1202     }
1203 }
1204 
SkipIrregularLines(LayoutWrapper * layoutWrapper,bool forward)1205 void GridScrollLayoutAlgorithm::SkipIrregularLines(LayoutWrapper* layoutWrapper, bool forward)
1206 {
1207     auto grid = layoutWrapper->GetHostNode();
1208     CHECK_NULL_VOID(grid);
1209     auto pattern = grid->GetPattern<GridPattern>();
1210     CHECK_NULL_VOID(pattern);
1211     auto averageHeight = pattern->GetAverageHeight();
1212     if (LessOrEqual(averageHeight, 0.0)) {
1213         return;
1214     }
1215     int32_t estimatedIndex = (gridLayoutInfo_.currentOffset_) / averageHeight;
1216     gridLayoutInfo_.startIndex_ =
1217         std::min(gridLayoutInfo_.startIndex_ - estimatedIndex, gridLayoutInfo_.childrenCount_);
1218     gridLayoutInfo_.currentOffset_ = gridLayoutInfo_.prevOffset_;
1219 }
1220 
FillNewLineForward(float crossSize,float mainSize,LayoutWrapper * layoutWrapper)1221 float GridScrollLayoutAlgorithm::FillNewLineForward(float crossSize, float mainSize, LayoutWrapper* layoutWrapper)
1222 {
1223     // To make the code more convenient to read, we name a param in situation of vertical, for example:
1224     // 1. [lineHight] means height of a row when the Grid is vertical;
1225     // 2. [lineHight] means width of a column when the Grid is horizontal;
1226     // Other params are also named according to this principle.
1227     cellAveLength_ = -1.0f;
1228     auto currentIndex = gridLayoutInfo_.startIndex_;
1229     bool hasNormalItem = false;
1230     if (gridLayoutInfo_.startMainLineIndex_ - 1 < 0) {
1231         if (currentIndex == 0) {
1232             return cellAveLength_;
1233         }
1234         // add more than one line
1235         UpdateMatrixForAddedItems();
1236     }
1237     gridLayoutInfo_.startMainLineIndex_--;
1238     bool doneCreateNewLine = false;
1239     auto gridMatrixIter = gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.startMainLineIndex_);
1240     if (gridMatrixIter == gridLayoutInfo_.gridMatrix_.end()) {
1241         AddForwardLines(currentIndex, crossSize, mainSize, layoutWrapper);
1242     }
1243     gridMatrixIter = gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.startMainLineIndex_);
1244     if (gridMatrixIter == gridLayoutInfo_.gridMatrix_.end()) {
1245         return cellAveLength_;
1246     }
1247 
1248     // need to obtain the item node in order and by step one in LazyLayoutWrapperBuilder::OnGetOrCreateWrapperByIndex
1249     for (auto itemIter = gridMatrixIter->second.rbegin(); itemIter != gridMatrixIter->second.rend(); ++itemIter) {
1250         currentIndex = itemIter->second;
1251 
1252         // Step1. Get wrapper of [GridItem]
1253         auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1254         if (!itemWrapper) {
1255             break;
1256         }
1257         // Step2. Measure child
1258         auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
1259         AdjustRowColSpan(itemWrapper, layoutWrapper, currentIndex);
1260         auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1261         if (crossStart == -1) {
1262             MeasureChildPlaced(frameSize, currentIndex, itemIter->first, layoutWrapper, itemWrapper);
1263         } else {
1264             MeasureChildPlaced(frameSize, currentIndex, crossStart, layoutWrapper, itemWrapper);
1265         }
1266         // Step3. Measure [GridItem]
1267         LargeItemLineHeight(itemWrapper, hasNormalItem);
1268         gridLayoutInfo_.startIndex_ = currentIndex;
1269     }
1270 
1271     doneCreateNewLine = GreatOrEqual(cellAveLength_, 0.0);
1272     // If it fails to create new line when [FillNewLineForward] is called, it means that it reaches start
1273     gridLayoutInfo_.reachStart_ = !doneCreateNewLine;
1274 
1275     return cellAveLength_;
1276 }
1277 
UpdateMatrixForAddedItems()1278 void GridScrollLayoutAlgorithm::UpdateMatrixForAddedItems()
1279 {
1280     decltype(gridLayoutInfo_.lineHeightMap_) gridLineHeightMap(std::move(gridLayoutInfo_.lineHeightMap_));
1281     decltype(gridLayoutInfo_.gridMatrix_) gridMatrix(std::move(gridLayoutInfo_.gridMatrix_));
1282     for (const auto& item : gridMatrix) {
1283         gridLayoutInfo_.gridMatrix_[item.first + 1] = item.second;
1284     }
1285     for (const auto& item : gridLineHeightMap) {
1286         gridLayoutInfo_.lineHeightMap_[item.first + 1] = item.second;
1287     }
1288     gridLayoutInfo_.startMainLineIndex_ = gridLayoutInfo_.startMainLineIndex_ + 1;
1289     gridLayoutInfo_.endMainLineIndex_ = gridLayoutInfo_.endMainLineIndex_ + 1;
1290     TAG_LOGI(AceLogTag::ACE_GRID, "add more than one line startMainLineIndex_:%{public}d",
1291         gridLayoutInfo_.startMainLineIndex_);
1292 }
1293 
AddForwardLines(int32_t currentIndex,float crossSize,float mainSize,LayoutWrapper * layoutWrapper)1294 void GridScrollLayoutAlgorithm::AddForwardLines(
1295     int32_t currentIndex, float crossSize, float mainSize, LayoutWrapper* layoutWrapper)
1296 {
1297     auto endMainLineIndex = gridLayoutInfo_.endMainLineIndex_;
1298     auto endIndex = gridLayoutInfo_.endIndex_;
1299     auto firstItem = GetStartingItem(layoutWrapper, currentIndex - 1);
1300     auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(firstItem);
1301     CHECK_NULL_VOID(itemWrapper);
1302     AdjustRowColSpan(itemWrapper, layoutWrapper, firstItem);
1303     auto mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
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     auto crossSpan = axis_ == Axis::VERTICAL ? currentItemColSpan_ : currentItemRowSpan_;
1584     auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1585     if (crossSpan > crossCount) {
1586         TAG_LOGW(AceLogTag::ACE_GRID,
1587             "item %{public}d can not be placed in grid: cross count:%{public}d, cross span:%{public}d", itemIndex,
1588             crossCount, crossSpan);
1589         return crossSpan;
1590     }
1591     int32_t mainIndex = currentMainLineIndex_;
1592 
1593     if (crossStart >= 0 && crossStart < crossCount) {
1594         if (crossStart < lastCross_) {
1595             return -1;
1596         } else if (CheckGridPlaced(itemIndex, mainIndex, crossStart, mainSpan, crossSpan)) {
1597             MeasureChild(layoutWrapper, frameSize, childLayoutWrapper, crossStart, crossSpan);
1598             itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(crossStart));
1599         } else {
1600             return -1;
1601         }
1602     } else {
1603         int32_t crossIndex = crossStart >= 0 ? crossStart : lastCross_;
1604 
1605         while (!CheckGridPlaced(itemIndex, mainIndex, crossIndex, mainSpan, crossSpan)) {
1606             if (GetNextGrid(mainIndex, crossIndex, reverse) == false) {
1607                 return -1;
1608             }
1609             if (mainIndex >= mainCount || crossIndex >= crossCount) {
1610                 break;
1611             }
1612         }
1613 
1614         MeasureChild(layoutWrapper, frameSize, childLayoutWrapper, crossIndex, crossSpan);
1615         itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(crossIndex));
1616     }
1617     if (mainSpan > 1 || crossSpan > 1) {
1618         for (int i = mainIndex; i < mainSpan; i++) {
1619             gridLayoutInfo_.irregularLines_[i] = true;
1620         }
1621     }
1622     return crossSpan;
1623 }
1624 
MeasureChildPlaced(const SizeF & frameSize,int32_t itemIndex,int32_t crossStart,LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & childLayoutWrapper)1625 int32_t GridScrollLayoutAlgorithm::MeasureChildPlaced(const SizeF& frameSize, int32_t itemIndex, int32_t crossStart,
1626     LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& childLayoutWrapper)
1627 {
1628     AdjustRowColSpan(childLayoutWrapper, layoutWrapper, itemIndex);
1629     auto crossSpan = axis_ == Axis::VERTICAL ? currentItemColSpan_ : currentItemRowSpan_;
1630     if (static_cast<uint32_t>(crossStart + crossSpan) > crossCount_) {
1631         TAG_LOGI(AceLogTag::ACE_GRID, "item %{public}d cross not enough, start:%{public}d, span:%{public}d", itemIndex,
1632             crossStart, crossSpan);
1633         return 0;
1634     }
1635     auto mainSpan = axis_ == Axis::HORIZONTAL ? currentItemColSpan_ : currentItemRowSpan_;
1636     if (crossSpan > 1 || mainSpan > 1) {
1637         for (int i = currentMainLineIndex_; i < mainSpan; i++) {
1638             gridLayoutInfo_.irregularLines_[i] = true;
1639         }
1640     }
1641 
1642     MeasureChild(layoutWrapper, frameSize, childLayoutWrapper, crossStart, crossSpan);
1643     itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(crossStart));
1644     return crossSpan;
1645 }
1646 
CheckNeedMeasure(const RefPtr<LayoutWrapper> & layoutWrapper,const LayoutConstraintF & layoutConstraint) const1647 bool GridScrollLayoutAlgorithm::CheckNeedMeasure(
1648     const RefPtr<LayoutWrapper>& layoutWrapper, const LayoutConstraintF& layoutConstraint) const
1649 {
1650     auto childLayoutProperty = AceType::DynamicCast<GridItemLayoutProperty>(layoutWrapper->GetLayoutProperty());
1651     if (childLayoutProperty && childLayoutProperty->GetNeedStretch() && gridLayoutInfo_.clearStretch_) {
1652         childLayoutProperty->SetNeedStretch(false);
1653         if (axis_ == Axis::VERTICAL) {
1654             childLayoutProperty->ClearUserDefinedIdealSize(false, true);
1655         } else {
1656             childLayoutProperty->ClearUserDefinedIdealSize(true, false);
1657         }
1658         return true;
1659     }
1660 
1661     if (expandSafeArea_ || layoutWrapper->CheckNeedForceMeasureAndLayout()) {
1662         return true;
1663     }
1664 
1665     auto geometryNode = layoutWrapper->GetGeometryNode();
1666     CHECK_NULL_RETURN(geometryNode, true);
1667     auto constraint = geometryNode->GetParentLayoutConstraint();
1668     CHECK_NULL_RETURN(constraint, true);
1669     return constraint->maxSize != layoutConstraint.maxSize ||
1670            constraint->percentReference != layoutConstraint.percentReference ||
1671            constraint->selfIdealSize.CrossSize(axis_) != layoutConstraint.selfIdealSize.CrossSize(axis_);
1672 }
1673 
MeasureChild(LayoutWrapper * layoutWrapper,const SizeF & frameSize,const RefPtr<LayoutWrapper> & childLayoutWrapper,int32_t crossStart,int32_t crossSpan)1674 void GridScrollLayoutAlgorithm::MeasureChild(LayoutWrapper* layoutWrapper, const SizeF& frameSize,
1675     const RefPtr<LayoutWrapper>& childLayoutWrapper, int32_t crossStart, int32_t crossSpan)
1676 {
1677     auto gridLayoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
1678     auto mainSize = GetMainAxisSize(frameSize, gridLayoutInfo_.axis_);
1679     auto crossSize = GetCrossAxisSize(frameSize, gridLayoutInfo_.axis_);
1680     auto childConstraint = CreateChildConstraint(mainSize, crossSize, gridLayoutProperty, crossStart, crossSpan);
1681     if (!CheckNeedMeasure(childLayoutWrapper, childConstraint)) {
1682         return;
1683     }
1684     auto childLayoutProperty = childLayoutWrapper->GetLayoutProperty();
1685     if (!childLayoutProperty) {
1686         childLayoutWrapper->Measure(childConstraint);
1687         return;
1688     }
1689 
1690     auto oldConstraint = childLayoutProperty->GetLayoutConstraint();
1691     if (oldConstraint.has_value() && !NearEqual(GetCrossAxisSize(oldConstraint.value().maxSize, axis_),
1692         GetCrossAxisSize(childConstraint.maxSize, axis_))) {
1693         auto layoutAlgorithmWrapper = childLayoutWrapper->GetLayoutAlgorithm();
1694         if (layoutAlgorithmWrapper->SkipMeasure()) {
1695             layoutAlgorithmWrapper->SetNeedMeasure();
1696             if (layoutAlgorithmWrapper->GetLayoutAlgorithm() == nullptr) {
1697                 layoutAlgorithmWrapper->SetLayoutAlgorithm(
1698                     childLayoutWrapper->GetHostNode()->GetPattern()->CreateLayoutAlgorithm());
1699             }
1700         }
1701     }
1702     childLayoutWrapper->Measure(childConstraint);
1703 }
1704 
CheckGridPlaced(int32_t index,int32_t main,int32_t cross,int32_t mainSpan,int32_t crossSpan)1705 bool GridScrollLayoutAlgorithm::CheckGridPlaced(
1706     int32_t index, int32_t main, int32_t cross, int32_t mainSpan, int32_t crossSpan)
1707 {
1708     // If start position is already exist in gridMatrix, place grid item fail.
1709     auto mainIter = gridLayoutInfo_.gridMatrix_.find(main);
1710     if (mainIter != gridLayoutInfo_.gridMatrix_.end()) {
1711         auto crossIter = mainIter->second.find(cross);
1712         if (crossIter != mainIter->second.end()) {
1713             return false;
1714         }
1715     }
1716 
1717     // If cross length of grid item if out of range,  place grid item fail.
1718     if (cross + crossSpan > static_cast<int32_t>(crossCount_)) {
1719         return false;
1720     }
1721 
1722     // If any grid item is already exist in gridMatrix, place grid item fail.
1723     for (int32_t i = 0; i < mainSpan; i++) {
1724         mainIter = gridLayoutInfo_.gridMatrix_.find(i + main);
1725         if (mainIter == gridLayoutInfo_.gridMatrix_.end()) {
1726             continue;
1727         }
1728         for (int32_t j = 0; j < crossSpan; j++) {
1729             auto crossIter = mainIter->second.find(j + cross);
1730             if (crossIter != mainIter->second.end()) {
1731                 return false;
1732             }
1733         }
1734     }
1735 
1736     // Padding grid matrix for grid item's range.
1737     for (int32_t i = main; i < main + mainSpan; ++i) {
1738         std::map<int32_t, int32_t> mainMap;
1739         auto iter = gridLayoutInfo_.gridMatrix_.find(i);
1740         if (iter != gridLayoutInfo_.gridMatrix_.end()) {
1741             mainMap = iter->second;
1742         }
1743         for (int32_t j = cross; j < cross + crossSpan; ++j) {
1744             mainMap.emplace(std::make_pair(j, index));
1745         }
1746         gridLayoutInfo_.gridMatrix_[i] = mainMap;
1747     }
1748     lastCross_ = cross + crossSpan;
1749 
1750     return true;
1751 }
1752 
ComputeItemCrossPosition(int32_t crossStart) const1753 float GridScrollLayoutAlgorithm::ComputeItemCrossPosition(int32_t crossStart) const
1754 {
1755     float position = 0.0f;
1756     for (int32_t index = 0; index < crossStart; ++index) {
1757         if (index >= 0 && index < static_cast<int32_t>(itemsCrossSize_.size())) {
1758             position += GetOrDefault(itemsCrossSize_, index, 0.0f);
1759         }
1760     }
1761     position += crossStart * crossGap_ + crossPaddingOffset_;
1762     return position;
1763 }
1764 
GetStartingItem(LayoutWrapper * layoutWrapper,int32_t currentIndex)1765 int32_t GridScrollLayoutAlgorithm::GetStartingItem(LayoutWrapper* layoutWrapper, int32_t currentIndex)
1766 {
1767     int32_t firstIndex = 0;
1768     currentIndex = currentIndex < gridLayoutInfo_.childrenCount_ ? currentIndex : gridLayoutInfo_.childrenCount_ - 1;
1769     auto index = currentIndex;
1770     if (gridLayoutInfo_.hasBigItem_) {
1771         while (index > 0) {
1772             auto childLayoutWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1773             if (!childLayoutWrapper) {
1774                 TAG_LOGW(AceLogTag::ACE_GRID, "item [%{public}d] does not exist, reload to [0]", index);
1775                 break;
1776             }
1777 
1778             AdjustRowColSpan(childLayoutWrapper, layoutWrapper, index);
1779             auto crossIndex = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1780             if (crossIndex == 0) {
1781                 firstIndex = index;
1782                 break;
1783             }
1784             --index;
1785         }
1786     } else {
1787         while (index > 0) {
1788             // need to obtain the item node in order and by step one
1789             auto childLayoutWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1790             if (!childLayoutWrapper) {
1791                 TAG_LOGW(AceLogTag::ACE_GRID, "item [%{public}d] does not exist, reload to [0]", index);
1792                 break;
1793             }
1794             AdjustRowColSpan(childLayoutWrapper, layoutWrapper, index);
1795             auto crossIndex = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1796             // Grid may change from no big item to has big item
1797             if (crossIndex >= 0) {
1798                 gridLayoutInfo_.hasBigItem_ = true;
1799                 return GetStartingItem(layoutWrapper, currentIndex);
1800             }
1801             if (index % gridLayoutInfo_.crossCount_ == 0) {
1802                 firstIndex = index;
1803                 break;
1804             }
1805             --index;
1806         }
1807     }
1808     return firstIndex;
1809 }
1810 
FillCacheLineAtEnd(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)1811 void GridScrollLayoutAlgorithm::FillCacheLineAtEnd(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
1812 {
1813     auto gridLayoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
1814     auto cacheCount = gridLayoutProperty->GetCachedCountValue(gridLayoutInfo_.defCachedCount_);
1815     if (gridLayoutInfo_.reachEnd_ || cacheCount == 0) {
1816         return;
1817     }
1818     auto tempEndIndex = gridLayoutInfo_.endIndex_;
1819     auto tempEndMainLineIndex = gridLayoutInfo_.endMainLineIndex_;
1820     auto tempCurrentMainLineIndex = currentMainLineIndex_;
1821     currentMainLineIndex_ = gridLayoutInfo_.endMainLineIndex_;
1822 
1823     for (; currentMainLineIndex_ <= tempEndMainLineIndex + cacheCount; currentMainLineIndex_++) {
1824         float lineHeight = FillNewCacheLineBackward(crossSize, mainSize, layoutWrapper, currentMainLineIndex_);
1825         if (LessNotEqual(lineHeight, 0.0)) {
1826             break;
1827         }
1828     }
1829     gridLayoutInfo_.endIndex_ = tempEndIndex;
1830     gridLayoutInfo_.endMainLineIndex_ = tempEndMainLineIndex;
1831     currentMainLineIndex_ = tempCurrentMainLineIndex;
1832 
1833     if (!predictBuildList_.empty()) {
1834         CreateCachedChildConstraint(layoutWrapper, mainSize, crossSize);
1835     }
1836 }
1837 
FillNewCacheLineBackward(float crossSize,float mainSize,LayoutWrapper * layoutWrapper,int32_t currentLine)1838 float GridScrollLayoutAlgorithm::FillNewCacheLineBackward(
1839     float crossSize, float mainSize, LayoutWrapper* layoutWrapper, int32_t currentLine)
1840 {
1841     // To make the code more convenient to read, we name a param in situation of vertical, for example:
1842     // 1. [lineHight] means height of a row when the Grid is vertical;
1843     // 2. [lineHight] means width of a column when the Grid is horizontal;
1844     // Other params are also named according to this principle.
1845     cellAveLength_ = -1.0f;
1846     auto currentIndex = gridLayoutInfo_.endIndex_ + 1;
1847 
1848     // if it fails to fill a new line backward, do [currentLine--]
1849     auto line = gridLayoutInfo_.gridMatrix_.find(currentLine);
1850     if (gridLayoutInfo_.gridMatrix_.find(currentLine) != gridLayoutInfo_.gridMatrix_.end()) {
1851         if (line->second.size() < crossCount_) {
1852             bool hasNormalItem = false;
1853             lastCross_ = 0;
1854             for (const auto& elem : line->second) {
1855                 if (elem.second > gridLayoutInfo_.endIndex_) {
1856                     gridLayoutInfo_.endIndex_ = elem.second;
1857                 }
1858             }
1859             auto currentIndex = gridLayoutInfo_.endIndex_ + 1;
1860             for (uint32_t i = line->second.size() ; i < crossCount_; i++) {
1861                 // Step1. Get wrapper of [GridItem]
1862                 auto itemWrapper = layoutWrapper->GetChildByIndex(currentIndex, true);
1863                 if (!itemWrapper || itemWrapper->CheckNeedForceMeasureAndLayout()) {
1864                     for (uint32_t y = i; y < crossCount_ && currentIndex < gridLayoutInfo_.childrenCount_; y++) {
1865                         predictBuildList_.emplace_back(currentIndex++);
1866                     }
1867                     if (GreatOrEqual(cellAveLength_, 0.0f) &&
1868                         gridLayoutInfo_.lineHeightMap_.find(currentLine) == gridLayoutInfo_.lineHeightMap_.end()) {
1869                         gridLayoutInfo_.lineHeightMap_[currentLine] = cellAveLength_;
1870                     }
1871                     return -1.0f;
1872                 }
1873                 // Step2. Measure child
1874                 auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
1875                 auto childState = MeasureCachedChild(frameSize, currentIndex, layoutWrapper, itemWrapper);
1876                 if (childState == -1) {
1877                     cellAveLength_ = LessNotEqual(cellAveLength_, 0.0)
1878                                          ? gridLayoutInfo_.lineHeightMap_.find(currentLine - 1)->second
1879                                          : cellAveLength_;
1880                     --currentIndex;
1881                     break;
1882                 }
1883                 i += static_cast<uint32_t>(childState) - 1;
1884                 // Step3. Measure [GridItem]
1885                 LargeItemLineHeight(itemWrapper, hasNormalItem);
1886                 gridLayoutInfo_.endIndex_ = currentIndex;
1887                 currentIndex++;
1888             }
1889         }
1890         CompleteItemCrossPosition(layoutWrapper, line->second);
1891         for (const auto& elem : line->second) {
1892             if (elem.second > gridLayoutInfo_.endIndex_) {
1893                 gridLayoutInfo_.endIndex_ = elem.second;
1894             }
1895         }
1896         if (gridLayoutInfo_.lineHeightMap_.find(currentLine) != gridLayoutInfo_.lineHeightMap_.end()) {
1897             return gridLayoutInfo_.lineHeightMap_.find(currentLine)->second;
1898         } else {
1899             gridLayoutInfo_.lineHeightMap_[currentLine] = cellAveLength_;
1900             return cellAveLength_;
1901         }
1902     }
1903 
1904     lastCross_ = 0;
1905     bool hasNormalItem = false;
1906     bool doneFillLine = false;
1907 
1908     for (uint32_t i = 0; i < crossCount_; i++) {
1909         if (currentIndex >= gridLayoutInfo_.childrenCount_) {
1910             break;
1911         }
1912         // Step1. Get wrapper of [GridItem]
1913         auto itemWrapper = layoutWrapper->GetChildByIndex(currentIndex, true);
1914         if (!itemWrapper || itemWrapper->CheckNeedForceMeasureAndLayout()) {
1915             for (uint32_t x = i; x < crossCount_ && currentIndex < gridLayoutInfo_.childrenCount_; x++) {
1916                 predictBuildList_.emplace_back(currentIndex++);
1917             }
1918             if (GreatOrEqual(cellAveLength_, 0.0f) &&
1919                 gridLayoutInfo_.lineHeightMap_.find(currentLine) == gridLayoutInfo_.lineHeightMap_.end()) {
1920                 gridLayoutInfo_.lineHeightMap_[currentLine] = cellAveLength_;
1921             }
1922             return -1.0f;
1923         }
1924         // // Step2. Measure child
1925         auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
1926         auto crossSpan = MeasureCachedChild(frameSize, currentIndex, layoutWrapper, itemWrapper);
1927         if (crossSpan < 0) {
1928             cellAveLength_ = LessNotEqual(cellAveLength_, 0.0)
1929                                  ? gridLayoutInfo_.lineHeightMap_.find(currentLine - 1)->second
1930                                  : cellAveLength_;
1931             --currentIndex;
1932             break;
1933         }
1934         i = static_cast<uint32_t>(lastCross_ - 1);
1935         // // Step3. Measure [GridItem]
1936         LargeItemLineHeight(itemWrapper, hasNormalItem);
1937 
1938         gridLayoutInfo_.endIndex_ = currentIndex;
1939         currentIndex++;
1940         doneFillLine = true;
1941     }
1942 
1943     if (doneFillLine || gridLayoutInfo_.gridMatrix_.find(currentLine) != gridLayoutInfo_.gridMatrix_.end()) {
1944         gridLayoutInfo_.lineHeightMap_[currentLine] = cellAveLength_;
1945         gridLayoutInfo_.endMainLineIndex_ = currentLine;
1946     } else {
1947         return -1.0f;
1948     }
1949     return cellAveLength_;
1950 }
1951 
MeasureCachedChild(const SizeF & frameSize,int32_t itemIndex,LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & childLayoutWrapper)1952 int32_t GridScrollLayoutAlgorithm::MeasureCachedChild(const SizeF& frameSize, int32_t itemIndex,
1953     LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& childLayoutWrapper)
1954 {
1955     auto mainCount = static_cast<int32_t>(mainCount_);
1956     auto crossCount = static_cast<int32_t>(crossCount_);
1957     AdjustRowColSpan(childLayoutWrapper, layoutWrapper, itemIndex);
1958     auto mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
1959     auto crossSpan = axis_ == Axis::VERTICAL ? currentItemColSpan_ : currentItemRowSpan_;
1960     auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1961     if (crossSpan > crossCount) {
1962         return crossSpan;
1963     }
1964     int32_t mainIndex = currentMainLineIndex_;
1965 
1966     if (crossStart >= 0 && crossStart < crossCount) {
1967         if (crossStart < lastCross_) {
1968             return -1;
1969         } else if (CheckGridPlaced(itemIndex, mainIndex, crossStart, mainSpan, crossSpan)) {
1970             itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(crossStart));
1971         } else {
1972             return -1;
1973         }
1974     } else {
1975         int32_t crossIndex = crossStart >= 0 ? crossStart : lastCross_;
1976 
1977         while (!CheckGridPlaced(itemIndex, mainIndex, crossIndex, mainSpan, crossSpan)) {
1978             if (GetNextGrid(mainIndex, crossIndex, false) == false) {
1979                 return -1;
1980             }
1981             if (mainIndex >= mainCount || crossIndex >= crossCount) {
1982                 break;
1983             }
1984         }
1985 
1986         itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(crossIndex));
1987     }
1988     if (crossSpan > 1 || mainSpan > 1) {
1989         for (int i = currentMainLineIndex_; i < mainSpan; i++) {
1990             gridLayoutInfo_.irregularLines_[i] = true;
1991         }
1992     }
1993     return crossSpan;
1994 }
1995 
CompleteItemCrossPosition(LayoutWrapper * layoutWrapper,const std::map<int32_t,int32_t> & items)1996 void GridScrollLayoutAlgorithm::CompleteItemCrossPosition(
1997     LayoutWrapper* layoutWrapper, const std::map<int32_t, int32_t>& items)
1998 {
1999     for (auto&& item : items) {
2000         auto currentIndex = item.second;
2001         auto itemWrapper = layoutWrapper->GetChildByIndex(currentIndex, true);
2002         if (!itemWrapper) {
2003             if (predictBuildList_.back().idx < currentIndex) {
2004                 predictBuildList_.emplace_front(currentIndex);
2005             } else if (predictBuildList_.front().idx > currentIndex) {
2006                 predictBuildList_.emplace_back(currentIndex);
2007             }
2008         }
2009         itemsCrossPosition_.try_emplace(currentIndex, ComputeItemCrossPosition(item.first));
2010     }
2011 }
2012 
2013 namespace {
GenerateCacheItemConstraint(const GridItemLayoutProperty & itemProp,Axis axis,const GridPredictLayoutParam & param)2014 LayoutConstraintF GenerateCacheItemConstraint(
2015     const GridItemLayoutProperty& itemProp, Axis axis, const GridPredictLayoutParam& param)
2016 {
2017     auto constraint = param.layoutConstraint;
2018     int32_t crossSpan = itemProp.GetCrossSpan(axis);
2019     int32_t crossStart = itemProp.GetCrossStart(axis);
2020     if (crossSpan > 1) {
2021         float itemCrossSize = param.crossGap * (crossSpan - 1);
2022         for (int32_t index = 0; index < crossSpan; ++index) {
2023             int32_t crossIndex = (crossStart + index) % static_cast<int32_t>(param.itemsCrossSizes.size());
2024             if (crossIndex >= 0 && crossIndex < static_cast<int32_t>(param.itemsCrossSizes.size())) {
2025                 itemCrossSize += GetOrDefault(param.itemsCrossSizes, crossIndex, 0.0f);
2026             }
2027         }
2028         constraint.maxSize.SetCrossSize(itemCrossSize, axis);
2029         constraint.selfIdealSize.SetCrossSize(itemCrossSize, axis);
2030     }
2031     return constraint;
2032 }
2033 
2034 /* revert layout range in GridLayoutInfo when this object destructs */
2035 class TempLayoutRange {
2036 public:
TempLayoutRange(GridLayoutInfo & info)2037     explicit TempLayoutRange(GridLayoutInfo& info)
2038         : subStart_(info.startIndex_), subStartLine_(info.startMainLineIndex_), subEnd_(info.endIndex_),
2039           subEndLine_(info.endMainLineIndex_), info_(info)
2040     {}
~TempLayoutRange()2041     ~TempLayoutRange()
2042     {
2043         info_.startIndex_ = subStart_;
2044         info_.startMainLineIndex_ = subStartLine_;
2045         info_.endIndex_ = subEnd_;
2046         info_.endMainLineIndex_ = subEndLine_;
2047     }
2048 
2049     const int32_t subStart_;
2050     const int32_t subStartLine_;
2051     const int32_t subEnd_;
2052     const int32_t subEndLine_;
2053 
2054 private:
2055     GridLayoutInfo& info_;
2056 
2057     ACE_DISALLOW_COPY_AND_MOVE(TempLayoutRange);
2058 };
2059 } // namespace
2060 
SyncPreload(LayoutWrapper * wrapper,int32_t cacheLineCnt,float crossSize,float mainSize)2061 void GridScrollLayoutAlgorithm::SyncPreload(
2062     LayoutWrapper* wrapper, int32_t cacheLineCnt, float crossSize, float mainSize)
2063 {
2064     TempLayoutRange scope(gridLayoutInfo_);
2065     for (int32_t i = 0; i < cacheLineCnt; ++i) {
2066         FillNewLineForward(crossSize, mainSize, wrapper);
2067 
2068         float len = 0.0f;
2069         int32_t endIdx = gridLayoutInfo_.endIndex_;
2070         int32_t line = scope.subEndLine_ + i + 1;
2071         if (!MeasureExistingLine(line, len, endIdx)) {
2072             currentMainLineIndex_ = line - 1;
2073             FillNewLineBackward(crossSize, mainSize, wrapper, false);
2074         }
2075     }
2076 }
2077 
PredictBuildItem(FrameNode & host,int32_t itemIdx,const GridPredictLayoutParam & param)2078 bool GridScrollLayoutAlgorithm::PredictBuildItem(FrameNode& host, int32_t itemIdx, const GridPredictLayoutParam& param)
2079 {
2080     // build callback
2081     auto wrapper = host.GetOrCreateChildByIndex(itemIdx, false, true);
2082     CHECK_NULL_RETURN(wrapper, false);
2083     auto itemProperty = DynamicCast<GridItemLayoutProperty>(wrapper->GetLayoutProperty());
2084     CHECK_NULL_RETURN(itemProperty, false);
2085     const Axis axis = host.GetPattern<GridPattern>()->GetAxis();
2086 
2087     auto constraint = GenerateCacheItemConstraint(*itemProperty, axis, param);
2088     wrapper->SetActive(false);
2089     auto frameNode = wrapper->GetHostNode();
2090     CHECK_NULL_RETURN(frameNode, false);
2091     frameNode->GetGeometryNode()->SetParentLayoutConstraint(constraint);
2092     FrameNode::ProcessOffscreenNode(frameNode);
2093     return true;
2094 }
2095 
CreateCachedChildConstraint(LayoutWrapper * layoutWrapper,float mainSize,float crossSize)2096 void GridScrollLayoutAlgorithm::CreateCachedChildConstraint(
2097     LayoutWrapper* layoutWrapper, float mainSize, float crossSize)
2098 {
2099     auto gridLayoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
2100     cachedChildConstraint_ = CreateChildConstraint(mainSize, crossSize, gridLayoutProperty, 0, 1);
2101 }
2102 
SupplyAllData2ZeroIndex(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)2103 void GridScrollLayoutAlgorithm::SupplyAllData2ZeroIndex(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
2104 {
2105     // Save the global variable at this moment.
2106     auto tempGridLayoutInfo = gridLayoutInfo_;
2107 
2108     // When the data is supplied again, there is an update of the original global variable gridLayoutInfo_. Therefore,
2109     // each time you supply the data, you must re-complete the data based on the current screen data
2110     auto startLineIndex = tempGridLayoutInfo.startMainLineIndex_;
2111     auto startIndex = tempGridLayoutInfo.startIndex_;
2112     auto endLineIndex = tempGridLayoutInfo.endMainLineIndex_;
2113     auto endIndex = tempGridLayoutInfo.endIndex_;
2114     auto targetIndex = tempGridLayoutInfo.targetIndex_;
2115     // Remove redundant data that is visible off-screen. This is the key to completing data accurately
2116     DeleteItemsOutOfScope(gridLayoutInfo_.lineHeightMap_, startLineIndex, endLineIndex);
2117     DeleteItemsOutOfScope(gridLayoutInfo_.gridMatrix_, startLineIndex, endLineIndex);
2118 
2119     // The continuous grid information is saved and used in the GridPattern to calculate the scroll distance
2120     // Complete all data with indexes from startIndex to 0
2121     if (startIndex > 0) {
2122         // The start line when completing the data
2123         currentMainLineIndex_ = startLineIndex;
2124         float lineHeight = 0.0f;
2125         do {
2126             lineHeight = FillNewLineForward(crossSize, mainSize, layoutWrapper);
2127         } while (GreatOrEqual(lineHeight, 0.0));
2128     }
2129 
2130     // Complete the data from endIndex+1 to targetIndex_
2131     auto lineHeight = 0.f;
2132     if (endIndex < targetIndex) {
2133         // The start line when completing the data
2134         currentMainLineIndex_ = endLineIndex;
2135         int32_t targetLineIndex = 0;
2136         do {
2137             lineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, false);
2138         } while (!(LessNotEqual(lineHeight, 0.0) || IsIndexInMatrix(targetIndex.value(), targetLineIndex)));
2139     }
2140 
2141     if (gridLayoutInfo_.extraOffset_.has_value() && Negative(gridLayoutInfo_.extraOffset_.value())) {
2142         auto extraOffset = -gridLayoutInfo_.extraOffset_.value();
2143         gridLayoutInfo_.GetLineIndexByIndex(targetIndex.value(), currentMainLineIndex_);
2144         lineHeight = gridLayoutInfo_.lineHeightMap_[currentMainLineIndex_];
2145         auto heightForExtralOffset = lineHeight + mainGap_;
2146         while (GreatOrEqual(extraOffset, heightForExtralOffset) && !Negative(lineHeight)) {
2147             lineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, false);
2148             heightForExtralOffset += (lineHeight + mainGap_);
2149         }
2150         ACE_SCOPED_TRACE(
2151             "SupplyAllData2ZeroIndex, extraOffset_:%f, heightForExtralOffset:%f, LineIndexForExtralOffset:%d",
2152             extraOffset, heightForExtralOffset, currentMainLineIndex_);
2153     }
2154 
2155     // Once the data is completed, the global variables need to be returned
2156     scrollGridLayoutInfo_ = gridLayoutInfo_;
2157     gridLayoutInfo_ = tempGridLayoutInfo;
2158 }
2159 
UpdateMainLineOnReload(int32_t startIdx)2160 void GridScrollLayoutAlgorithm::UpdateMainLineOnReload(int32_t startIdx)
2161 {
2162     auto& info = gridLayoutInfo_;
2163     if (!info.hasBigItem_) {
2164         info.startMainLineIndex_ = startIdx / info.crossCount_;
2165     }
2166 }
2167 
GetResetMode(LayoutWrapper * layoutWrapper,int32_t updateIdx)2168 std::pair<bool, bool> GridScrollLayoutAlgorithm::GetResetMode(LayoutWrapper* layoutWrapper, int32_t updateIdx)
2169 {
2170     if (updateIdx == -1) {
2171         return { 0, 0 };
2172     }
2173     bool outOfMatrix = false;
2174     if (updateIdx != -1 && updateIdx < gridLayoutInfo_.startIndex_) {
2175         int32_t startLine = 0;
2176         outOfMatrix = !IsIndexInMatrix(updateIdx, startLine);
2177     }
2178     auto gridLayoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
2179     bool hasOptions = gridLayoutProperty->GetLayoutOptions().has_value();
2180     return { !gridLayoutInfo_.hasBigItem_ || outOfMatrix || hasOptions,
2181         gridLayoutInfo_.hasBigItem_ && !outOfMatrix && !hasOptions };
2182 }
2183 
CheckReset(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)2184 void GridScrollLayoutAlgorithm::CheckReset(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
2185 {
2186     int32_t updateIdx = layoutWrapper->GetHostNode()->GetChildrenUpdated();
2187     // [resetFromStart,resetFromUpdate]
2188     std::pair<bool, bool> resetMode = GetResetMode(layoutWrapper, updateIdx);
2189     if (gridLayoutInfo_.lastCrossCount_ != crossCount_ || resetMode.first || gridLayoutInfo_.IsResetted()) {
2190         gridLayoutInfo_.lastCrossCount_ = crossCount_;
2191         gridLayoutInfo_.lineHeightMap_.clear();
2192         gridLayoutInfo_.gridMatrix_.clear();
2193         gridLayoutInfo_.irregularItemsPosition_.clear();
2194         gridLayoutInfo_.endIndex_ = -1;
2195         gridLayoutInfo_.endMainLineIndex_ = 0;
2196         gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
2197         gridLayoutInfo_.ResetPositionFlags();
2198         gridLayoutInfo_.clearStretch_ = true;
2199         gridLayoutInfo_.hasMultiLineItem_ = false;
2200         isChildrenUpdated_ = true;
2201         if (gridLayoutInfo_.childrenCount_ > 0) {
2202             ReloadToStartIndex(mainSize, crossSize, layoutWrapper);
2203         } else {
2204             gridLayoutInfo_.startIndex_ = 0;
2205             gridLayoutInfo_.startMainLineIndex_ = 0;
2206         }
2207         if (IsScrollToEndLine()) {
2208             gridLayoutInfo_.currentOffset_ =
2209                 mainSize - gridLayoutInfo_.lineHeightMap_[gridLayoutInfo_.endMainLineIndex_];
2210             gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
2211         }
2212     } else if (resetMode.second) {
2213         isChildrenUpdated_ = true;
2214         gridLayoutInfo_.irregularItemsPosition_.clear();
2215         gridLayoutInfo_.ResetPositionFlags();
2216         gridLayoutInfo_.clearStretch_ = true;
2217         gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
2218         auto it = gridLayoutInfo_.FindInMatrix(updateIdx);
2219         it = gridLayoutInfo_.FindStartLineInMatrix(it, updateIdx);
2220         if (it != gridLayoutInfo_.gridMatrix_.end()) {
2221             int32_t updateLineIndex = it->first;
2222             gridLayoutInfo_.ClearMatrixToEnd(updateIdx, updateLineIndex);
2223             gridLayoutInfo_.ClearHeightsFromMatrix(updateLineIndex);
2224             if (updateIdx <= gridLayoutInfo_.startIndex_) {
2225                 ReloadFromUpdateIdxToStartIndex(mainSize, crossSize, updateLineIndex, layoutWrapper);
2226             }
2227         }
2228     }
2229 }
2230 
CheckLastLineItemFullyShowed(LayoutWrapper * layoutWrapper)2231 bool GridScrollLayoutAlgorithm::CheckLastLineItemFullyShowed(LayoutWrapper* layoutWrapper)
2232 {
2233     auto lastLine = gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.endMainLineIndex_);
2234     if (lastLine != gridLayoutInfo_.gridMatrix_.end()) {
2235         for (const auto [corssIndex, itemIndex] : lastLine->second) {
2236             auto itemWrapper = layoutWrapper->GetChildByIndex(itemIndex);
2237             if (!itemWrapper) {
2238                 continue;
2239             }
2240             auto itemLayoutProperty = DynamicCast<GridItemLayoutProperty>(itemWrapper->GetLayoutProperty());
2241             if (!itemLayoutProperty) {
2242                 continue;
2243             }
2244             if (itemLayoutProperty->GetMainSpan(axis_) == 1) {
2245                 continue;
2246             }
2247             auto it = gridLayoutInfo_.FindStartLineInMatrix(lastLine, itemIndex);
2248             if (it == gridLayoutInfo_.gridMatrix_.end()) {
2249                 continue;
2250             }
2251             int32_t startLine = it->first;
2252             if (startLine + itemLayoutProperty->GetMainSpan(axis_) > gridLayoutInfo_.endMainLineIndex_ + 1) {
2253                 return false;
2254             }
2255         }
2256     }
2257     return true;
2258 }
2259 
IsIrregularLine(int32_t lineIndex) const2260 bool GridScrollLayoutAlgorithm::IsIrregularLine(int32_t lineIndex) const
2261 {
2262     auto irregular = gridLayoutInfo_.irregularLines_.find(lineIndex);
2263     if (irregular != gridLayoutInfo_.irregularLines_.end() && irregular->second) {
2264         return true;
2265     }
2266     return false;
2267 }
2268 
ResetOffsetWhenHeightChanged()2269 void GridScrollLayoutAlgorithm::ResetOffsetWhenHeightChanged()
2270 {
2271     if (scrollSource_ == SCROLL_FROM_NONE) {
2272         gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
2273     }
2274 }
2275 
MergeRemainingLines(std::map<int32_t,std::map<int32_t,int32_t>> matrix,int32_t forwardLines)2276 void GridScrollLayoutAlgorithm::MergeRemainingLines(
2277     std::map<int32_t, std::map<int32_t, int32_t>> matrix, int32_t forwardLines)
2278 {
2279     for (const auto& line : matrix) {
2280         if (line.second.empty()) {
2281             continue;
2282         }
2283         for (const auto& [crossIndex, index] : line.second) {
2284             gridLayoutInfo_.gridMatrix_[line.first - forwardLines][crossIndex] = index;
2285         }
2286     }
2287 }
2288 
SkipLargeLineHeightLines(float mainSize)2289 bool GridScrollLayoutAlgorithm::SkipLargeLineHeightLines(float mainSize)
2290 {
2291     bool needSkip = false;
2292     for (int32_t line = gridLayoutInfo_.startMainLineIndex_; line <= gridLayoutInfo_.endMainLineIndex_; line++) {
2293         auto iter = gridLayoutInfo_.lineHeightMap_.find(line);
2294         if (iter != gridLayoutInfo_.lineHeightMap_.end() && iter->second >= mainSize) {
2295             needSkip = true;
2296             break;
2297         }
2298     }
2299     if (needSkip) {
2300         auto totalViewHeight = gridLayoutInfo_.GetTotalHeightOfItemsInView(mainGap_, true);
2301         auto needSkipHeight = totalViewHeight + gridLayoutInfo_.prevOffset_ + mainGap_;
2302         if (GreatOrEqual(needSkipHeight, -gridLayoutInfo_.currentOffset_)) {
2303             return false;
2304         }
2305 
2306         auto endLine = gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.endMainLineIndex_ + 1);
2307         if (endLine != gridLayoutInfo_.gridMatrix_.end() && !endLine->second.empty()) {
2308             gridLayoutInfo_.currentOffset_ += needSkipHeight;
2309             gridLayoutInfo_.endMainLineIndex_++;
2310             gridLayoutInfo_.startMainLineIndex_ = gridLayoutInfo_.endMainLineIndex_;
2311         }
2312     }
2313     return true;
2314 }
2315 } // namespace OHOS::Ace::NG
2316