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