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