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