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