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