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