• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 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/utils.h"
22 #include "core/components/common/properties/alignment.h"
23 #include "core/components/scroll/scroll_controller_base.h"
24 #include "core/components_ng/layout/layout_algorithm.h"
25 #include "core/components_ng/pattern/grid/grid_item_layout_property.h"
26 #include "core/components_ng/pattern/grid/grid_item_pattern.h"
27 #include "core/components_ng/pattern/grid/grid_pattern.h"
28 #include "core/components_ng/pattern/grid/grid_utils.h"
29 #include "core/components_ng/pattern/pattern.h"
30 #include "core/components_ng/pattern/text_field/text_field_manager.h"
31 #include "core/components_ng/property/layout_constraint.h"
32 #include "core/components_ng/property/measure_utils.h"
33 #include "core/components_ng/property/templates_parser.h"
34 #include "core/pipeline_ng/pipeline_context.h"
35 namespace OHOS::Ace::NG {
Measure(LayoutWrapper * layoutWrapper)36 void GridScrollLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
37 {
38     auto gridLayoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
39     CHECK_NULL_VOID(gridLayoutProperty);
40 
41     // Step1: Decide size of Grid
42     Axis axis = gridLayoutInfo_.axis_;
43     auto idealSize = CreateIdealSize(
44         gridLayoutProperty->GetLayoutConstraint().value(), axis, gridLayoutProperty->GetMeasureType(), true);
45     if (GreatOrEqual(GetMainAxisSize(idealSize, axis), Infinity<float>())) {
46         // TODO: use total height of all children as grid's main size when main size of ideal is infinite
47         LOGE("size of main axis value is infinity, please check");
48         return;
49     }
50     layoutWrapper->GetGeometryNode()->SetFrameSize(idealSize);
51     MinusPaddingToSize(gridLayoutProperty->CreatePaddingAndBorder(), idealSize);
52 
53     InitialItemsCrossSize(gridLayoutProperty, idealSize, layoutWrapper->GetTotalChildCount());
54 
55     // Step2: Measure children that can be displayed in viewport of Grid
56     float mainSize = GetMainAxisSize(idealSize, axis);
57     float crossSize = GetCrossAxisSize(idealSize, axis);
58     if (!NearEqual(mainSize, gridLayoutInfo_.lastMainSize_)) {
59         gridLayoutInfo_.ResetPositionFlags();
60         UpdateOffsetOnVirtualKeyboardHeightChange(layoutWrapper, mainSize);
61     }
62     FillGridViewportAndMeasureChildren(mainSize, crossSize, layoutWrapper);
63 
64     // update cache info.
65     layoutWrapper->SetCacheCount(static_cast<int32_t>(gridLayoutProperty->GetCachedCountValue(1) * crossCount_));
66 
67     AdaptToChildMainSize(layoutWrapper, gridLayoutProperty, mainSize, idealSize);
68 
69     // reset offsetEnd after scroll to moveToEndLineIndex_
70     gridLayoutInfo_.offsetEnd_ = moveToEndLineIndex_ > 0
71                                      ? (gridLayoutInfo_.endIndex_ + 1 >= layoutWrapper->GetTotalChildCount())
72                                      : gridLayoutInfo_.offsetEnd_;
73 }
74 
UpdateOffsetOnVirtualKeyboardHeightChange(LayoutWrapper * layoutWrapper,float mainSize)75 void GridScrollLayoutAlgorithm::UpdateOffsetOnVirtualKeyboardHeightChange(LayoutWrapper* layoutWrapper, float mainSize)
76 {
77     if (GreatOrEqual(mainSize, gridLayoutInfo_.lastMainSize_)) {
78         return;
79     }
80     // only need to offset vertical grid
81     if (gridLayoutInfo_.axis_ != Axis::VERTICAL) {
82         return;
83     }
84 
85     auto grid = layoutWrapper->GetHostNode();
86     CHECK_NULL_VOID_NOLOG(grid);
87     auto focusHub = grid->GetFocusHub();
88     CHECK_NULL_VOID_NOLOG(focusHub);
89     // textField not in Grid
90     if (!focusHub->IsCurrentFocus()) {
91         return;
92     }
93 
94     auto context = PipelineContext::GetCurrentContext();
95     CHECK_NULL_VOID_NOLOG(context);
96     auto textFieldManager = AceType::DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
97     CHECK_NULL_VOID_NOLOG(textFieldManager);
98     // only when textField is onFocus
99     auto focused = textFieldManager->GetOnFocusTextField().Upgrade();
100     CHECK_NULL_VOID_NOLOG(focused);
101     auto position = textFieldManager->GetClickPosition().GetY();
102     auto gridOffset = grid->GetTransformRelativeOffset();
103     auto offset = mainSize + gridOffset.GetY() - position;
104     if (LessOrEqual(offset, 0.0)) {
105         // negative offset to scroll down
106         auto lineHeight = gridLayoutInfo_.GetAverageLineHeight();
107         if (GreatNotEqual(lineHeight, 0)) {
108             offset = floor(offset / lineHeight) * lineHeight;
109         }
110         gridLayoutInfo_.currentOffset_ += offset;
111         LOGI("update offset on virtual keyboard height change, %{public}f", offset);
112     }
113 }
114 
AdaptToChildMainSize(LayoutWrapper * layoutWrapper,RefPtr<GridLayoutProperty> & gridLayoutProperty,float mainSize,const SizeF & idealSize)115 void GridScrollLayoutAlgorithm::AdaptToChildMainSize(LayoutWrapper* layoutWrapper,
116     RefPtr<GridLayoutProperty>& gridLayoutProperty, float mainSize, const SizeF& idealSize)
117 {
118     gridLayoutInfo_.lastMainSize_ = mainSize;
119     // grid with columnsTemplate/rowsTemplate and maxCount
120     if (!gridLayoutProperty->HasMaxCount()) {
121         return;
122     }
123 
124     std::optional<CalcLength> mainAxisIdealSize;
125     const auto& selfLayoutConstraint = gridLayoutProperty->GetCalcLayoutConstraint();
126     if (selfLayoutConstraint && selfLayoutConstraint->selfIdealSize.has_value()) {
127         mainAxisIdealSize = axis_ == Axis::HORIZONTAL ? selfLayoutConstraint->selfIdealSize->Width()
128                                                       : selfLayoutConstraint->selfIdealSize->Height();
129     }
130 
131     if (!mainAxisIdealSize.has_value()) {
132         float lengthOfItemsInViewport = 0;
133         for (auto i = gridLayoutInfo_.startMainLineIndex_; i <= gridLayoutInfo_.endMainLineIndex_; i++) {
134             lengthOfItemsInViewport += (gridLayoutInfo_.lineHeightMap_[i] + mainGap_);
135         }
136         lengthOfItemsInViewport -= mainGap_;
137         auto gridMainSize = std::min(lengthOfItemsInViewport, mainSize);
138         // should use minCount * cellLength ?
139         if (axis_ == Axis::HORIZONTAL) {
140             gridMainSize = std::max(gridMainSize, gridLayoutProperty->GetLayoutConstraint()->minSize.Width());
141             layoutWrapper->GetGeometryNode()->SetFrameSize(SizeF(gridMainSize, idealSize.Height()));
142         } else {
143             gridMainSize = std::max(gridMainSize, gridLayoutProperty->GetLayoutConstraint()->minSize.Height());
144             layoutWrapper->GetGeometryNode()->SetFrameSize(SizeF(idealSize.Width(), gridMainSize));
145         }
146         gridLayoutInfo_.lastMainSize_ = gridMainSize;
147         LOGI("gridMainSize:%{public}f", gridMainSize);
148     }
149 }
150 
Layout(LayoutWrapper * layoutWrapper)151 void GridScrollLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
152 {
153     auto gridLayoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
154     CHECK_NULL_VOID(gridLayoutProperty);
155     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
156     auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
157     MinusPaddingToSize(padding, size);
158     auto childFrameOffset = OffsetF(padding.left.value_or(0.0f), padding.top.value_or(0.0f));
159     childFrameOffset += gridLayoutProperty->IsVertical() ? OffsetF(0.0f, gridLayoutInfo_.currentOffset_)
160                                                          : OffsetF(gridLayoutInfo_.currentOffset_, 0.0f);
161 
162     float prevLineHeight = 0.0f;
163     layoutWrapper->RemoveAllChildInRenderTree();
164     LargeItemForwardLineHeight(gridLayoutInfo_.startMainLineIndex_, layoutWrapper);
165     for (auto i = gridLayoutInfo_.startMainLineIndex_; i <= gridLayoutInfo_.endMainLineIndex_; i++) {
166         const auto& line = gridLayoutInfo_.gridMatrix_.find(i);
167         if (line == gridLayoutInfo_.gridMatrix_.end()) {
168             continue;
169         }
170 
171         auto prevLineOffset = axis_ == Axis::VERTICAL ? OffsetF(0.0, prevLineHeight) : OffsetF(prevLineHeight, 0.0);
172         if (line->second.empty()) {
173             LOGE("line %{public}d should not be empty, please check.", line->first);
174             break;
175         }
176         int32_t itemIdex = -1;
177         float lineHeight = gridLayoutInfo_.lineHeightMap_[line->first];
178         for (auto iter = line->second.begin(); iter != line->second.end(); iter++) {
179             // If item index is the same, must be the same GridItem, need't layout again.
180             if (itemIdex == iter->second) {
181                 continue;
182             }
183             itemIdex = iter->second;
184             auto crossIter = itemsCrossPosition_.find(itemIdex);
185             if (crossIter == itemsCrossPosition_.end()) {
186                 LOGI("item %{public}d not in cross position", itemIdex);
187                 continue;
188             }
189             auto crossOffset = crossIter->second;
190             auto offset = childFrameOffset + prevLineOffset;
191             offset = CalculateLargeItemOffset(offset, itemIdex, i, iter->first);
192             if (axis_ == Axis::VERTICAL) {
193                 offset.SetX(crossOffset);
194             } else {
195                 offset.SetY(crossOffset);
196             }
197             auto wrapper = layoutWrapper->GetOrCreateChildByIndex(itemIdex);
198             if (!wrapper) {
199                 LOGE("Layout item wrapper of index: %{public}d is null, please check.", itemIdex);
200                 continue;
201             }
202             auto frSize = itemsCrossSize_.at(iter->first);
203             SizeF blockSize = gridLayoutProperty->IsVertical() ? SizeF(frSize, lineHeight) : SizeF(lineHeight, frSize);
204             auto translate = OffsetF(0.0f, 0.0f);
205             if (axis_ == Axis::VERTICAL) {
206                 translate = Alignment::GetAlignPosition(
207                     blockSize, wrapper->GetGeometryNode()->GetMarginFrameSize(), Alignment::TOP_CENTER);
208             } else {
209                 translate = Alignment::GetAlignPosition(
210                     blockSize, wrapper->GetGeometryNode()->GetMarginFrameSize(), Alignment::CENTER_LEFT);
211             }
212 
213             wrapper->GetGeometryNode()->SetMarginFrameOffset(offset + translate);
214             wrapper->Layout();
215             auto layoutProperty = wrapper->GetLayoutProperty();
216             CHECK_NULL_VOID(layoutProperty);
217             auto gridItemLayoutProperty = AceType::DynamicCast<GridItemLayoutProperty>(layoutProperty);
218             CHECK_NULL_VOID(gridItemLayoutProperty);
219             gridItemLayoutProperty->UpdateMainIndex(line->first);
220             gridItemLayoutProperty->UpdateCrossIndex(iter->first);
221         }
222         prevLineHeight += gridLayoutInfo_.lineHeightMap_[line->first] + mainGap_;
223     }
224     gridLayoutInfo_.totalHeightOfItemsInView_ = gridLayoutInfo_.GetTotalHeightOfItemsInView(mainGap_);
225 }
226 
InitialItemsCrossSize(const RefPtr<GridLayoutProperty> & layoutProperty,const SizeF & frameSize,int32_t childrenCount)227 void GridScrollLayoutAlgorithm::InitialItemsCrossSize(
228     const RefPtr<GridLayoutProperty>& layoutProperty, const SizeF& frameSize, int32_t childrenCount)
229 {
230     itemsCrossSize_.clear();
231     auto rowsTemplate = layoutProperty->GetRowsTemplate().value_or("");
232     auto columnsTemplate = layoutProperty->GetColumnsTemplate().value_or("");
233     axis_ = columnsTemplate.empty() ? Axis::HORIZONTAL : Axis::VERTICAL;
234     auto scale = layoutProperty->GetLayoutConstraint()->scaleProperty;
235     auto rowsGap = ConvertToPx(layoutProperty->GetRowsGap().value_or(0.0_vp), scale, frameSize.Height()).value_or(0);
236     auto columnsGap =
237         ConvertToPx(layoutProperty->GetColumnsGap().value_or(0.0_vp), scale, frameSize.Width()).value_or(0);
238     mainGap_ = axis_ == Axis::HORIZONTAL ? columnsGap : rowsGap;
239     crossGap_ = axis_ == Axis::VERTICAL ? columnsGap : rowsGap;
240     auto padding = layoutProperty->CreatePaddingAndBorder();
241     crossPaddingOffset_ = axis_ == Axis::HORIZONTAL ? padding.top.value_or(0) : padding.left.value_or(0);
242 
243     auto crossSize = frameSize.CrossSize(axis_);
244     std::vector<double> crossLens;
245     std::pair<std::vector<double>, bool> cross;
246     if (!rowsTemplate.empty()) {
247         cross = ParseTemplateArgs(GridUtils::ParseArgs(rowsTemplate), crossSize, crossGap_, childrenCount);
248     } else {
249         cross = ParseTemplateArgs(GridUtils::ParseArgs(columnsTemplate), crossSize, crossGap_, childrenCount);
250     }
251     crossLens = cross.first;
252     if (cross.second) {
253         crossGap_ = 0.0f;
254     }
255 
256     if (crossLens.empty()) {
257         crossLens.push_back(crossSize);
258     }
259 
260     if (crossCount_ != crossLens.size()) {
261         crossCount_ = crossLens.size();
262         gridLayoutInfo_.crossCount_ = crossCount_;
263     }
264 
265     int32_t index = 0;
266     for (const auto& len : crossLens) {
267         itemsCrossSize_.try_emplace(index, len);
268         ++index;
269     }
270 }
271 
FillGridViewportAndMeasureChildren(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)272 void GridScrollLayoutAlgorithm::FillGridViewportAndMeasureChildren(
273     float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
274 {
275     itemsCrossPosition_.clear();
276     UpdateGridLayoutInfo(layoutWrapper, mainSize);
277     SkipLargeOffset(mainSize, layoutWrapper);
278 
279     if (!gridLayoutInfo_.lastCrossCount_) {
280         gridLayoutInfo_.lastCrossCount_ = crossCount_;
281     }
282 
283     if (gridLayoutInfo_.lastCrossCount_ != crossCount_ || layoutWrapper->GetHostNode()->GetChildrenUpdated() != -1 ||
284         gridLayoutInfo_.IsResetted()) {
285         gridLayoutInfo_.lastCrossCount_ = crossCount_;
286         gridLayoutInfo_.lineHeightMap_.clear();
287         gridLayoutInfo_.gridMatrix_.clear();
288         gridLayoutInfo_.irregularItemsPosition_.clear();
289         gridLayoutInfo_.endIndex_ = -1;
290         gridLayoutInfo_.endMainLineIndex_ = 0;
291         gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
292         gridLayoutInfo_.ResetPositionFlags();
293         isChildrenUpdated_ = true;
294 
295         int32_t currentItemIndex = gridLayoutInfo_.startIndex_;
296         auto firstItem = GetStartingItem(layoutWrapper, currentItemIndex);
297         gridLayoutInfo_.startIndex_ = firstItem;
298         currentMainLineIndex_ = (firstItem == 0 ? 0 : gridLayoutInfo_.startMainLineIndex_) - 1;
299         gridLayoutInfo_.endIndex_ = firstItem - 1;
300         LOGI("data reload begin, firstItem:%{public}d, currentItemIndex:%{public}d", firstItem, currentItemIndex);
301         while (gridLayoutInfo_.endIndex_ < currentItemIndex) {
302             auto lineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, false);
303             if (LessNotEqual(lineHeight, 0.0)) {
304                 gridLayoutInfo_.reachEnd_ = true;
305                 break;
306             }
307         }
308         gridLayoutInfo_.startMainLineIndex_ = currentMainLineIndex_;
309         gridLayoutInfo_.UpdateStartIndexByStartLine();
310         // FillNewLineBackward sometimes make startIndex_ > currentItemIndex
311         while (gridLayoutInfo_.startIndex_ > currentItemIndex &&
312                gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.startMainLineIndex_) !=
313                    gridLayoutInfo_.gridMatrix_.end()) {
314             gridLayoutInfo_.startMainLineIndex_--;
315             gridLayoutInfo_.UpdateStartIndexByStartLine();
316         }
317         LOGI("data reload end, startIndex_:%{public}d, startMainLineIndex_:%{public}d", gridLayoutInfo_.startIndex_,
318             gridLayoutInfo_.startMainLineIndex_);
319     }
320 
321     if (gridLayoutInfo_.scrollAlign_ == ScrollAlign::CENTER || gridLayoutInfo_.scrollAlign_ == ScrollAlign::END) {
322         UpdateCurrentOffsetForJumpTo(layoutWrapper, mainSize);
323     }
324     gridLayoutInfo_.jumpIndex_ = -1;
325     gridLayoutInfo_.scrollAlign_ = ScrollAlign::AUTO;
326 
327     // Step1: Measure [GridItem] that has been recorded to [gridMatrix_]
328     float mainLength = MeasureRecordedItems(mainSize, crossSize, layoutWrapper);
329 
330     // Step2: When done measure items in record, request new items to fill blank at end
331     FillBlankAtEnd(mainSize, crossSize, layoutWrapper, mainLength);
332     if (gridLayoutInfo_.reachEnd_) { // If it reaches end when [FillBlankAtEnd], modify [currentOffset_]
333         ModifyCurrentOffsetWhenReachEnd(mainSize);
334     }
335 
336     // Step3: Check if need to fill blank at start (in situation of grid items moving down)
337     auto haveNewLineAtStart = FillBlankAtStart(mainSize, crossSize, layoutWrapper);
338     if (gridLayoutInfo_.reachStart_) {
339         auto offset = gridLayoutInfo_.currentOffset_;
340         if (!canOverScroll_) {
341             gridLayoutInfo_.currentOffset_ = 0.0;
342             gridLayoutInfo_.prevOffset_ = 0.0;
343         }
344         if (!haveNewLineAtStart) {
345             layoutWrapper->GetHostNode()->ChildrenUpdatedFrom(-1);
346             return;
347         }
348         // we need lastline if blank at start is not fully filled when start line is shorter
349         mainLength -= offset;
350         currentMainLineIndex_ = gridLayoutInfo_.endMainLineIndex_;
351         if (UseCurrentLines(mainSize, crossSize, layoutWrapper, mainLength)) {
352             FillBlankAtEnd(mainSize, crossSize, layoutWrapper, mainLength);
353             if (gridLayoutInfo_.reachEnd_) {
354                 ModifyCurrentOffsetWhenReachEnd(mainSize);
355             }
356         }
357     }
358 
359     layoutWrapper->GetHostNode()->ChildrenUpdatedFrom(-1);
360 }
361 
FillBlankAtStart(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)362 bool GridScrollLayoutAlgorithm::FillBlankAtStart(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
363 {
364     bool fillNewLine = false;
365     // If [currentOffset_] is non-positive, it means no blank at start
366     if (LessOrEqual(gridLayoutInfo_.currentOffset_, 0.0)) {
367         return fillNewLine;
368     }
369     auto blankAtStart = gridLayoutInfo_.currentOffset_;
370     while (GreatNotEqual(blankAtStart, 0.0)) {
371         float lineHeight = FillNewLineForward(crossSize, mainSize, layoutWrapper);
372         if (GreatNotEqual(lineHeight, 0.0)) {
373             gridLayoutInfo_.lineHeightMap_[gridLayoutInfo_.startMainLineIndex_] = lineHeight;
374             blankAtStart -= (lineHeight + mainGap_);
375             fillNewLine = true;
376             continue;
377         }
378         gridLayoutInfo_.reachStart_ = true;
379         break;
380     }
381     gridLayoutInfo_.currentOffset_ = blankAtStart;
382     gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
383     return fillNewLine;
384 }
385 
386 // When a moving up event comes, the [currentOffset_] may have been reduced too much than the items really need to
387 // be moved up, so we need to modify [currentOffset_] according to previous position.
ModifyCurrentOffsetWhenReachEnd(float mainSize)388 void GridScrollLayoutAlgorithm::ModifyCurrentOffsetWhenReachEnd(float mainSize)
389 {
390     // Step1. Calculate total length of all items with main gap in viewport.
391     // [lengthOfItemsInViewport] must be greater than or equal to viewport height
392     float lengthOfItemsInViewport = gridLayoutInfo_.GetTotalHeightOfItemsInView(mainGap_);
393     // scroll forward
394     if (LessNotEqual(gridLayoutInfo_.prevOffset_, gridLayoutInfo_.currentOffset_)) {
395         if (!canOverScroll_) {
396             gridLayoutInfo_.reachEnd_ = false;
397             return;
398         } else if (!isChildrenUpdated_) {
399             if (LessNotEqual(lengthOfItemsInViewport, mainSize)) {
400                 return;
401             }
402         }
403     }
404     // Step2. Calculate real offset that items can only be moved up by.
405     // Hint: [prevOffset_] is a non-positive value
406     if (LessNotEqual(lengthOfItemsInViewport, mainSize) && gridLayoutInfo_.startIndex_ == 0) {
407         if (!canOverScroll_ || isChildrenUpdated_) {
408             gridLayoutInfo_.currentOffset_ = 0;
409             gridLayoutInfo_.prevOffset_ = 0;
410         }
411         gridLayoutInfo_.reachStart_ = true;
412         gridLayoutInfo_.offsetEnd_ = true;
413         return;
414     }
415 
416     // last grid item is not fully showed
417     if (GreatNotEqual(gridLayoutInfo_.currentOffset_ + lengthOfItemsInViewport, mainSize)) {
418         return;
419     }
420 
421     // Step3. modify [currentOffset_]
422     if (!canOverScroll_) {
423         float realOffsetToMoveUp = lengthOfItemsInViewport - mainSize + gridLayoutInfo_.prevOffset_;
424         gridLayoutInfo_.currentOffset_ = gridLayoutInfo_.prevOffset_ - realOffsetToMoveUp;
425         gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
426     }
427     gridLayoutInfo_.offsetEnd_ = true;
428 }
429 
FillBlankAtEnd(float mainSize,float crossSize,LayoutWrapper * layoutWrapper,float & mainLength)430 void GridScrollLayoutAlgorithm::FillBlankAtEnd(
431     float mainSize, float crossSize, LayoutWrapper* layoutWrapper, float& mainLength)
432 {
433     if (GreatNotEqual(mainLength, mainSize)) {
434         return;
435     }
436     // fill current line first
437     auto mainIter = gridLayoutInfo_.gridMatrix_.find(currentMainLineIndex_);
438     auto nextMain = gridLayoutInfo_.gridMatrix_.find(currentMainLineIndex_ + 1);
439     if (mainIter != gridLayoutInfo_.gridMatrix_.end() && mainIter->second.size() < crossCount_ &&
440         nextMain == gridLayoutInfo_.gridMatrix_.end()) {
441         auto currentIndex = gridLayoutInfo_.endIndex_ + 1;
442         cellAveLength_ = -1.0f;
443         bool hasNormalItem = false;
444         lastCross_ = 0;
445         for (uint32_t i = (mainIter->second.empty() ? 0 : mainIter->second.rbegin()->first); i < crossCount_; i++) {
446             // Step1. Get wrapper of [GridItem]
447             auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
448             if (!itemWrapper) {
449                 break;
450             }
451             // Step2. Measure child
452             auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
453             auto childState = MeasureNewChild(frameSize, currentIndex, layoutWrapper, itemWrapper, false);
454             if (childState == -1) {
455                 cellAveLength_ = LessNotEqual(cellAveLength_, 0.0)
456                                      ? gridLayoutInfo_.lineHeightMap_.find(currentMainLineIndex_ - 1)->second
457                                      : cellAveLength_;
458                 --currentIndex;
459                 break;
460             }
461             i += childState - 1;
462             // Step3. Measure [GridItem]
463             LargeItemLineHeight(itemWrapper, hasNormalItem);
464             gridLayoutInfo_.endIndex_ = currentIndex;
465             currentIndex++;
466         }
467     }
468     // When [mainLength] is still less than [mainSize], do [FillNewLineBackward] repeatedly until filling up the lower
469     // part of the viewport
470     while (LessNotEqual(mainLength, mainSize)) {
471         float lineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, false);
472         if (GreatOrEqual(lineHeight, 0.0)) {
473             mainLength += lineHeight;
474             continue;
475         }
476         gridLayoutInfo_.reachEnd_ = true;
477         return;
478     };
479     // last line make LessNotEqual(mainLength, mainSize) and continue is reach end too
480     gridLayoutInfo_.reachEnd_ = gridLayoutInfo_.endIndex_ == layoutWrapper->GetTotalChildCount() - 1;
481 }
482 
CalculateLargeItemOffset(OffsetF currOffset,int32_t itemIndex,int32_t currLineIndex,int32_t currentCrossIndex)483 OffsetF GridScrollLayoutAlgorithm::CalculateLargeItemOffset(
484     OffsetF currOffset, int32_t itemIndex, int32_t currLineIndex, int32_t currentCrossIndex)
485 {
486     OffsetF offset = currOffset;
487     for (int32_t lastCrossIndex = currLineIndex - 1; lastCrossIndex >= 0; lastCrossIndex--) {
488         auto LastGridMatrixIter = gridLayoutInfo_.gridMatrix_.find(lastCrossIndex);
489         if (LastGridMatrixIter == gridLayoutInfo_.gridMatrix_.end()) {
490             continue;
491         }
492         auto lastGridItemRecord = LastGridMatrixIter->second;
493         auto lastLineCrossItem = lastGridItemRecord.find(currentCrossIndex);
494         if (lastLineCrossItem == lastGridItemRecord.end()) {
495             continue;
496         }
497         if (lastLineCrossItem->second == itemIndex) {
498             offset -= axis_ == Axis::VERTICAL ? OffsetF(0, gridLayoutInfo_.lineHeightMap_[lastCrossIndex] + mainGap_)
499                                               : OffsetF(gridLayoutInfo_.lineHeightMap_[lastCrossIndex] + mainGap_, 0.0);
500         } else {
501             break;
502         }
503     }
504 
505     return offset;
506 }
507 
NeedAdjust(const RefPtr<GridItemLayoutProperty> & itemLayoutProperty)508 bool GridScrollLayoutAlgorithm::NeedAdjust(const RefPtr<GridItemLayoutProperty>& itemLayoutProperty)
509 {
510     bool needAdjust = false;
511     auto main = axis_ == Axis::VERTICAL ? mainCount_: crossCount_;
512     auto cross = axis_ == Axis::VERTICAL ? crossCount_: mainCount_;
513     if (itemLayoutProperty->GetRowStart().has_value()) {
514         currentItemRowStart_ = itemLayoutProperty->GetRowStart().value_or(-1);
515         if ((currentItemRowStart_ < 0) || (currentItemRowStart_ >= static_cast<int32_t>(main))) {
516             needAdjust = true;
517         }
518     }
519     if (itemLayoutProperty->GetRowEnd().has_value()) {
520         currentItemRowEnd_ = itemLayoutProperty->GetRowEnd().value_or(-1);
521         if ((currentItemRowEnd_ < 0) || (currentItemRowEnd_ >= static_cast<int32_t>(main))) {
522             needAdjust = true;
523         }
524     }
525     if (itemLayoutProperty->GetColumnStart().has_value()) {
526         currentItemColStart_ = itemLayoutProperty->GetColumnStart().value_or(-1);
527         if ((currentItemColStart_ < 0) || (currentItemColStart_ >= static_cast<int32_t>(cross))) {
528             needAdjust = true;
529         }
530     }
531     if (itemLayoutProperty->GetColumnEnd().has_value()) {
532         currentItemColEnd_ = itemLayoutProperty->GetColumnEnd().value_or(-1);
533         if ((currentItemColEnd_ < 0) || (currentItemColEnd_ >= static_cast<int32_t>(cross))) {
534             needAdjust = true;
535         }
536     }
537     return needAdjust;
538 }
539 
AdjustRowColSpan(const RefPtr<LayoutWrapper> & itemLayoutWrapper,LayoutWrapper *,int32_t)540 void GridScrollLayoutAlgorithm::AdjustRowColSpan(
541     const RefPtr<LayoutWrapper>& itemLayoutWrapper, LayoutWrapper* /* layoutWrapper */, int32_t /* itemIndex */)
542 {
543     auto itemLayoutProperty = DynamicCast<GridItemLayoutProperty>(itemLayoutWrapper->GetLayoutProperty());
544     CHECK_NULL_VOID(itemLayoutProperty);
545     bool needAdjust = false;
546     currentItemRowSpan_ = 1;
547     currentItemColSpan_ = 1;
548     currentItemRowStart_ = -1;
549     currentItemColStart_ = -1;
550     currentItemColEnd_ = -1;
551     currentItemRowEnd_ = -1;
552     needAdjust = NeedAdjust(itemLayoutProperty);
553     if (!needAdjust) {
554         currentItemRowSpan_ = std::max(currentItemRowEnd_ - currentItemRowStart_ + 1, 1);
555         currentItemColSpan_ = std::max(currentItemColEnd_ - currentItemColStart_ + 1, 1);
556     } else {
557         currentItemRowStart_ = -1;
558         currentItemColStart_ = -1;
559         currentItemColEnd_ = -1;
560         currentItemRowEnd_ = -1;
561     }
562     if ((currentItemRowStart_ == -1 && currentItemRowEnd_ != -1) ||
563         (currentItemRowEnd_ == -1 && currentItemRowStart_ != -1) ||
564         (currentItemColStart_ == -1 && currentItemColEnd_ != -1) ||
565         (currentItemColEnd_ == -1 && currentItemColStart_ != -1)) {
566         currentItemRowSpan_ = 1;
567         currentItemColSpan_ = 1;
568         currentItemRowStart_ = -1;
569         currentItemColStart_ = -1;
570         currentItemColEnd_ = -1;
571         currentItemRowEnd_ = -1;
572     }
573     if (currentItemRowSpan_ > 1 || currentItemColSpan_ > 1) {
574         gridLayoutInfo_.hasBigItem_ = true;
575     }
576 }
577 
LargeItemLineHeight(const RefPtr<LayoutWrapper> & itemWrapper,bool & hasNormalItem)578 void GridScrollLayoutAlgorithm::LargeItemLineHeight(const RefPtr<LayoutWrapper>& itemWrapper, bool& hasNormalItem)
579 {
580     AdjustRowColSpan(itemWrapper, nullptr, 0);
581     auto mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
582     auto itemSize = itemWrapper->GetGeometryNode()->GetMarginFrameSize();
583     if (mainSpan == 1) {
584         cellAveLength_ = std::max(GetMainAxisSize(itemSize, gridLayoutInfo_.axis_), cellAveLength_);
585         hasNormalItem = true;
586     }
587 
588     if ((mainSpan > 1) && !hasNormalItem) {
589         cellAveLength_ = std::max(GetMainAxisSize(itemSize, gridLayoutInfo_.axis_) / mainSpan, cellAveLength_);
590     }
591 }
592 
IsIndexInMatrix(int32_t index,int32_t & startLine)593 bool GridScrollLayoutAlgorithm::IsIndexInMatrix(int32_t index, int32_t& startLine)
594 {
595     auto iter = std::find_if(gridLayoutInfo_.gridMatrix_.begin(), gridLayoutInfo_.gridMatrix_.end(),
596         [index, &startLine](const std::pair<int32_t, std::map<int32_t, int32_t>>& item) {
597             for (auto& subitem : item.second) {
598                 if (subitem.second == index) {
599                     startLine = item.first;
600                     return true;
601                 }
602             }
603             return false;
604         });
605     return (iter != gridLayoutInfo_.gridMatrix_.end());
606 }
607 
GetTargetIndexInfoWithBenchMark(LayoutWrapper * layoutWrapper,bool isTargetBackward,int32_t targetIndex)608 void GridScrollLayoutAlgorithm::GetTargetIndexInfoWithBenchMark(
609     LayoutWrapper* layoutWrapper, bool isTargetBackward, int32_t targetIndex)
610 {
611     int32_t benchmarkIndex = isTargetBackward ? gridLayoutInfo_.gridMatrix_.rbegin()->second.rbegin()->second + 1 : 0;
612     int32_t mainStartIndex = isTargetBackward ? gridLayoutInfo_.gridMatrix_.rbegin()->first + 1 : 0;
613     int32_t currentIndex = benchmarkIndex;
614     int32_t headOfMainStartLine = currentIndex;
615 
616     while (currentIndex < targetIndex) {
617         int32_t crossGridReserve = gridLayoutInfo_.crossCount_;
618         /* go through a new line */
619         while ((crossGridReserve > 0) && (currentIndex <= targetIndex)) {
620             auto currentWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex, false);
621             CHECK_NULL_VOID(currentWrapper);
622             auto layoutProperty = DynamicCast<GridItemLayoutProperty>(currentWrapper->GetLayoutProperty());
623             CHECK_NULL_VOID(layoutProperty);
624             auto gridSpan = layoutProperty->GetCrossSpan(gridLayoutInfo_.axis_);
625             if (crossGridReserve >= gridSpan) {
626                 crossGridReserve -= gridSpan;
627             } else if (gridLayoutInfo_.crossCount_ >= gridSpan) {
628                 ++mainStartIndex;
629                 headOfMainStartLine = currentIndex;
630                 crossGridReserve = gridLayoutInfo_.crossCount_ - gridSpan;
631             }
632             ++currentIndex;
633         }
634         if (currentIndex > targetIndex) {
635             break;
636         }
637         ++mainStartIndex;
638         headOfMainStartLine = currentIndex;
639     }
640     gridLayoutInfo_.startMainLineIndex_ = mainStartIndex;
641     gridLayoutInfo_.startIndex_ = headOfMainStartLine;
642     gridLayoutInfo_.endIndex_ = headOfMainStartLine - 1;
643     gridLayoutInfo_.prevOffset_ = 0;
644     gridLayoutInfo_.currentOffset_ = 0;
645     gridLayoutInfo_.ResetPositionFlags();
646     gridLayoutInfo_.gridMatrix_.clear();
647     gridLayoutInfo_.lineHeightMap_.clear();
648     gridLayoutInfo_.irregularItemsPosition_.clear();
649 }
650 
UpdateGridLayoutInfo(LayoutWrapper * layoutWrapper,float mainSize)651 void GridScrollLayoutAlgorithm::UpdateGridLayoutInfo(LayoutWrapper* layoutWrapper, float mainSize)
652 {
653     /* 1. Have gotten gridLayoutInfo_.startMainLineIndex_ and directly jump to it */
654     if (gridLayoutInfo_.jumpIndex_ < 0) {
655         return;
656     }
657     /* 2. Need to find out the startMainLineIndex according to startIndex */
658     int32_t targetIndex = gridLayoutInfo_.jumpIndex_;
659     /* 2.1 invalid targetIndex */
660     if (layoutWrapper->GetTotalChildCount() <= targetIndex) {
661         return;
662     }
663 
664     switch (gridLayoutInfo_.scrollAlign_) {
665         case ScrollAlign::START:
666         case ScrollAlign::END:
667         case ScrollAlign::CENTER:
668             ScrollToIndexStart(layoutWrapper, targetIndex);
669             return;
670         default:
671             ScrollToIndexAuto(layoutWrapper, mainSize, targetIndex);
672     }
673 }
674 
ScrollToIndexAuto(LayoutWrapper * layoutWrapper,float mainSize,int32_t targetIndex)675 void GridScrollLayoutAlgorithm::ScrollToIndexAuto(LayoutWrapper* layoutWrapper, float mainSize, int32_t targetIndex)
676 {
677     int32_t startLine = 0;
678     if (IsIndexInMatrix(targetIndex, startLine)) {
679         if (startLine < gridLayoutInfo_.endMainLineIndex_ && startLine > gridLayoutInfo_.startMainLineIndex_) {
680             return;
681         }
682 
683         if (startLine >= gridLayoutInfo_.endMainLineIndex_) {
684             auto totalViewHeight = gridLayoutInfo_.GetTotalHeightOfItemsInView(mainGap_);
685             gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
686             gridLayoutInfo_.currentOffset_ -= (totalViewHeight - mainSize + gridLayoutInfo_.currentOffset_);
687             for (int32_t i = gridLayoutInfo_.endMainLineIndex_ + 1; i <= startLine; ++i) {
688                 gridLayoutInfo_.currentOffset_ -= (mainGap_ + gridLayoutInfo_.lineHeightMap_[i]);
689             }
690             gridLayoutInfo_.ResetPositionFlags();
691             return;
692         }
693 
694         // startLine <= gridLayoutInfo_.startMainLineIndex_
695         gridLayoutInfo_.startMainLineIndex_ = startLine;
696         gridLayoutInfo_.UpdateStartIndexByStartLine();
697         gridLayoutInfo_.prevOffset_ = 0;
698         gridLayoutInfo_.currentOffset_ = 0;
699         gridLayoutInfo_.ResetPositionFlags();
700         return;
701     }
702 
703     /* 2.3 targetIndex is out of the matrix */
704     if (gridLayoutInfo_.gridMatrix_.empty()) {
705         LOGW("no grid for jump to index:%{public}d", targetIndex);
706         return;
707     }
708     bool isTargetBackward = true;
709     if (targetIndex < gridLayoutInfo_.gridMatrix_.begin()->second.begin()->second) {
710         isTargetBackward = false;
711     } else if (targetIndex > gridLayoutInfo_.gridMatrix_.rbegin()->second.rbegin()->second) {
712         isTargetBackward = true;
713     } else {
714         return;
715     }
716     auto grid = layoutWrapper->GetHostNode();
717     CHECK_NULL_VOID(grid);
718     grid->ChildrenUpdatedFrom(0);
719     GetTargetIndexInfoWithBenchMark(layoutWrapper, isTargetBackward, targetIndex);
720     moveToEndLineIndex_ = isTargetBackward ? targetIndex : moveToEndLineIndex_;
721 }
722 
ScrollToIndexStart(LayoutWrapper * layoutWrapper,int32_t targetIndex)723 void GridScrollLayoutAlgorithm::ScrollToIndexStart(LayoutWrapper* layoutWrapper, int32_t targetIndex)
724 {
725     int32_t startLine = 0;
726     /* targetIndex is in the matrix */
727     if (IsIndexInMatrix(targetIndex, startLine)) {
728         if (startLine == gridLayoutInfo_.startMainLineIndex_) {
729             gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
730             gridLayoutInfo_.currentOffset_ = 0;
731             gridLayoutInfo_.ResetPositionFlags();
732             return;
733         }
734 
735         gridLayoutInfo_.startMainLineIndex_ = startLine;
736         gridLayoutInfo_.UpdateStartIndexByStartLine();
737         gridLayoutInfo_.prevOffset_ = 0;
738         gridLayoutInfo_.currentOffset_ = 0;
739         gridLayoutInfo_.ResetPositionFlags();
740         return;
741     }
742     /* targetIndex is out of the matrix */
743     if (gridLayoutInfo_.gridMatrix_.empty()) {
744         LOGW("no grid for jump to index:%{public}d", targetIndex);
745         return;
746     }
747     bool isTargetBackward = true;
748     if (targetIndex < gridLayoutInfo_.gridMatrix_.begin()->second.begin()->second) {
749         isTargetBackward = false;
750     } else if (targetIndex > gridLayoutInfo_.gridMatrix_.rbegin()->second.rbegin()->second) {
751         isTargetBackward = true;
752     } else {
753         return;
754     }
755     auto grid = layoutWrapper->GetHostNode();
756     CHECK_NULL_VOID(grid);
757     grid->ChildrenUpdatedFrom(0);
758     GetTargetIndexInfoWithBenchMark(layoutWrapper, isTargetBackward, targetIndex);
759 }
760 
UpdateCurrentOffsetForJumpTo(LayoutWrapper * layoutWrapper,float mainSize)761 void GridScrollLayoutAlgorithm::UpdateCurrentOffsetForJumpTo(LayoutWrapper* layoutWrapper, float mainSize)
762 {
763     int32_t startLine = 0;
764     /* targetIndex is in the matrix */
765     if (IsIndexInMatrix(gridLayoutInfo_.jumpIndex_, startLine)) {
766         // scroll to end of the screen
767         gridLayoutInfo_.currentOffset_ = mainSize - gridLayoutInfo_.lineHeightMap_[startLine];
768         // scroll to center of the screen
769         if (gridLayoutInfo_.scrollAlign_ == ScrollAlign::CENTER) {
770             gridLayoutInfo_.currentOffset_ /= 2;
771         }
772         gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
773         return;
774     }
775     /* targetIndex is out of the matrix */
776     LOGW("can not find jumpIndex in Grid Matrix :%{public}d", gridLayoutInfo_.jumpIndex_);
777 }
778 
MeasureRecordedItems(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)779 float GridScrollLayoutAlgorithm::MeasureRecordedItems(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
780 {
781     currentMainLineIndex_ = gridLayoutInfo_.startMainLineIndex_ - 1;
782     float mainLength = gridLayoutInfo_.currentOffset_;
783     // already at start line, do not use offset for mainLength
784     if (gridLayoutInfo_.startMainLineIndex_ == 0 && GreatNotEqual(mainLength, 0)) {
785         mainLength = 0;
786     }
787     UseCurrentLines(mainSize, crossSize, layoutWrapper, mainLength);
788     return mainLength;
789 }
790 
UseCurrentLines(float mainSize,float crossSize,LayoutWrapper * layoutWrapper,float & mainLength)791 bool GridScrollLayoutAlgorithm::UseCurrentLines(
792     float mainSize, float crossSize, LayoutWrapper* layoutWrapper, float& mainLength)
793 {
794     bool runOutOfRecord = false;
795     // Measure grid items row by row
796     int32_t tempEndIndex = -1;
797     while (LessNotEqual(mainLength, mainSize)) {
798         // If [gridMatrix_] does not contain record of line [currentMainLineIndex_], do [FillNewLineBackward]
799         auto gridMatrixIter = gridLayoutInfo_.gridMatrix_.find(++currentMainLineIndex_);
800         if ((gridMatrixIter == gridLayoutInfo_.gridMatrix_.end()) ||
801             (gridLayoutInfo_.lineHeightMap_.find(currentMainLineIndex_) == gridLayoutInfo_.lineHeightMap_.end())) {
802             runOutOfRecord = true;
803             break;
804         }
805         int32_t currentIndex = -1;
806         bool hasNormalItem = false;
807         cellAveLength_ = -1.0f;
808         for (const auto& gridItemRecord : gridMatrixIter->second) {
809             if (currentIndex == gridItemRecord.second) {
810                 continue;
811             }
812             currentIndex = gridItemRecord.second;
813             if (currentIndex == -1) {
814                 // move from another grid
815                 continue;
816             }
817             auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
818             if (!itemWrapper) {
819                 LOGE("GridItem wrapper of index %{public}u null", currentIndex);
820                 break;
821             }
822             auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
823             AdjustRowColSpan(itemWrapper, layoutWrapper, currentIndex);
824             auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
825             if (crossStart == -1) {
826                 MeasureChildPlaced(frameSize, currentIndex, gridItemRecord.first, layoutWrapper, itemWrapper);
827             } else {
828                 MeasureChildPlaced(frameSize, currentIndex, crossStart, layoutWrapper, itemWrapper);
829             }
830             // Record end index. When fill new line, the [endIndex_] will be the first item index to request
831             LargeItemLineHeight(itemWrapper, hasNormalItem);
832             tempEndIndex = std::max(currentIndex, tempEndIndex);
833             gridLayoutInfo_.endIndex_ = tempEndIndex;
834         }
835 
836         if (GreatOrEqual(cellAveLength_, 0.0)) { // Means at least one item has been measured
837             gridLayoutInfo_.lineHeightMap_[currentMainLineIndex_] = cellAveLength_;
838             mainLength += (cellAveLength_ + mainGap_);
839         }
840         // If a line moves up out of viewport, update [startIndex_], [currentOffset_] and [startMainLineIndex_]
841         if (LessNotEqual(mainLength, 0.0)) {
842             gridLayoutInfo_.currentOffset_ = mainLength;
843             gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
844             gridLayoutInfo_.startMainLineIndex_ = currentMainLineIndex_ + 1;
845             gridLayoutInfo_.UpdateStartIndexByStartLine();
846         }
847     }
848     // Case 1. if this while-loop breaks due to running out of records, the [currentMainLineIndex_] is larger by 1 than
849     // real main line index, so reduce 1.
850     // Case 2. if this while-loop stops due to false result of [LessNotEqual(mainLength, mainSize)], the
851     // [currentMainLineIndex_] is exactly the real main line index. Update [endMainLineIndex_] when the recorded items
852     // are done measured.
853     gridLayoutInfo_.endMainLineIndex_ = runOutOfRecord ? --currentMainLineIndex_ : currentMainLineIndex_;
854     // reset reachEnd_ if any line at bottom is out of viewport
855     // last line make LessNotEqual(mainLength, mainSize) and continue is reach end too
856     gridLayoutInfo_.reachEnd_ = gridLayoutInfo_.endIndex_ == layoutWrapper->GetTotalChildCount() - 1;
857     return runOutOfRecord;
858 }
859 
SkipLargeOffset(float mainSize,LayoutWrapper * layoutWrapper)860 void GridScrollLayoutAlgorithm::SkipLargeOffset(float mainSize, LayoutWrapper* layoutWrapper)
861 {
862     auto gridLayoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
863     CHECK_NULL_VOID(gridLayoutProperty);
864     auto cacheCount = gridLayoutProperty->GetCachedCountValue(1);
865     cacheCount = std::max(cacheCount, 1);
866     SkipForwardLines(cacheCount * mainSize, layoutWrapper);
867     SkipBackwardLines(cacheCount * mainSize, layoutWrapper);
868 }
869 
SkipForwardLines(float mainSize,LayoutWrapper * layoutWrapper)870 void GridScrollLayoutAlgorithm::SkipForwardLines(float mainSize, LayoutWrapper* layoutWrapper)
871 {
872     if (!GreatOrEqual(gridLayoutInfo_.currentOffset_ - gridLayoutInfo_.prevOffset_, mainSize)) {
873         return;
874     }
875 
876     // skip lines in matrix
877     while (GreatOrEqual(gridLayoutInfo_.currentOffset_, mainSize)) {
878         auto line = gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.startMainLineIndex_ - 1);
879         if (line == gridLayoutInfo_.gridMatrix_.end()) {
880             break;
881         }
882         auto lineHeight = gridLayoutInfo_.lineHeightMap_.find(gridLayoutInfo_.startMainLineIndex_ - 1);
883         if (lineHeight == gridLayoutInfo_.lineHeightMap_.end()) {
884             break;
885         }
886         gridLayoutInfo_.startMainLineIndex_--;
887         gridLayoutInfo_.startIndex_ = line->second.begin()->second;
888         gridLayoutInfo_.currentOffset_ -= lineHeight->second;
889     }
890 
891     // skip lines not in matrix
892     if (GreatOrEqual(gridLayoutInfo_.currentOffset_, mainSize) && gridLayoutInfo_.startIndex_ > 0) {
893         auto grid = layoutWrapper->GetHostNode();
894         CHECK_NULL_VOID(grid);
895         auto pattern = grid->GetPattern<GridPattern>();
896         CHECK_NULL_VOID(pattern);
897         auto estimatedHeight = pattern->GetScrollableDistance();
898         // no scroller
899         if (LessOrEqual(estimatedHeight, 0)) {
900             return;
901         }
902         auto averageHeight = pattern->GetAverageHeight();
903         if (LessOrEqual(averageHeight, 0.0)) {
904             return;
905         }
906         int32_t estimatedIndex = (gridLayoutInfo_.currentOffset_) / averageHeight;
907         gridLayoutInfo_.startIndex_ = gridLayoutInfo_.startIndex_ > estimatedIndex
908                                           ? std::max(gridLayoutInfo_.startIndex_ - estimatedIndex, 0)
909                                           : 0;
910         gridLayoutInfo_.currentOffset_ =
911             gridLayoutInfo_.startIndex_ > estimatedIndex ? gridLayoutInfo_.prevOffset_ : 0.0f;
912         LOGI("estimatedIndex:%{public}d", gridLayoutInfo_.startIndex_);
913         grid->ChildrenUpdatedFrom(0);
914     }
915 }
916 
SkipBackwardLines(float mainSize,LayoutWrapper * layoutWrapper)917 void GridScrollLayoutAlgorithm::SkipBackwardLines(float mainSize, LayoutWrapper* layoutWrapper)
918 {
919     if (!GreatOrEqual(gridLayoutInfo_.prevOffset_ - gridLayoutInfo_.currentOffset_, mainSize)) {
920         return;
921     }
922 
923     // grid size change from big to small
924     gridLayoutInfo_.UpdateEndLine(mainSize, mainGap_);
925 
926     // skip lines in matrix
927     while (GreatOrEqual(-gridLayoutInfo_.currentOffset_, mainSize)) {
928         auto line = gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.endMainLineIndex_ + 1);
929         if (line == gridLayoutInfo_.gridMatrix_.end()) {
930             break;
931         }
932         auto lineHeight = gridLayoutInfo_.lineHeightMap_.find(gridLayoutInfo_.endMainLineIndex_ + 1);
933         if (lineHeight == gridLayoutInfo_.lineHeightMap_.end()) {
934             break;
935         }
936         gridLayoutInfo_.startMainLineIndex_++;
937         gridLayoutInfo_.endMainLineIndex_++;
938         gridLayoutInfo_.currentOffset_ += lineHeight->second;
939     }
940     gridLayoutInfo_.UpdateStartIndexByStartLine();
941 
942     // skip lines not in matrix
943     if (GreatOrEqual(-gridLayoutInfo_.currentOffset_, mainSize)) {
944         auto grid = layoutWrapper->GetHostNode();
945         CHECK_NULL_VOID(grid);
946         auto pattern = grid->GetPattern<GridPattern>();
947         CHECK_NULL_VOID(pattern);
948         auto estimatedHeight = pattern->GetScrollableDistance();
949         // no scroller
950         if (LessOrEqual(estimatedHeight, 0)) {
951             return;
952         }
953         auto averageHeight = pattern->GetAverageHeight();
954         if (LessOrEqual(averageHeight, 0.0)) {
955             return;
956         }
957         int32_t estimatedIndex = (gridLayoutInfo_.currentOffset_) / averageHeight;
958         gridLayoutInfo_.startIndex_ =
959             std::min(gridLayoutInfo_.startIndex_ - estimatedIndex, gridLayoutInfo_.childrenCount_);
960         gridLayoutInfo_.currentOffset_ = gridLayoutInfo_.prevOffset_;
961         LOGI("estimatedIndex:%{public}d, currentOffset_:%{public}f", gridLayoutInfo_.startIndex_,
962             gridLayoutInfo_.currentOffset_);
963         grid->ChildrenUpdatedFrom(0);
964     }
965 }
966 
FillNewLineForward(float crossSize,float mainSize,LayoutWrapper * layoutWrapper)967 float GridScrollLayoutAlgorithm::FillNewLineForward(float crossSize, float mainSize, LayoutWrapper* layoutWrapper)
968 {
969     // To make the code more convenient to read, we name a param in situation of vertical, for example:
970     // 1. [lineHight] means height of a row when the Grid is vertical;
971     // 2. [lineHight] means width of a column when the Grid is horizontal;
972     // Other params are also named according to this principle.
973     cellAveLength_ = -1.0f;
974     auto currentIndex = gridLayoutInfo_.startIndex_;
975     bool hasNormalItem = false;
976     // TODO: shoule we use policy of adaptive layout according to size of [GridItem] ?
977     if (gridLayoutInfo_.startMainLineIndex_ - 1 < 0) {
978         if (currentIndex == 0) {
979             return cellAveLength_;
980         }
981         // add more than one line
982         UpdateMatrixForAddedItems();
983     }
984     gridLayoutInfo_.startMainLineIndex_--;
985     bool doneCreateNewLine = false;
986     auto gridMatrixIter = gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.startMainLineIndex_);
987     if (gridMatrixIter == gridLayoutInfo_.gridMatrix_.end()) {
988         AddForwardLines(currentIndex, crossSize, mainSize, layoutWrapper);
989     }
990     gridMatrixIter = gridLayoutInfo_.gridMatrix_.find(gridLayoutInfo_.startMainLineIndex_);
991     if (gridMatrixIter == gridLayoutInfo_.gridMatrix_.end()) {
992         return cellAveLength_;
993     }
994 
995     // need to obtain the item node in order and by step one in LazyLayoutWrapperBuilder::OnGetOrCreateWrapperByIndex
996     for (auto itemIter = gridMatrixIter->second.rbegin(); itemIter != gridMatrixIter->second.rend(); ++itemIter) {
997         currentIndex = itemIter->second;
998 
999         // Step1. Get wrapper of [GridItem]
1000         auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1001         if (!itemWrapper) {
1002             LOGE("GridItem wrapper of index %{public}u null", currentIndex);
1003             break;
1004         }
1005         // Step2. Measure child
1006         // TODO: need to use [isScrollable_]
1007         auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
1008         AdjustRowColSpan(itemWrapper, layoutWrapper, currentIndex);
1009         auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1010         if (crossStart == -1) {
1011             MeasureChildPlaced(frameSize, currentIndex, itemIter->first, layoutWrapper, itemWrapper);
1012         } else {
1013             MeasureChildPlaced(frameSize, currentIndex, crossStart, layoutWrapper, itemWrapper);
1014         }
1015         // Step3. Measure [GridItem]
1016         LargeItemLineHeight(itemWrapper, hasNormalItem);
1017         gridLayoutInfo_.startIndex_ = currentIndex;
1018     }
1019 
1020     doneCreateNewLine = GreatOrEqual(cellAveLength_, 0.0);
1021     // If it fails to create new line when [FillNewLineForward] is called, it means that it reaches start
1022     gridLayoutInfo_.reachStart_ = !doneCreateNewLine;
1023 
1024     return cellAveLength_;
1025 }
1026 
UpdateMatrixForAddedItems()1027 void GridScrollLayoutAlgorithm::UpdateMatrixForAddedItems()
1028 {
1029     decltype(gridLayoutInfo_.lineHeightMap_) gridLineHeightMap(std::move(gridLayoutInfo_.lineHeightMap_));
1030     decltype(gridLayoutInfo_.gridMatrix_) gridMatrix(std::move(gridLayoutInfo_.gridMatrix_));
1031     for (const auto& item : gridMatrix) {
1032         gridLayoutInfo_.gridMatrix_[item.first + 1] = item.second;
1033     }
1034     for (const auto& item : gridLineHeightMap) {
1035         gridLayoutInfo_.lineHeightMap_[item.first + 1] = item.second;
1036     }
1037     gridLayoutInfo_.startMainLineIndex_ = gridLayoutInfo_.startMainLineIndex_ + 1;
1038     gridLayoutInfo_.endMainLineIndex_ = gridLayoutInfo_.endMainLineIndex_ + 1;
1039     LOGI("add more than one line startMainLineIndex_:%{public}d", gridLayoutInfo_.startMainLineIndex_);
1040 }
1041 
AddForwardLines(int32_t currentIndex,float crossSize,float mainSize,LayoutWrapper * layoutWrapper)1042 void GridScrollLayoutAlgorithm::AddForwardLines(
1043     int32_t currentIndex, float crossSize, float mainSize, LayoutWrapper* layoutWrapper)
1044 {
1045     auto endMainLineIndex = gridLayoutInfo_.endMainLineIndex_;
1046     auto endIndex = gridLayoutInfo_.endIndex_;
1047     auto firstItem = GetStartingItem(layoutWrapper, currentIndex - 1);
1048     auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(firstItem);
1049     AdjustRowColSpan(itemWrapper, layoutWrapper, firstItem);
1050     auto mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
1051     auto measureNumber = 0;
1052     currentMainLineIndex_ = (firstItem == 0 ? 0 : gridLayoutInfo_.startMainLineIndex_) - 1;
1053     gridLayoutInfo_.endIndex_ = firstItem - 1;
1054     // firstItem may be more than one line ahead, use new matrix to save and merge to old matrix
1055     decltype(gridLayoutInfo_.lineHeightMap_) gridLineHeightMap(std::move(gridLayoutInfo_.lineHeightMap_));
1056     decltype(gridLayoutInfo_.gridMatrix_) gridMatrix(std::move(gridLayoutInfo_.gridMatrix_));
1057     bool addLine = false;
1058     while (gridLayoutInfo_.endIndex_ < currentIndex - 1 || mainSpan > measureNumber) {
1059         auto newLineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, true);
1060         measureNumber++;
1061         if (LessNotEqual(newLineHeight, 0.0)) {
1062             gridLayoutInfo_.reachEnd_ = true;
1063             break;
1064         }
1065         addLine = true;
1066     }
1067     if (!addLine) {
1068         return;
1069     }
1070     // merge matrix
1071     auto forwardLines = gridLayoutInfo_.endMainLineIndex_ - gridLayoutInfo_.startMainLineIndex_;
1072     if (forwardLines >= 0) {
1073         auto begin = gridLayoutInfo_.gridMatrix_.begin()->first;
1074         if (gridLayoutInfo_.endMainLineIndex_ - begin <= begin) {
1075             for (auto i = begin; i <= gridLayoutInfo_.endMainLineIndex_; i++) {
1076                 gridMatrix.emplace(i - forwardLines, std::move(gridLayoutInfo_.gridMatrix_[i]));
1077                 gridLineHeightMap.emplace(i - forwardLines, gridLayoutInfo_.lineHeightMap_[i]);
1078             }
1079             gridMatrix.swap(gridLayoutInfo_.gridMatrix_);
1080             gridLineHeightMap.swap(gridLayoutInfo_.lineHeightMap_);
1081         } else {
1082             for (auto i = gridLayoutInfo_.startMainLineIndex_ + 1; i <= gridMatrix.rbegin()->first; i++) {
1083                 gridLayoutInfo_.gridMatrix_.emplace(forwardLines + i, std::move(gridMatrix[i]));
1084                 gridLayoutInfo_.lineHeightMap_.emplace(forwardLines + i, gridLineHeightMap[i]);
1085             }
1086         }
1087     } else {
1088         // delete more than one line items
1089         for (auto i = gridLayoutInfo_.startMainLineIndex_ + 1; i <= gridMatrix.rbegin()->first; i++) {
1090             gridLayoutInfo_.gridMatrix_.emplace(forwardLines + i, std::move(gridMatrix[i]));
1091             gridLayoutInfo_.lineHeightMap_.emplace(forwardLines + i, gridLineHeightMap[i]);
1092         }
1093     }
1094 
1095     gridLayoutInfo_.startMainLineIndex_ = gridLayoutInfo_.endMainLineIndex_ - (forwardLines > 0 ? forwardLines : 0);
1096     gridLayoutInfo_.endMainLineIndex_ = endMainLineIndex + (forwardLines < 0 ? forwardLines : 0);
1097     gridLayoutInfo_.endIndex_ = endIndex;
1098     LOGI("after load forward:start main line %{public}d end main line %{public}d", gridLayoutInfo_.startMainLineIndex_,
1099         gridLayoutInfo_.endMainLineIndex_);
1100 }
1101 
FillNewLineBackward(float crossSize,float mainSize,LayoutWrapper * layoutWrapper,bool reverse)1102 float GridScrollLayoutAlgorithm::FillNewLineBackward(
1103     float crossSize, float mainSize, LayoutWrapper* layoutWrapper, bool reverse)
1104 {
1105     // To make the code more convenient to read, we name a param in situation of vertical, for example:
1106     // 1. [lineHight] means height of a row when the Grid is vertical;
1107     // 2. [lineHight] means width of a column when the Grid is horizontal;
1108     // Other params are also named according to this principle.
1109     cellAveLength_ = -1.0f;
1110     if (moveToEndLineIndex_ > 0 && gridLayoutInfo_.endIndex_ >= moveToEndLineIndex_) {
1111         LOGI("scroll to end line with index:%{public}d", moveToEndLineIndex_);
1112         return cellAveLength_;
1113     }
1114     auto currentIndex = gridLayoutInfo_.endIndex_ + 1;
1115     currentMainLineIndex_++; // if it fails to fill a new line backward, do [currentMainLineIndex_--]
1116     lastCross_ = 0;
1117     bool hasNormalItem = false;
1118     bool doneFillLine = false;
1119     // TODO: shoule we use policy of adaptive layout according to size of [GridItem] ?
1120 
1121     for (uint32_t i = 0; i < crossCount_; i++) {
1122         // already finish first line forward
1123         if (reverse && currentIndex >= gridLayoutInfo_.startIndex_) {
1124             break;
1125         }
1126         // Step1. Get wrapper of [GridItem]
1127         auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1128         if (!itemWrapper) {
1129             LOGE("GridItem wrapper of index %{public}u null", currentIndex);
1130             LargeItemNextLineHeight(currentMainLineIndex_, layoutWrapper);
1131             break;
1132         }
1133         // Step2. Measure child
1134         auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
1135         auto crossSpan = MeasureNewChild(frameSize, currentIndex, layoutWrapper, itemWrapper, false);
1136         if (crossSpan < 0) {
1137             cellAveLength_ = LessNotEqual(cellAveLength_, 0.0)
1138                                  ? gridLayoutInfo_.lineHeightMap_.find(currentMainLineIndex_ - 1)->second
1139                                  : cellAveLength_;
1140             --currentIndex;
1141             break;
1142         }
1143         i = lastCross_ - 1;
1144         // Step3. Measure [GridItem]
1145         LargeItemLineHeight(itemWrapper, hasNormalItem);
1146 
1147         gridLayoutInfo_.endIndex_ = currentIndex;
1148         currentIndex++;
1149         doneFillLine = true;
1150     }
1151 
1152     if (doneFillLine) {
1153         gridLayoutInfo_.lineHeightMap_[currentMainLineIndex_] = cellAveLength_;
1154         gridLayoutInfo_.endMainLineIndex_ = currentMainLineIndex_;
1155     } else {
1156         currentMainLineIndex_--;
1157     }
1158     return cellAveLength_;
1159 }
1160 
LargeItemNextLineHeight(int32_t currentLineIndex,LayoutWrapper * layoutWrapper)1161 void GridScrollLayoutAlgorithm::LargeItemNextLineHeight(int32_t currentLineIndex, LayoutWrapper* layoutWrapper)
1162 {
1163     auto gridMatrixIter = gridLayoutInfo_.gridMatrix_.find(currentLineIndex);
1164     bool hasNormalItem = false;
1165     auto currentIndex = 0;
1166     if (gridMatrixIter != gridLayoutInfo_.gridMatrix_.end()) {
1167         for (auto itemIter = gridMatrixIter->second.rbegin(); itemIter != gridMatrixIter->second.rend(); ++itemIter) {
1168             currentIndex = itemIter->second;
1169             auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1170             if (!itemWrapper) {
1171                 break;
1172             }
1173             LargeItemLineHeight(itemWrapper, hasNormalItem);
1174         }
1175     }
1176 }
1177 
LargeItemForwardLineHeight(int32_t currentLineIndex,LayoutWrapper * layoutWrapper)1178 void GridScrollLayoutAlgorithm::LargeItemForwardLineHeight(int32_t currentLineIndex, LayoutWrapper* layoutWrapper)
1179 {
1180     auto lineIndex = currentLineIndex;
1181     auto gridMatrixIter = gridLayoutInfo_.gridMatrix_.find(lineIndex);
1182     if (gridMatrixIter == gridLayoutInfo_.gridMatrix_.end()) {
1183         return;
1184     }
1185     auto currentIndex = -1;
1186 
1187     lineIndex = CalculateLineIndexForLargeItem(gridMatrixIter, currentIndex, lineIndex, layoutWrapper);
1188     CalculateLineHeightForLargeItem(lineIndex, currentLineIndex, gridMatrixIter, layoutWrapper);
1189 }
1190 
CalculateLineIndexForLargeItem(std::map<int32_t,std::map<int32_t,int32_t>>::iterator gridMatrixIter,int32_t currentIndex,int32_t lineIndex,LayoutWrapper * layoutWrapper)1191 int32_t GridScrollLayoutAlgorithm::CalculateLineIndexForLargeItem(
1192     std::map<int32_t, std::map<int32_t, int32_t>>::iterator gridMatrixIter, int32_t currentIndex, int32_t lineIndex,
1193     LayoutWrapper* layoutWrapper)
1194 {
1195     for (const auto& gridItemRecord : gridMatrixIter->second) {
1196         if (currentIndex == gridItemRecord.second || gridItemRecord.second == -1) {
1197             continue;
1198         }
1199         currentIndex = gridItemRecord.second;
1200         auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1201         if (!itemWrapper) {
1202             break;
1203         }
1204         AdjustRowColSpan(itemWrapper, layoutWrapper, currentIndex);
1205         for (int32_t lastCrossIndex = lineIndex - 1; lastCrossIndex >= 0; lastCrossIndex--) {
1206             auto lastGridMatrixIter = gridLayoutInfo_.gridMatrix_.find(lastCrossIndex);
1207             if (lastGridMatrixIter == gridLayoutInfo_.gridMatrix_.end()) {
1208                 continue;
1209             }
1210             auto lastGridItemRecord = lastGridMatrixIter->second;
1211             auto lastLineCrossItem = lastGridItemRecord.find(gridItemRecord.first);
1212             if (lastLineCrossItem == lastGridItemRecord.end()) {
1213                 continue;
1214             }
1215             if (lastLineCrossItem->second == currentIndex) {
1216                 lineIndex--;
1217             } else {
1218                 break;
1219             }
1220         }
1221     }
1222     return lineIndex;
1223 }
1224 
CalculateLineHeightForLargeItem(int32_t lineIndex,int32_t currentLineIndex,std::map<int32_t,std::map<int32_t,int32_t>>::iterator gridMatrixIter,LayoutWrapper * layoutWrapper)1225 void GridScrollLayoutAlgorithm::CalculateLineHeightForLargeItem(int32_t lineIndex, int32_t currentLineIndex,
1226     std::map<int32_t, std::map<int32_t, int32_t>>::iterator gridMatrixIter, LayoutWrapper* layoutWrapper)
1227 {
1228     for (int32_t i = lineIndex; i <= currentLineIndex; i++) {
1229         auto currentGridMatrixIter = gridLayoutInfo_.gridMatrix_.find(i);
1230         if (currentGridMatrixIter == gridLayoutInfo_.gridMatrix_.end()) {
1231             continue;
1232         }
1233         bool hasNormalItem = false;
1234         auto currentIndex = 0;
1235         cellAveLength_ = -1.0f;
1236         for (auto itemIter = gridMatrixIter->second.rbegin(); itemIter != gridMatrixIter->second.rend(); ++itemIter) {
1237             if (currentIndex == itemIter->second) {
1238                 continue;
1239             }
1240             currentIndex = itemIter->second;
1241             auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1242             if (!itemWrapper) {
1243                 break;
1244             }
1245             LargeItemLineHeight(itemWrapper, hasNormalItem);
1246             gridLayoutInfo_.lineHeightMap_[i] = cellAveLength_;
1247         }
1248     }
1249 }
1250 
CreateChildConstraint(float mainSize,float crossSize,const RefPtr<GridLayoutProperty> & gridLayoutProperty,int32_t crossStart,int32_t crossSpan) const1251 LayoutConstraintF GridScrollLayoutAlgorithm::CreateChildConstraint(float mainSize, float crossSize,
1252     const RefPtr<GridLayoutProperty>& gridLayoutProperty, int32_t crossStart, int32_t crossSpan) const
1253 {
1254     float itemMainSize =
1255         gridLayoutProperty->IsConfiguredScrollable() ? Infinity<float>() : mainSize / static_cast<float>(mainCount_);
1256 
1257     auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
1258     float itemCrossSize = GridUtils::GetCrossGap(gridLayoutProperty, frameSize, axis_) * (crossSpan - 1);
1259     for (int32_t index = 0; index < crossSpan; ++index) {
1260         int32_t crossIndex = (crossStart + index) % static_cast<int32_t>(crossCount_);
1261         if (crossIndex >= 0 && crossIndex < static_cast<int32_t>(itemsCrossSize_.size())) {
1262             itemCrossSize += itemsCrossSize_.at(crossIndex);
1263         }
1264     }
1265 
1266     SizeF itemIdealSize =
1267         gridLayoutProperty->IsVertical() ? SizeF(itemCrossSize, itemMainSize) : SizeF(itemMainSize, itemCrossSize);
1268     auto itemConstraint = gridLayoutProperty->CreateChildConstraint();
1269 
1270     // The percent size of GridItem is based on the fraction size, for e.g., if a GridItem has width of "50%" in Grid
1271     // configured with columnsTemplate = "1fr 1fr", rowsTemplate = "1fr 1fr",
1272     // then the GridItem width = [width of 1fr] * 50%,
1273     // [itemFractionCount] is now only in direction of cross axis
1274     float widthPercentBase =
1275         GreatOrEqual(crossCount_, Infinity<uint32_t>()) ? itemConstraint.percentReference.Width() : itemCrossSize;
1276     float heightPercentBase = GreatOrEqual(mainCount_, Infinity<uint32_t>())
1277                                   ? itemConstraint.percentReference.Height()
1278                                   : itemConstraint.percentReference.Height() / static_cast<float>(mainCount_);
1279     if (axis_ == Axis::VERTICAL) {
1280         itemConstraint.percentReference = SizeF(widthPercentBase, itemConstraint.percentReference.Height());
1281     } else {
1282         itemConstraint.percentReference = SizeF(itemConstraint.percentReference.Width(), heightPercentBase);
1283     }
1284     itemConstraint.maxSize = itemIdealSize;
1285     itemConstraint.UpdateIllegalSelfMarginSizeWithCheck(axis_ == Axis::VERTICAL
1286                                                             ? OptionalSizeF(itemCrossSize, std::nullopt)
1287                                                             : OptionalSizeF(std::nullopt, itemCrossSize));
1288     return itemConstraint;
1289 }
1290 
GetNextGrid(int32_t & curMain,int32_t & curCross,bool reverse) const1291 bool GridScrollLayoutAlgorithm::GetNextGrid(int32_t& curMain, int32_t& curCross, bool reverse) const
1292 {
1293     if (!reverse) {
1294         ++curCross;
1295         if (curCross >= static_cast<int32_t>(crossCount_)) {
1296             return false;
1297         }
1298         return true;
1299     }
1300 
1301     --curCross;
1302     if (curCross < 0) {
1303         return false;
1304     }
1305     return true;
1306 }
1307 
MeasureNewChild(const SizeF & frameSize,int32_t itemIndex,LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & childLayoutWrapper,bool reverse)1308 int32_t GridScrollLayoutAlgorithm::MeasureNewChild(const SizeF& frameSize, int32_t itemIndex,
1309     LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& childLayoutWrapper, bool reverse)
1310 {
1311     auto mainCount = static_cast<int32_t>(mainCount_);
1312     auto crossCount = static_cast<int32_t>(crossCount_);
1313     AdjustRowColSpan(childLayoutWrapper, layoutWrapper, itemIndex);
1314     auto mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
1315     auto crossSpan = axis_ == Axis::VERTICAL ? currentItemColSpan_ : currentItemRowSpan_;
1316     auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1317     if (crossSpan > crossCount) {
1318         LOGW("item %{public}d can not be placed in grid: cross count:%{public}d, cross span:%{public}d", itemIndex,
1319             crossCount, crossSpan);
1320         return crossSpan;
1321     }
1322     int32_t mainIndex = currentMainLineIndex_;
1323 
1324     if (crossStart >= 0 && crossStart < crossCount) {
1325         if (crossStart < lastCross_) {
1326             return -1;
1327         } else if (CheckGridPlaced(itemIndex, mainIndex, crossStart, mainSpan, crossSpan)) {
1328             MeasureChild(layoutWrapper, frameSize, childLayoutWrapper, crossStart, crossSpan);
1329             itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(layoutWrapper, crossStart));
1330         } else {
1331             return -1;
1332         }
1333     } else {
1334         int32_t crossIndex = crossStart >= 0 ? crossStart : lastCross_;
1335 
1336         while (!CheckGridPlaced(itemIndex, mainIndex, crossIndex, mainSpan, crossSpan)) {
1337             if (GetNextGrid(mainIndex, crossIndex, reverse) == false) {
1338                 return -1;
1339             }
1340             if (mainIndex >= mainCount || crossIndex >= crossCount) {
1341                 break;
1342             }
1343         }
1344 
1345         MeasureChild(layoutWrapper, frameSize, childLayoutWrapper, crossIndex, crossSpan);
1346         itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(layoutWrapper, crossIndex));
1347     }
1348     return crossSpan;
1349 }
1350 
MeasureChildPlaced(const SizeF & frameSize,int32_t itemIndex,int32_t crossStart,LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & childLayoutWrapper)1351 int32_t GridScrollLayoutAlgorithm::MeasureChildPlaced(const SizeF& frameSize, int32_t itemIndex, int32_t crossStart,
1352     LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& childLayoutWrapper)
1353 {
1354     AdjustRowColSpan(childLayoutWrapper, layoutWrapper, itemIndex);
1355     auto crossSpan = axis_ == Axis::VERTICAL ? currentItemColSpan_ : currentItemRowSpan_;
1356     if (static_cast<uint32_t>(crossStart + crossSpan) > crossCount_) {
1357         LOGI("item %{public}d cross not enough, start:%{public}d, span:%{public}d", itemIndex, crossStart, crossSpan);
1358         return 0;
1359     }
1360 
1361     MeasureChild(layoutWrapper, frameSize, childLayoutWrapper, crossStart, crossSpan);
1362     itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(layoutWrapper, crossStart));
1363     return crossSpan;
1364 }
1365 
MeasureChild(LayoutWrapper * layoutWrapper,const SizeF & frameSize,const RefPtr<LayoutWrapper> & childLayoutWrapper,int32_t crossStart,int32_t crossSpan)1366 void GridScrollLayoutAlgorithm::MeasureChild(LayoutWrapper* layoutWrapper, const SizeF& frameSize,
1367     const RefPtr<LayoutWrapper>& childLayoutWrapper, int32_t crossStart, int32_t crossSpan)
1368 {
1369     auto gridLayoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
1370     auto mainSize = GetMainAxisSize(frameSize, gridLayoutInfo_.axis_);
1371     auto crossSize = GetCrossAxisSize(frameSize, gridLayoutInfo_.axis_);
1372     auto childConstraint = CreateChildConstraint(mainSize, crossSize, gridLayoutProperty, crossStart, crossSpan);
1373     auto childLayoutProperty = childLayoutWrapper->GetLayoutProperty();
1374     if (!childLayoutProperty) {
1375         LOGW("childLayoutProperty is nullptr");
1376         childLayoutWrapper->Measure(childConstraint);
1377         return;
1378     }
1379     auto oldConstraint = childLayoutProperty->GetLayoutConstraint();
1380     if (oldConstraint.has_value() && !NearEqual(GetCrossAxisSize(oldConstraint.value().maxSize, axis_),
1381                                                 GetCrossAxisSize(childConstraint.maxSize, axis_))) {
1382         auto layoutAlgorithmWrapper = childLayoutWrapper->GetLayoutAlgorithm();
1383         if (layoutAlgorithmWrapper->SkipMeasure()) {
1384             layoutAlgorithmWrapper->SetNeedMeasure();
1385             if (layoutAlgorithmWrapper->GetLayoutAlgorithm() == nullptr) {
1386                 layoutAlgorithmWrapper->SetLayoutAlgorithm(
1387                     childLayoutWrapper->GetHostNode()->GetPattern()->CreateLayoutAlgorithm());
1388             }
1389         }
1390     }
1391     childLayoutWrapper->Measure(childConstraint);
1392 }
1393 
CheckGridPlaced(int32_t index,int32_t main,int32_t cross,int32_t mainSpan,int32_t crossSpan)1394 bool GridScrollLayoutAlgorithm::CheckGridPlaced(
1395     int32_t index, int32_t main, int32_t cross, int32_t mainSpan, int32_t crossSpan)
1396 {
1397     // If start position is already exist in gridMatrix, place grid item fail.
1398     auto mainIter = gridLayoutInfo_.gridMatrix_.find(main);
1399     if (mainIter != gridLayoutInfo_.gridMatrix_.end()) {
1400         auto crossIter = mainIter->second.find(cross);
1401         if (crossIter != mainIter->second.end()) {
1402             return false;
1403         }
1404     }
1405 
1406     // If cross length of grid item if out of range,  place grid item fail.
1407     if (cross + crossSpan > static_cast<int32_t>(crossCount_)) {
1408         return false;
1409     }
1410 
1411     // If any grid item is already exist in gridMatrix, place grid item fail.
1412     for (int32_t i = 0; i < mainSpan; i++) {
1413         mainIter = gridLayoutInfo_.gridMatrix_.find(i + main);
1414         if (mainIter == gridLayoutInfo_.gridMatrix_.end()) {
1415             continue;
1416         }
1417         for (int32_t j = 0; j < crossSpan; j++) {
1418             auto crossIter = mainIter->second.find(j + cross);
1419             if (crossIter != mainIter->second.end()) {
1420                 return false;
1421             }
1422         }
1423     }
1424 
1425     // Padding grid matrix for grid item's range.
1426     for (int32_t i = main; i < main + mainSpan; ++i) {
1427         std::map<int32_t, int32_t> mainMap;
1428         auto iter = gridLayoutInfo_.gridMatrix_.find(i);
1429         if (iter != gridLayoutInfo_.gridMatrix_.end()) {
1430             mainMap = iter->second;
1431         }
1432         for (int32_t j = cross; j < cross + crossSpan; ++j) {
1433             mainMap.emplace(std::make_pair(j, index));
1434         }
1435         gridLayoutInfo_.gridMatrix_[i] = mainMap;
1436     }
1437     lastCross_ = cross + crossSpan;
1438 
1439     return true;
1440 }
1441 
ComputeItemCrossPosition(LayoutWrapper * layoutWrapper,int32_t crossStart) const1442 float GridScrollLayoutAlgorithm::ComputeItemCrossPosition(LayoutWrapper* layoutWrapper, int32_t crossStart) const
1443 {
1444     float position = 0.0f;
1445     for (int32_t index = 0; index < crossStart; ++index) {
1446         if (index >= 0 && index < static_cast<int32_t>(itemsCrossSize_.size())) {
1447             position += itemsCrossSize_.at(index);
1448         }
1449     }
1450     position += crossStart * crossGap_ + crossPaddingOffset_;
1451     return position;
1452 }
1453 
GetStartingItem(LayoutWrapper * layoutWrapper,int32_t currentIndex)1454 int32_t GridScrollLayoutAlgorithm::GetStartingItem(LayoutWrapper* layoutWrapper, int32_t currentIndex)
1455 {
1456     int32_t firstIndex = 0;
1457     currentIndex =
1458         currentIndex < layoutWrapper->GetTotalChildCount() ? currentIndex : layoutWrapper->GetTotalChildCount() - 1;
1459     auto index = currentIndex;
1460     if (gridLayoutInfo_.hasBigItem_) {
1461         while (index > 0) {
1462             auto childLayoutWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1463             if (!childLayoutWrapper) {
1464                 LOGE("GridItem wrapper of index %{public}u null", index);
1465                 break;
1466             }
1467 
1468             AdjustRowColSpan(childLayoutWrapper, layoutWrapper, index);
1469             auto crossIndex = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1470             if (crossIndex == 0) {
1471                 firstIndex = index;
1472                 break;
1473             }
1474             --index;
1475         }
1476     } else {
1477         while (index > 0) {
1478             // need to obtain the item node in order and by step one
1479             auto childLayoutWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1480             if (!childLayoutWrapper) {
1481                 LOGE("GridItem wrapper of index %{public}u null", index);
1482                 break;
1483             }
1484             AdjustRowColSpan(childLayoutWrapper, layoutWrapper, index);
1485             auto crossIndex = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1486             // Grid may change from no big item to has big item
1487             if (crossIndex >= 0) {
1488                 gridLayoutInfo_.hasBigItem_ = true;
1489                 return GetStartingItem(layoutWrapper, currentIndex);
1490             }
1491             if (index % gridLayoutInfo_.crossCount_ == 0) {
1492                 firstIndex = index;
1493                 break;
1494             }
1495             --index;
1496         }
1497     }
1498     return firstIndex;
1499 }
1500 
1501 // only for debug use
PrintGridMatrix(const std::map<int32_t,std::map<int32_t,int32_t>> & gridMatrix,const std::map<int32_t,float> & positions)1502 void GridScrollLayoutAlgorithm::PrintGridMatrix(
1503     const std::map<int32_t, std::map<int32_t, int32_t>>& gridMatrix, const std::map<int32_t, float>& positions)
1504 {
1505     for (const auto& record : gridMatrix) {
1506         for (const auto& item : record.second) {
1507             float position = -1;
1508             auto iter = positions.find(item.second);
1509             if (iter != positions.end()) {
1510                 position = iter->second;
1511             }
1512             LOGI("grid matrix -- line: %{public}d, item: %{public}d, fr: %{public}d, position: %{public}f",
1513                 record.first, item.first, item.second, position);
1514         }
1515     }
1516 }
1517 
1518 // only for debug use
PrintLineHeightMap(const std::map<int32_t,float> & lineHeightMap)1519 void GridScrollLayoutAlgorithm::PrintLineHeightMap(const std::map<int32_t, float>& lineHeightMap)
1520 {
1521     for (const auto& record : lineHeightMap) {
1522         LOGI("line height -- line: %{public}d, lineHeight: %{public}f", record.first, record.second);
1523     }
1524 }
1525 
1526 } // namespace OHOS::Ace::NG
1527