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