• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-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/irregular/grid_irregular_layout_algorithm.h"
17 
18 #include "base/utils/utils.h"
19 #include "core/components/scroll/scroll_controller_base.h"
20 #include "core/components_ng/pattern/grid/grid_layout_info.h"
21 #include "core/components_ng/pattern/grid/grid_layout_property.h"
22 #include "core/components_ng/pattern/grid/grid_utils.h"
23 #include "core/components_ng/pattern/grid/irregular/grid_irregular_filler.h"
24 #include "core/components_ng/pattern/grid/irregular/grid_layout_range_solver.h"
25 #include "core/components_ng/pattern/grid/irregular/grid_layout_utils.h"
26 #include "core/components_ng/pattern/scrollable/scrollable_utils.h"
27 #include "core/components_ng/property/measure_property.h"
28 #include "core/components_ng/property/templates_parser.h"
29 
30 namespace OHOS::Ace::NG {
Measure(LayoutWrapper * layoutWrapper)31 void GridIrregularLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
32 {
33     if (info_.childrenCount_ <= 0) {
34         return;
35     }
36     wrapper_ = layoutWrapper;
37     auto props = DynamicCast<GridLayoutProperty>(wrapper_->GetLayoutProperty());
38 
39     float mainSize = MeasureSelf(props);
40     Init(props);
41 
42     if (info_.targetIndex_) {
43         MeasureToTarget();
44         info_.targetIndex_.reset();
45     }
46 
47     if (info_.jumpIndex_ != EMPTY_JUMP_INDEX) {
48         MeasureOnJump(mainSize);
49     } else {
50         MeasureOnOffset(mainSize);
51     }
52 
53     if (props->GetAlignItems().value_or(GridItemAlignment::DEFAULT) == GridItemAlignment::STRETCH) {
54         GridLayoutBaseAlgorithm::AdjustChildrenHeight(layoutWrapper);
55     }
56 
57     UpdateLayoutInfo();
58 }
59 
Layout(LayoutWrapper * layoutWrapper)60 void GridIrregularLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
61 {
62     const auto& info = gridLayoutInfo_;
63     if (info.childrenCount_ <= 0) {
64         return;
65     }
66     wrapper_ = layoutWrapper;
67     auto props = DynamicCast<GridLayoutProperty>(wrapper_->GetLayoutProperty());
68     CHECK_NULL_VOID(props);
69 
70     const int32_t cacheCount = props->GetCachedCountValue(info.defCachedCount_);
71     if (!props->HasCachedCount()) {
72         gridLayoutInfo_.UpdateDefaultCachedCount();
73     }
74     LayoutChildren(info.currentOffset_, cacheCount);
75 
76     const int32_t cacheCnt = cacheCount * info.crossCount_;
77     wrapper_->SetActiveChildRange(std::min(info.startIndex_, info.endIndex_), info.endIndex_, cacheCnt, cacheCnt);
78     wrapper_->SetCacheCount(cacheCnt);
79     PreloadItems(cacheCnt);
80 }
81 
MeasureSelf(const RefPtr<GridLayoutProperty> & props)82 float GridIrregularLayoutAlgorithm::MeasureSelf(const RefPtr<GridLayoutProperty>& props)
83 {
84     // set self size
85     auto size = CreateIdealSize(props->GetLayoutConstraint().value(), info_.axis_, props->GetMeasureType(), true);
86     wrapper_->GetGeometryNode()->SetFrameSize(size);
87 
88     // set content size
89     const auto& padding = props->CreatePaddingAndBorder();
90     wrapper_->GetGeometryNode()->UpdatePaddingWithBorder(padding);
91     MinusPaddingToSize(padding, size);
92     info_.contentEndPadding_ = ScrollableUtils::CheckHeightExpansion(props, info_.axis_);
93     size.AddHeight(info_.contentEndPadding_);
94     wrapper_->GetGeometryNode()->SetContentSize(size);
95 
96     return size.MainSize(info_.axis_);
97 }
98 
Init(const RefPtr<GridLayoutProperty> & props)99 void GridIrregularLayoutAlgorithm::Init(const RefPtr<GridLayoutProperty>& props)
100 {
101     const auto& contentSize = wrapper_->GetGeometryNode()->GetContentSize();
102     crossGap_ = GridUtils::GetCrossGap(props, contentSize, info_.axis_);
103     mainGap_ = GridUtils::GetMainGap(props, contentSize, info_.axis_);
104 
105     std::string args;
106     if (props->GetRowsTemplate()) {
107         info_.axis_ = Axis::HORIZONTAL;
108         args = props->GetRowsTemplate().value_or("");
109     } else {
110         info_.axis_ = Axis::VERTICAL;
111         args = props->GetColumnsTemplate().value_or("");
112     }
113 
114     const float crossSize = contentSize.CrossSize(info_.axis_);
115     auto res = ParseTemplateArgs(GridUtils::ParseArgs(args), crossSize, crossGap_, info_.childrenCount_);
116 
117     crossLens_ = std::vector<float>(res.first.begin(), res.first.end());
118     if (crossLens_.empty()) {
119         crossLens_.push_back(crossSize);
120     }
121 
122     crossGap_ = res.second;
123 
124     info_.crossCount_ = static_cast<int32_t>(crossLens_.size());
125     CheckForReset();
126 }
127 
128 namespace {
PrepareJumpOnReset(GridLayoutInfo & info)129 inline void PrepareJumpOnReset(GridLayoutInfo& info)
130 {
131     info.jumpIndex_ = std::min(info.startIndex_, info.childrenCount_ - 1);
132     info.scrollAlign_ = ScrollAlign::START;
133 }
ResetMaps(GridLayoutInfo & info)134 inline void ResetMaps(GridLayoutInfo& info)
135 {
136     info.gridMatrix_.clear();
137     info.lineHeightMap_.clear();
138 }
ResetLayoutRange(GridLayoutInfo & info)139 inline void ResetLayoutRange(GridLayoutInfo& info)
140 {
141     info.startIndex_ = 0;
142     info.endIndex_ = -1;
143     info.startMainLineIndex_ = 0;
144     info.endMainLineIndex_ = -1;
145     info.currentOffset_ = 0.0f;
146     info.prevOffset_ = 0.0f;
147 }
148 } // namespace
149 
CheckForReset()150 void GridIrregularLayoutAlgorithm::CheckForReset()
151 {
152     if (info_.IsResetted()) {
153         // reset layout info_ and perform jump to current startIndex
154         postJumpOffset_ = info_.currentOffset_;
155         PrepareJumpOnReset(info_);
156         ResetMaps(info_);
157         ResetLayoutRange(info_);
158         return;
159     }
160 
161     int32_t updateIdx = wrapper_->GetHostNode()->GetChildrenUpdated();
162     if (updateIdx != -1) {
163         auto it = info_.FindInMatrix(updateIdx);
164         if (it != info_.gridMatrix_.end()) {
165             info_.ClearHeightsToEnd(it->first);
166             info_.ClearMatrixToEnd(updateIdx, it->first);
167         }
168         if (updateIdx <= info_.endIndex_) {
169             postJumpOffset_ = info_.currentOffset_;
170             PrepareJumpOnReset(info_);
171             ResetLayoutRange(info_);
172         }
173         wrapper_->GetHostNode()->ChildrenUpdatedFrom(-1);
174         return;
175     }
176 
177     if (wrapper_->GetLayoutProperty()->GetPropertyChangeFlag() & PROPERTY_UPDATE_BY_CHILD_REQUEST) {
178         postJumpOffset_ = info_.currentOffset_;
179         info_.lineHeightMap_.clear();
180         PrepareJumpOnReset(info_);
181         ResetLayoutRange(info_);
182         return;
183     }
184 
185     if (!wrapper_->IsConstraintNoChanged()) {
186         // need to remeasure all items in current view
187         postJumpOffset_ = info_.currentOffset_;
188         PrepareJumpOnReset(info_);
189     }
190 }
191 
MeasureOnOffset(float mainSize)192 void GridIrregularLayoutAlgorithm::MeasureOnOffset(float mainSize)
193 {
194     if (TrySkipping(mainSize)) {
195         return;
196     }
197 
198     if (GreatNotEqual(info_.currentOffset_, info_.prevOffset_)) {
199         MeasureBackward(mainSize);
200     } else {
201         MeasureForward(mainSize);
202     }
203 }
204 
205 namespace {
UpdateStartInfo(GridLayoutInfo & info,const GridLayoutRangeSolver::StartingRowInfo & res)206 inline void UpdateStartInfo(GridLayoutInfo& info, const GridLayoutRangeSolver::StartingRowInfo& res)
207 {
208     info.startMainLineIndex_ = res.row;
209     info.currentOffset_ = res.pos;
210     info.startIndex_ = res.idx;
211 }
212 
GetPrevHeight(const GridLayoutInfo & info,float mainGap)213 inline float GetPrevHeight(const GridLayoutInfo& info, float mainGap)
214 {
215     return info.GetHeightInRange(info.startMainLineIndex_, info.endMainLineIndex_, mainGap);
216 }
217 } // namespace
218 
MeasureForward(float mainSize)219 void GridIrregularLayoutAlgorithm::MeasureForward(float mainSize)
220 {
221     float heightToFill = mainSize - info_.currentOffset_ - GetPrevHeight(info_, mainGap_);
222     if (Positive(heightToFill)) {
223         GridIrregularFiller filler(&info_, wrapper_);
224         filler.Fill({ crossLens_, crossGap_, mainGap_ }, heightToFill, info_.endMainLineIndex_);
225     }
226 
227     GridLayoutRangeSolver solver(&info_, wrapper_);
228     auto res = solver.FindStartingRow(mainGap_);
229     UpdateStartInfo(info_, res);
230     auto [endMainLineIdx, endIdx] = solver.SolveForwardForEndIdx(mainGap_, mainSize - res.pos, res.row);
231     info_.endMainLineIndex_ = endMainLineIdx;
232     info_.endIndex_ = endIdx;
233 
234     // adjust offset
235     if (!overScroll_ && info_.endIndex_ == info_.childrenCount_ - 1) {
236         float overDis =
237             -info_.GetDistanceToBottom(mainSize, info_.GetTotalHeightOfItemsInView(mainGap_, false), mainGap_);
238         if (Negative(overDis)) {
239             return;
240         }
241         info_.currentOffset_ += overDis;
242         if (Positive(info_.currentOffset_)) {
243             MeasureBackward(mainSize);
244         }
245     }
246 }
247 
MeasureBackward(float mainSize)248 void GridIrregularLayoutAlgorithm::MeasureBackward(float mainSize)
249 {
250     // skip adding starting lines that are outside viewport in LayoutIrregular
251     auto [it, offset] = info_.SkipLinesAboveView(mainGap_);
252     GridIrregularFiller filler(&info_, wrapper_);
253     filler.MeasureBackward({ crossLens_, crossGap_, mainGap_ }, offset + it->second + mainGap_, it->first);
254 
255     GridLayoutRangeSolver solver(&info_, wrapper_);
256     auto res = solver.FindStartingRow(mainGap_);
257     if (!overScroll_ && res.row == 0) {
258         res.pos = std::min(res.pos, 0.0f);
259     }
260     UpdateStartInfo(info_, res);
261 
262     auto [endLine, endIdx] = solver.SolveForwardForEndIdx(mainGap_, mainSize - res.pos, res.row);
263     info_.endMainLineIndex_ = endLine;
264     info_.endIndex_ = endIdx;
265 }
266 
267 namespace {
268 constexpr float SKIP_THRESHOLD = 2.0f;
269 }
270 
TrySkipping(float mainSize)271 bool GridIrregularLayoutAlgorithm::TrySkipping(float mainSize)
272 {
273     float delta = std::abs(info_.currentOffset_ - info_.prevOffset_);
274     if (enableSkip_ && GreatNotEqual(delta, mainSize)) {
275         // a more costly check, therefore perform after comparing to [mainSize]
276         if (LessOrEqual(delta, SKIP_THRESHOLD * info_.GetTotalHeightOfItemsInView(mainGap_))) {
277             return false;
278         }
279         info_.jumpIndex_ = Negative(info_.currentOffset_) ? SkipLinesForward() : SkipLinesBackward();
280         info_.scrollAlign_ = ScrollAlign::START;
281         info_.currentOffset_ = 0.0f;
282         Jump(mainSize);
283         return true;
284     }
285     return false;
286 }
287 
MeasureOnJump(float mainSize)288 void GridIrregularLayoutAlgorithm::MeasureOnJump(float mainSize)
289 {
290     Jump(mainSize);
291 
292     if (info_.extraOffset_ && !NearZero(*info_.extraOffset_)) {
293         info_.prevOffset_ = info_.currentOffset_;
294         info_.currentOffset_ += *info_.extraOffset_;
295         MeasureOnOffset(mainSize);
296     }
297     if (!NearZero(postJumpOffset_)) {
298         info_.currentOffset_ = postJumpOffset_;
299         enableSkip_ = false;
300         MeasureOnOffset(mainSize);
301     }
302 }
303 
Jump(float mainSize)304 void GridIrregularLayoutAlgorithm::Jump(float mainSize)
305 {
306     if (info_.jumpIndex_ == JUMP_TO_BOTTOM_EDGE) {
307         GridIrregularFiller filler(&info_, wrapper_);
308         filler.FillMatrixOnly(info_.childrenCount_ - 1);
309         info_.PrepareJumpToBottom();
310     }
311 
312     if (info_.jumpIndex_ == LAST_ITEM) {
313         info_.jumpIndex_ = info_.childrenCount_ - 1;
314     }
315 
316     if (info_.scrollAlign_ == ScrollAlign::AUTO) {
317         int32_t height = GridLayoutUtils::GetItemSize(&info_, wrapper_, info_.jumpIndex_).rows;
318         info_.scrollAlign_ = info_.TransformAutoScrollAlign(info_.jumpIndex_, height, mainSize, mainGap_);
319     }
320     if (info_.scrollAlign_ == ScrollAlign::NONE) {
321         info_.jumpIndex_ = EMPTY_JUMP_INDEX;
322         return;
323     }
324 
325     int32_t jumpLineIdx = FindJumpLineIdx(info_.jumpIndex_);
326 
327     PrepareLineHeight(mainSize, jumpLineIdx);
328 
329     GridLayoutRangeSolver solver(&info_, wrapper_);
330     const auto res = solver.FindRangeOnJump(info_.jumpIndex_, jumpLineIdx, mainGap_);
331 
332     info_.currentOffset_ = res.pos;
333     info_.startMainLineIndex_ = res.startRow;
334     info_.startIndex_ = res.startIdx;
335     info_.endMainLineIndex_ = res.endRow;
336     info_.endIndex_ = res.endIdx;
337     info_.jumpIndex_ = EMPTY_JUMP_INDEX;
338 }
339 
UpdateLayoutInfo()340 void GridIrregularLayoutAlgorithm::UpdateLayoutInfo()
341 {
342     info_.reachStart_ = info_.startIndex_ == 0 && NonNegative(info_.currentOffset_);
343     // GridLayoutInfo::reachEnd_ has a different meaning
344     info_.reachEnd_ = info_.endIndex_ == info_.childrenCount_ - 1;
345 
346     float mainSize = wrapper_->GetGeometryNode()->GetContentSize().MainSize(info_.axis_);
347 
348     info_.lastMainSize_ = mainSize;
349     info_.totalHeightOfItemsInView_ = info_.GetTotalHeightOfItemsInView(mainGap_, false);
350     info_.avgLineHeight_ = info_.GetTotalLineHeight(0.0f) / static_cast<float>(info_.lineHeightMap_.size());
351 
352     if (info_.reachEnd_) {
353         info_.offsetEnd_ = NonPositive(info_.GetDistanceToBottom(mainSize, info_.totalHeightOfItemsInView_, mainGap_));
354     } else {
355         info_.offsetEnd_ = false;
356     }
357     info_.prevOffset_ = info_.currentOffset_;
358 
359     // validity check
360     for (int i = info_.startMainLineIndex_; i <= info_.endMainLineIndex_; ++i) {
361         if (info_.lineHeightMap_.find(i) == info_.lineHeightMap_.end()) {
362             TAG_LOGW(AceLogTag::ACE_GRID,
363                 "lineHeight at line %d not ready. Data is corrupted. StartLine = %d, EndLine = %d", i,
364                 info_.startMainLineIndex_, info_.endMainLineIndex_);
365             info_.endMainLineIndex_ = i - 1;
366             info_.endIndex_ = info_.startIndex_ - 1;
367             return;
368         }
369     }
370 }
371 
372 namespace {
GetAlignment(Axis axis,const RefPtr<GridLayoutProperty> & props)373 Alignment GetAlignment(Axis axis, const RefPtr<GridLayoutProperty>& props)
374 {
375     Alignment align = axis == Axis::VERTICAL ? Alignment::TOP_CENTER : Alignment::CENTER_LEFT;
376     const auto& positionProp = props->GetPositionProperty();
377     if (positionProp) {
378         align = positionProp->GetAlignment().value_or(align);
379     }
380     return align;
381 }
382 /* adjust mainOffset to the first cache line */
AdjustStartOffset(const std::map<int32_t,float> & lineHeights,int32_t startLine,int32_t cacheStartLine,float mainGap,float & mainOffset)383 void AdjustStartOffset(const std::map<int32_t, float>& lineHeights, int32_t startLine, int32_t cacheStartLine,
384     float mainGap, float& mainOffset)
385 {
386     auto startLineIt = lineHeights.lower_bound(startLine);
387     for (auto it = lineHeights.lower_bound(cacheStartLine); it != startLineIt; ++it) {
388         mainOffset -= mainGap + it->second;
389     }
390 }
391 } // namespace
392 
LayoutChildren(float mainOffset,int32_t cacheLine)393 void GridIrregularLayoutAlgorithm::LayoutChildren(float mainOffset, int32_t cacheLine)
394 {
395     const auto& info = gridLayoutInfo_;
396     const auto& props = DynamicCast<GridLayoutProperty>(wrapper_->GetLayoutProperty());
397     const Alignment align = GetAlignment(info.axis_, props);
398 
399     const auto& padding = *wrapper_->GetGeometryNode()->GetPadding();
400     mainOffset += info.axis_ == Axis::HORIZONTAL ? 0.0f : padding.top.value_or(0.0f);
401     auto crossPos = CalculateCrossPositions(padding);
402 
403     auto frameSize = wrapper_->GetGeometryNode()->GetFrameSize();
404     MinusPaddingToSize(padding, frameSize);
405     const bool isRtl = props->GetNonAutoLayoutDirection() == TextDirection::RTL;
406     const int32_t cacheStartLine = info.startMainLineIndex_ - cacheLine;
407     AdjustStartOffset(info.lineHeightMap_, info.startMainLineIndex_, cacheStartLine, mainGap_, mainOffset);
408 
409     auto endIt = info.gridMatrix_.upper_bound(std::max(info.endMainLineIndex_ + cacheLine, info.startMainLineIndex_));
410     for (auto it = info.gridMatrix_.lower_bound(cacheStartLine); it != endIt; ++it) {
411         auto lineHeightIt = info.lineHeightMap_.find(it->first);
412         if (lineHeightIt == info.lineHeightMap_.end()) {
413             continue;
414         }
415         const bool isCache = it->first < info.startMainLineIndex_ || it->first > info.endMainLineIndex_;
416         const auto& row = it->second;
417         for (const auto& [c, itemIdx] : row) {
418             if (itemIdx < 0 || (itemIdx == 0 && (it->first > 0 || c > 0))) {
419                 // not top-left tile
420                 continue;
421             }
422             auto child = wrapper_->GetChildByIndex(itemIdx, isCache);
423             if (!child) {
424                 continue;
425             }
426 
427             SizeF blockSize = SizeF(crossLens_.at(c), lineHeightIt->second, info.axis_);
428             auto childSize = child->GetGeometryNode()->GetMarginFrameSize();
429             auto alignPos = Alignment::GetAlignPosition(blockSize, childSize, align);
430 
431             OffsetF offset = OffsetF(crossPos[c], mainOffset, info.axis_);
432 
433             if (isRtl) {
434                 offset.SetX(frameSize.Width() - offset.GetX() - childSize.Width());
435             }
436             offset += OffsetF { padding.left.value_or(0.0f), 0.0f };
437             child->GetGeometryNode()->SetMarginFrameOffset(offset + alignPos);
438             if (child->CheckNeedForceMeasureAndLayout()) {
439                 child->Layout();
440             } else {
441                 child->GetHostNode()->ForceSyncGeometryNode();
442             }
443         }
444         // add mainGap below the item
445         mainOffset += lineHeightIt->second + mainGap_;
446     }
447 }
448 
CalculateCrossPositions(const PaddingPropertyF & padding)449 std::vector<float> GridIrregularLayoutAlgorithm::CalculateCrossPositions(const PaddingPropertyF& padding)
450 {
451     std::vector<float> res(info_.crossCount_, 0.0f);
452     res[0] = info_.axis_ == Axis::HORIZONTAL ? padding.top.value_or(0.0f) : 0.0f;
453     for (int32_t i = 1; i < info_.crossCount_; ++i) {
454         res[i] = res[i - 1] + crossLens_[i - 1] + crossGap_;
455     }
456     return res;
457 }
458 
FindJumpLineIdx(int32_t jumpIdx)459 int32_t GridIrregularLayoutAlgorithm::FindJumpLineIdx(int32_t jumpIdx)
460 {
461     GridIrregularFiller filler(&info_, wrapper_);
462     int32_t jumpLine = -1;
463     auto it = info_.FindInMatrix(jumpIdx);
464     if (it == info_.gridMatrix_.end()) {
465         // fill matrix up to jumpIndex_
466         jumpLine = filler.FillMatrixOnly(jumpIdx);
467     } else {
468         jumpLine = it->first;
469     }
470 
471     if (info_.scrollAlign_ == ScrollAlign::END) {
472         // jump to the last line the item occupies
473         auto lastLine = jumpLine + GridLayoutUtils::GetItemSize(&info_, wrapper_, jumpIdx).rows - 1;
474         filler.FillMatrixByLine(jumpLine, lastLine + 1);
475         jumpLine = lastLine;
476     }
477     return jumpLine;
478 }
479 
480 using FillParams = GridIrregularFiller::FillParameters;
PrepareLineHeight(float mainSize,int32_t & jumpLineIdx)481 void GridIrregularLayoutAlgorithm::PrepareLineHeight(float mainSize, int32_t& jumpLineIdx)
482 {
483     /* When mainSize can't be filled, adjust parameters and call function again. The maximum length of
484      * the recursion is 3 iterations ([Start && len not filled] -> [End && len not filled] -> [Start with jumpIdx 0]).
485      */
486     GridIrregularFiller filler(&info_, wrapper_);
487     const FillParams params { crossLens_, crossGap_, mainGap_ };
488     switch (info_.scrollAlign_) {
489         case ScrollAlign::START: {
490             // call this to ensure irregular items on the first line are measured, not skipped
491             filler.MeasureLineWithIrregulars(params, jumpLineIdx);
492 
493             float len = filler.Fill(params, mainSize, jumpLineIdx).length;
494             // condition [jumpLineIdx > 0] guarantees a finite call stack
495             if (LessNotEqual(len, mainSize) && jumpLineIdx > 0) {
496                 jumpLineIdx = info_.lineHeightMap_.rbegin()->first;
497                 info_.scrollAlign_ = ScrollAlign::END;
498                 PrepareLineHeight(mainSize, jumpLineIdx);
499             }
500             break;
501         }
502         case ScrollAlign::CENTER: {
503             // because the current line's height is unknown, we can't determine the exact target length to fill.
504             // Using the full [mainSize]
505             const auto pos = info_.GetItemPos(info_.jumpIndex_);
506             const float itemLen = filler.MeasureItem(params, info_.jumpIndex_, pos.first, pos.second, false).first;
507             const float targetLen = mainSize / 2.0f;
508             float backwardLen = filler.MeasureBackward(params, mainSize, jumpLineIdx);
509             backwardLen -= info_.lineHeightMap_.at(jumpLineIdx) / 2.0f;
510             if (LessNotEqual(backwardLen, targetLen)) {
511                 jumpLineIdx = 0;
512                 info_.scrollAlign_ = ScrollAlign::START;
513                 PrepareLineHeight(mainSize, jumpLineIdx);
514                 return;
515             }
516             float forwardLen = filler.Fill(params, std::max(mainSize, itemLen), jumpLineIdx).length;
517             forwardLen -= info_.lineHeightMap_.at(jumpLineIdx) / 2.0f;
518             if (LessNotEqual(forwardLen, targetLen)) {
519                 jumpLineIdx = info_.lineHeightMap_.rbegin()->first;
520                 info_.scrollAlign_ = ScrollAlign::END;
521                 PrepareLineHeight(mainSize, jumpLineIdx);
522             }
523             break;
524         }
525         case ScrollAlign::END: {
526             float len = filler.MeasureBackward(params, mainSize, jumpLineIdx);
527             if (LessNotEqual(len, mainSize)) {
528                 jumpLineIdx = 0;
529                 info_.scrollAlign_ = ScrollAlign::START;
530                 PrepareLineHeight(mainSize, jumpLineIdx);
531             }
532             break;
533         }
534         default:
535             break;
536     }
537 }
538 
539 namespace {
AddLineHeight(float & height,int32_t curLine,int32_t startLine,const std::map<int32_t,float> & lineHeights)540 void AddLineHeight(float& height, int32_t curLine, int32_t startLine, const std::map<int32_t, float>& lineHeights)
541 {
542     auto iter = lineHeights.find(curLine);
543     if (iter != lineHeights.end()) {
544         height += iter->second;
545     } else {
546         // estimation
547         height += height / std::abs(curLine - startLine);
548     }
549 }
550 } // namespace
551 
SkipLinesForward()552 int32_t GridIrregularLayoutAlgorithm::SkipLinesForward()
553 {
554     int32_t line = info_.startMainLineIndex_;
555     float height = 0.0f;
556     while (LessNotEqual(height, -info_.currentOffset_)) {
557         AddLineHeight(height, line++, info_.startMainLineIndex_, info_.lineHeightMap_);
558     }
559     GridIrregularFiller filler(&info_, wrapper_);
560     return filler.FillMatrixByLine(info_.startMainLineIndex_, line);
561 }
562 
SkipLinesBackward() const563 int32_t GridIrregularLayoutAlgorithm::SkipLinesBackward() const
564 {
565     const auto& info = gridLayoutInfo_;
566     float height = info.GetHeightInRange(info.startMainLineIndex_, info.endMainLineIndex_ + 1, 0.0f);
567 
568     float target = info.currentOffset_ + height;
569     int32_t line = info.startMainLineIndex_;
570     while (LessNotEqual(height, target) && line > 0) {
571         AddLineHeight(height, --line, info.endMainLineIndex_, info.lineHeightMap_);
572     }
573     return std::max(0, info.FindEndIdx(line).itemIdx);
574 }
575 
MeasureToTarget()576 void GridIrregularLayoutAlgorithm::MeasureToTarget()
577 {
578     GridIrregularFiller filler(&info_, wrapper_);
579     FillParams param { crossLens_, crossGap_, mainGap_ };
580     if (info_.targetIndex_ < info_.startIndex_) {
581         auto it = info_.FindInMatrix(*info_.targetIndex_);
582         filler.MeasureBackwardToTarget(param, it->first, info_.startMainLineIndex_);
583     } else {
584         filler.FillToTarget(param, *info_.targetIndex_, info_.startMainLineIndex_);
585     }
586 }
587 
IsIrregularLine(int32_t lineIndex) const588 bool GridIrregularLayoutAlgorithm::IsIrregularLine(int32_t lineIndex) const
589 {
590     const auto& line = gridLayoutInfo_.gridMatrix_.find(lineIndex);
591     if (line == gridLayoutInfo_.gridMatrix_.end() || line->second.empty()) {
592         return true;
593     }
594     auto props = AceType::DynamicCast<GridLayoutProperty>(wrapper_->GetLayoutProperty());
595     auto opts = &props->GetLayoutOptions().value();
596     for (const auto& item : line->second) {
597         if (!item.second || opts->irregularIndexes.find(std::abs(item.second)) != opts->irregularIndexes.end()) {
598             return true;
599         }
600     }
601     return false;
602 }
603 
PreloadItems(int32_t cacheCnt)604 void GridIrregularLayoutAlgorithm::PreloadItems(int32_t cacheCnt)
605 {
606     std::list<GridPreloadItem> itemsToPreload;
607     for (int32_t i = 1; i <= cacheCnt; ++i) {
608         const int32_t l = info_.startIndex_ - i;
609         if (l >= 0 && !wrapper_->GetChildByIndex(l, true)) {
610             itemsToPreload.emplace_back(l);
611         }
612         const int32_t r = info_.endIndex_ + i;
613         if (r < info_.childrenCount_ && !wrapper_->GetChildByIndex(r, true)) {
614             itemsToPreload.emplace_back(r);
615         }
616     }
617 
618     GridIrregularFiller filler(&info_, wrapper_);
619     filler.FillMatrixOnly(std::min(info_.childrenCount_, info_.endIndex_ + cacheCnt));
620 
621     GridLayoutUtils::PreloadGridItems(wrapper_->GetHostNode()->GetPattern<GridPattern>(), std::move(itemsToPreload),
622         [crossLens = crossLens_, crossGap = crossGap_, mainGap = mainGap_](
623             const RefPtr<FrameNode>& host, int32_t itemIdx) {
624             CHECK_NULL_RETURN(host, false);
625             auto pattern = host->GetPattern<GridPattern>();
626             CHECK_NULL_RETURN(pattern, false);
627             auto& info = pattern->GetMutableLayoutInfo();
628             GridIrregularFiller filler(&info, RawPtr(host));
629             const auto pos = info.GetItemPos(itemIdx);
630             auto constraint = filler
631                                   .MeasureItem(GridIrregularFiller::FillParameters { crossLens, crossGap, mainGap },
632                                       itemIdx, pos.first, pos.second, true)
633                                   .second;
634 
635             auto item = DynamicCast<FrameNode>(host->GetChildByIndex(itemIdx, true));
636             CHECK_NULL_RETURN(item, false);
637             item->GetGeometryNode()->SetParentLayoutConstraint(constraint);
638             item->Layout();
639             item->SetActive(false);
640             return false;
641         });
642 }
643 } // namespace OHOS::Ace::NG
644