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