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/grid_scroll/grid_scroll_with_options_layout_algorithm.h"
17
18 #include "core/components_ng/pattern/grid/grid_utils.h"
19 #include "core/components_ng/pattern/grid/irregular/grid_layout_utils.h"
20 #include "core/components_ng/property/measure_utils.h"
21
22 namespace OHOS::Ace::NG {
23 namespace {
UpdateGridItemRowAndColumnInfo(const RefPtr<LayoutWrapper> & itemLayoutWrapper,GridItemIndexInfo irregualItemInfo)24 void UpdateGridItemRowAndColumnInfo(const RefPtr<LayoutWrapper>& itemLayoutWrapper, GridItemIndexInfo irregualItemInfo)
25 {
26 auto gridItemHost = itemLayoutWrapper->GetHostNode();
27 CHECK_NULL_VOID(gridItemHost);
28 auto gridItemPattern = gridItemHost->GetPattern<GridItemPattern>();
29 CHECK_NULL_VOID(gridItemPattern);
30 gridItemPattern->SetIrregularItemInfo(irregualItemInfo);
31 }
32 } // namespace
33
AdjustRowColSpan(const RefPtr<LayoutWrapper> & itemLayoutWrapper,LayoutWrapper * layoutWrapper,int32_t itemIndex)34 void GridScrollWithOptionsLayoutAlgorithm::AdjustRowColSpan(
35 const RefPtr<LayoutWrapper>& itemLayoutWrapper, LayoutWrapper* layoutWrapper, int32_t itemIndex)
36 {
37 auto layoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
38 CHECK_NULL_VOID(layoutProperty);
39 const auto& options = *layoutProperty->GetLayoutOptions();
40 auto result = GetCrossStartAndSpan(options, itemIndex);
41 if (info_.axis_ == Axis::VERTICAL) {
42 currentItemColStart_ = result.first;
43 currentItemColSpan_ = result.second;
44 currentItemColEnd_ = currentItemColStart_ + currentItemColSpan_ - 1;
45 currentItemRowStart_ = -1;
46 currentItemRowEnd_ = -1;
47 currentItemRowSpan_ = 1;
48 } else {
49 currentItemRowStart_ = result.first;
50 currentItemRowSpan_ = result.second;
51 currentItemRowEnd_ = currentItemRowStart_ + currentItemRowSpan_ - 1;
52 currentItemColStart_ = -1;
53 currentItemColEnd_ = -1;
54 currentItemColSpan_ = 1;
55 }
56
57 if (currentItemRowSpan_ > 1 || currentItemColSpan_ > 1) {
58 info_.hasBigItem_ = true;
59 bool isVertical = info_.axis_ == Axis::VERTICAL;
60 GridItemIndexInfo irregualItemInfo;
61 irregualItemInfo.mainStart = isVertical ? currentItemRowStart_ : currentItemColStart_;
62 irregualItemInfo.mainEnd = isVertical ? currentItemRowEnd_ : currentItemColEnd_;
63 irregualItemInfo.mainSpan = isVertical ? currentItemRowSpan_ : currentItemColSpan_;
64 irregualItemInfo.crossStart = isVertical ? currentItemColStart_ : currentItemRowStart_;
65 irregualItemInfo.crossEnd = isVertical ? currentItemColEnd_ : currentItemRowEnd_;
66 irregualItemInfo.crossSpan = isVertical ? currentItemColSpan_ : currentItemRowSpan_;
67 UpdateGridItemRowAndColumnInfo(itemLayoutWrapper, irregualItemInfo);
68 }
69 }
70
LargeItemLineHeight(const RefPtr<LayoutWrapper> & itemWrapper)71 void GridScrollWithOptionsLayoutAlgorithm::LargeItemLineHeight(const RefPtr<LayoutWrapper>& itemWrapper)
72 {
73 auto itemSize = itemWrapper->GetGeometryNode()->GetMarginFrameSize();
74 auto itemMainSize = GetMainAxisSize(itemSize, info_.axis_);
75 if (LessNotEqual(itemMainSize, 0.0f)) {
76 TAG_LOGI(
77 AceLogTag::ACE_GRID, "item height of index %{public}d is less than zero", info_.endIndex_ + 1);
78 itemMainSize = 0.0f;
79 }
80 cellAveLength_ = std::max(itemMainSize, cellAveLength_);
81 }
82
GetTargetIndexInfoWithBenchMark(LayoutWrapper * layoutWrapper,bool isTargetBackward,int32_t targetIndex)83 void GridScrollWithOptionsLayoutAlgorithm::GetTargetIndexInfoWithBenchMark(
84 LayoutWrapper* layoutWrapper, bool isTargetBackward, int32_t targetIndex)
85 {
86 int32_t benchmarkIndex = (isTargetBackward && !info_.gridMatrix_.empty())
87 ? info_.gridMatrix_.rbegin()->second.rbegin()->second + 1
88 : 0;
89 int32_t mainStartIndex = (isTargetBackward && !info_.gridMatrix_.empty())
90 ? info_.gridMatrix_.rbegin()->first + 1
91 : 0;
92 int32_t currentIndex = benchmarkIndex;
93 int32_t headOfMainStartLine = currentIndex;
94 auto layoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
95 CHECK_NULL_VOID(layoutProperty);
96 const auto& options = *layoutProperty->GetLayoutOptions();
97 while (currentIndex < targetIndex) {
98 int32_t crossGridReserve = info_.crossCount_;
99 /* go through a new line */
100 while ((crossGridReserve > 0) && (currentIndex <= targetIndex)) {
101 auto crossPos = GetCrossStartAndSpan(options, currentIndex);
102 auto gridSpan = crossPos.second;
103 if (crossGridReserve >= gridSpan) {
104 crossGridReserve -= gridSpan;
105 } else if (info_.crossCount_ >= gridSpan) {
106 ++mainStartIndex;
107 headOfMainStartLine = currentIndex;
108 crossGridReserve = info_.crossCount_ - gridSpan;
109 }
110 ++currentIndex;
111 }
112 if (currentIndex > targetIndex) {
113 break;
114 }
115 ++mainStartIndex;
116 headOfMainStartLine = currentIndex;
117 }
118 info_.startMainLineIndex_ = mainStartIndex;
119 info_.startIndex_ = headOfMainStartLine;
120 info_.endIndex_ = headOfMainStartLine - 1;
121 info_.prevOffset_ = 0;
122 info_.currentOffset_ = 0;
123 info_.ResetPositionFlags();
124 info_.gridMatrix_.clear();
125 info_.lineHeightMap_.clear();
126 info_.irregularItemsPosition_.clear();
127 }
128
GetCrossStartAndSpan(const GridLayoutOptions & options,int32_t itemIndex)129 std::pair<int32_t, int32_t> GridScrollWithOptionsLayoutAlgorithm::GetCrossStartAndSpan(
130 const GridLayoutOptions& options, int32_t itemIndex)
131 {
132 if (options.irregularIndexes.empty()) {
133 return std::make_pair(-1, 1);
134 }
135
136 auto firstIrregularIndex = *(options.irregularIndexes.begin());
137 if (itemIndex < firstIrregularIndex) {
138 return std::make_pair(itemIndex % crossCount_, 1);
139 }
140
141 // without function
142 if (!options.getSizeByIndex) {
143 if (options.irregularIndexes.find(itemIndex) != options.irregularIndexes.end()) {
144 return std::make_pair(0, crossCount_);
145 }
146 int32_t crossStart = -1;
147 auto iter = options.irregularIndexes.upper_bound(itemIndex);
148 auto crossCount = static_cast<int32_t>(crossCount_);
149 if (iter == options.irregularIndexes.end()) {
150 crossStart = (itemIndex - (*(options.irregularIndexes.rbegin()) + 1)) % crossCount;
151 } else {
152 if (iter != options.irregularIndexes.begin()) {
153 crossStart = (itemIndex - (*(--iter) + 1)) % crossCount;
154 } else {
155 crossStart = itemIndex % crossCount;
156 }
157 }
158 return std::make_pair(crossStart, 1);
159 }
160
161 return GetCrossStartAndSpanWithUserFunction(itemIndex, options, firstIrregularIndex);
162 }
163
JumpToLastIrregularItem(const std::map<int32_t,int32_t> & irregularItemsPosition,int32_t & sum,int32_t & lastIndex,int32_t targetIndex)164 static void JumpToLastIrregularItem(
165 const std::map<int32_t, int32_t>& irregularItemsPosition, int32_t& sum, int32_t& lastIndex, int32_t targetIndex)
166 {
167 if (irregularItemsPosition.empty()) {
168 return;
169 }
170
171 auto iter = irregularItemsPosition.lower_bound(targetIndex);
172 if (iter == irregularItemsPosition.begin()) {
173 return;
174 }
175 if (iter != irregularItemsPosition.end()) {
176 --iter;
177 sum = iter->second;
178 lastIndex = iter->first;
179 } else {
180 auto lastIter = irregularItemsPosition.rbegin();
181 sum = lastIter->second;
182 lastIndex = lastIter->first;
183 }
184 }
185
ResetInvalidCrossSpan(uint32_t crossCount,int32_t & crossSpan)186 static void ResetInvalidCrossSpan(uint32_t crossCount, int32_t& crossSpan)
187 {
188 if (crossSpan > static_cast<int32_t>(crossCount) || crossSpan <= 0) {
189 crossSpan = 1;
190 }
191 }
192
InitIrregularItemsPosition(std::map<int32_t,int32_t> & irregularItemsPosition,const GridLayoutOptions & options,int32_t firstIrregularIndex,Axis axis,int32_t crossCount)193 static void InitIrregularItemsPosition(std::map<int32_t, int32_t>& irregularItemsPosition,
194 const GridLayoutOptions& options, int32_t firstIrregularIndex, Axis axis, int32_t crossCount)
195 {
196 if (irregularItemsPosition.empty()) {
197 auto sum = firstIrregularIndex;
198 auto crossSpan = options.getSizeByIndex(firstIrregularIndex).GetCrossSize(axis);
199 ResetInvalidCrossSpan(crossCount, crossSpan);
200 // first irregular item in new line
201 if (crossCount != 0) {
202 auto crossStart = sum % crossCount;
203 if (crossStart + crossSpan > crossCount) {
204 sum += (crossCount - crossStart);
205 }
206 }
207 irregularItemsPosition.emplace(firstIrregularIndex, sum + crossSpan);
208 }
209 }
210
GetCrossStartAndSpanWithUserFunction(int32_t itemIndex,const GridLayoutOptions & options,int32_t firstIrregularIndex)211 std::pair<int32_t, int32_t> GridScrollWithOptionsLayoutAlgorithm::GetCrossStartAndSpanWithUserFunction(
212 int32_t itemIndex, const GridLayoutOptions& options, int32_t firstIrregularIndex)
213 {
214 auto crossCount = static_cast<int32_t>(crossCount_);
215 InitIrregularItemsPosition(
216 info_.irregularItemsPosition_, options, firstIrregularIndex, info_.axis_, crossCount);
217 auto sum = firstIrregularIndex;
218 auto lastIndex = firstIrregularIndex;
219 JumpToLastIrregularItem(info_.irregularItemsPosition_, sum, lastIndex, itemIndex);
220 auto iter = options.irregularIndexes.find(lastIndex);
221 if (iter == options.irregularIndexes.end()) {
222 iter = options.irregularIndexes.begin();
223 }
224 for (; iter != options.irregularIndexes.end(); ++iter) {
225 auto index = *iter;
226 if (index >= itemIndex) {
227 break;
228 }
229
230 if (index == lastIndex) {
231 continue;
232 }
233
234 auto crossSpan = options.getSizeByIndex(index).GetCrossSize(info_.axis_);
235 ResetInvalidCrossSpan(crossCount_, crossSpan);
236 auto irregularStart = (sum + index - lastIndex - 1) % crossCount;
237 // put it into next line
238 if (irregularStart + crossSpan > crossCount) {
239 sum += (crossCount - irregularStart);
240 }
241 sum += (index - lastIndex - 1);
242 sum += crossSpan;
243 lastIndex = index;
244 info_.irregularItemsPosition_.emplace(index, sum);
245 }
246 sum += ((itemIndex > lastIndex) ? (itemIndex - lastIndex - 1) : 0);
247 auto crossStart = sum % crossCount;
248 bool isRegularItem = (options.irregularIndexes.find(itemIndex) == options.irregularIndexes.end());
249 auto crossSpan = isRegularItem ? 1 : options.getSizeByIndex(itemIndex).GetCrossSize(info_.axis_);
250 ResetInvalidCrossSpan(crossCount_, crossSpan);
251 if (crossStart + crossSpan > crossCount) {
252 sum += (crossCount - crossStart);
253 crossStart = 0;
254 }
255 if (!isRegularItem) {
256 sum += crossSpan;
257 info_.irregularItemsPosition_.emplace(itemIndex, sum);
258 }
259 return std::make_pair(crossStart, crossSpan);
260 }
261
SkipLargeOffset(float mainSize,LayoutWrapper * layoutWrapper)262 void GridScrollWithOptionsLayoutAlgorithm::SkipLargeOffset(float mainSize, LayoutWrapper* layoutWrapper)
263 {
264 SkipForwardLines(mainSize, layoutWrapper);
265 SkipBackwardLines(mainSize, layoutWrapper);
266 }
267
SkipIrregularLines(LayoutWrapper * layoutWrapper,bool forward)268 void GridScrollWithOptionsLayoutAlgorithm::SkipIrregularLines(LayoutWrapper* layoutWrapper, bool forward)
269 {
270 auto layoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
271 CHECK_NULL_VOID(layoutProperty);
272 const auto& options = *layoutProperty->GetLayoutOptions();
273 if (options.irregularIndexes.empty()) {
274 return SkipRegularLines(forward);
275 }
276 if (options.getSizeByIndex) {
277 return GridScrollLayoutAlgorithm::SkipIrregularLines(layoutWrapper, forward);
278 }
279
280 info_.SkipStartIndexByOffset(options, mainGap_);
281 }
282
CalculateCachedCount(LayoutWrapper * layoutWrapper,int32_t cachedCount)283 std::pair<int32_t, int32_t> GridScrollWithOptionsLayoutAlgorithm::CalculateCachedCount(
284 LayoutWrapper* layoutWrapper, int32_t cachedCount)
285 {
286 if (cachedCount == 0 || info_.crossCount_ == 1) {
287 return std::make_pair(cachedCount, cachedCount);
288 }
289 int32_t cache = cachedCount * info_.crossCount_;
290
291 CHECK_NULL_RETURN(layoutWrapper, std::make_pair(cache, cache));
292 auto props = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
293 CHECK_NULL_RETURN(props, std::make_pair(cache, cache));
294
295 const auto& options = *props->GetLayoutOptions();
296 if (options.irregularIndexes.empty()) {
297 return std::make_pair(cache, cache);
298 }
299 int32_t start = CalculateStartCachedCount(options, cachedCount);
300 int32_t end = CalculateEndCachedCount(options, cachedCount);
301 return std::make_pair(start, end);
302 }
303
CalculateStartCachedCount(const GridLayoutOptions & options,int32_t cachedCount)304 int32_t GridScrollWithOptionsLayoutAlgorithm::CalculateStartCachedCount(
305 const GridLayoutOptions& options, int32_t cachedCount)
306 {
307 int32_t start = cachedCount * info_.crossCount_;
308
309 if (info_.startMainLineIndex_ - cachedCount <= 0) {
310 return std::min(info_.startIndex_, start);
311 }
312
313 auto startLine = info_.gridMatrix_.find(info_.startMainLineIndex_ - cachedCount);
314 if (startLine != info_.gridMatrix_.end()) {
315 auto line = startLine->second;
316 if (!line.empty()) {
317 auto index = line.begin()->second;
318 return info_.startIndex_ - index;
319 }
320 }
321
322 auto firstIrregularIndex = *(options.irregularIndexes.begin());
323 if (info_.startIndex_ <= firstIrregularIndex) {
324 return start;
325 }
326
327 if (!options.getSizeByIndex) {
328 auto iter = options.irregularIndexes.lower_bound(info_.startIndex_);
329 auto crossCount = static_cast<int32_t>(crossCount_);
330 if (iter == options.irregularIndexes.end()) {
331 return start;
332 }
333 if (*iter == info_.startIndex_ && iter != options.irregularIndexes.begin()) {
334 iter--;
335 }
336
337 int lineCount = 0;
338 int sum = 0;
339 int32_t diff = info_.startIndex_ - *(iter)-1;
340 while (lineCount < cachedCount) {
341 if (diff >= (cachedCount - lineCount) * crossCount) {
342 return (cachedCount - lineCount) * crossCount + sum;
343 }
344
345 if (diff == 0) {
346 sum++;
347 lineCount++;
348 }
349
350 if (diff > 0 && diff <= (cachedCount - lineCount - 1) * crossCount) {
351 lineCount += std::ceil(diff / crossCount) + 1;
352 sum += diff;
353 }
354
355 if (iter == options.irregularIndexes.begin()) {
356 return (cachedCount - lineCount) * crossCount + sum;
357 }
358
359 diff = (*iter) - (*(--iter)) - 1;
360 }
361 return sum;
362 }
363 return start;
364 }
365
CalculateEndCachedCount(const GridLayoutOptions & options,int32_t cachedCount)366 int32_t GridScrollWithOptionsLayoutAlgorithm::CalculateEndCachedCount(
367 const GridLayoutOptions& options, int32_t cachedCount)
368 {
369 if (info_.startIndex_ + cachedCount >= info_.GetChildrenCount() - 1) {
370 return info_.startIndex_;
371 }
372
373 int32_t end = cachedCount * info_.crossCount_;
374
375 auto endLine = info_.gridMatrix_.find(info_.endMainLineIndex_ + cachedCount);
376 if (endLine != info_.gridMatrix_.end()) {
377 auto line = endLine->second;
378 if (!line.empty()) {
379 auto index = line.rbegin()->second;
380 return index - info_.endIndex_;
381 }
382 }
383
384 auto lastIrregularIndex = *(options.irregularIndexes.rbegin());
385 if (info_.endIndex_ >= lastIrregularIndex) {
386 return end;
387 }
388
389 if (!options.getSizeByIndex) {
390 auto iter = options.irregularIndexes.upper_bound(info_.endIndex_);
391 auto crossCount = static_cast<int32_t>(crossCount_);
392 if (iter == options.irregularIndexes.end()) {
393 return end;
394 }
395
396 int lineCount = 0;
397 int sum = 0;
398 int32_t diff = *(iter)-info_.endIndex_ - 1;
399 while (lineCount < cachedCount) {
400 if (diff >= (cachedCount - lineCount) * crossCount) {
401 return (cachedCount - lineCount) * crossCount + sum;
402 }
403
404 if (diff == 0) {
405 sum++;
406 lineCount++;
407 }
408
409 if (diff > 0 && diff <= (cachedCount - lineCount - 1) * crossCount) {
410 lineCount += std::ceil(diff / crossCount) + 1;
411 sum += diff;
412 }
413
414 if (iter == options.irregularIndexes.end()) {
415 return (cachedCount - lineCount) * crossCount + sum;
416 }
417
418 diff = -*(iter) + *(++iter) - 1;
419 }
420 return sum;
421 }
422 return end;
423 }
424
PreloadItems(LayoutWrapper * layoutWrapper)425 void GridScrollWithOptionsLayoutAlgorithm::PreloadItems(LayoutWrapper* layoutWrapper)
426 {
427 auto layoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
428 CHECK_NULL_VOID(layoutProperty);
429 const auto& options = *layoutProperty->GetLayoutOptions();
430 std::map<int32_t, std::pair<int32_t, int32_t>> itemCrossMap;
431 for (auto item : predictBuildList_) {
432 auto result = GetCrossStartAndSpan(options, item.idx);
433 itemCrossMap.emplace(item.idx, result);
434 }
435 GridLayoutUtils::PreloadGridItems(layoutWrapper->GetHostNode()->GetPattern<GridPattern>(),
436 std::move(predictBuildList_),
437 [param = GridPredictLayoutParam { cachedChildConstraint_, itemsCrossSize_, crossGap_ }, itemCrossMap](
438 const RefPtr<FrameNode>& host, int32_t itemIdx) {
439 CHECK_NULL_RETURN(host, false);
440 return PredictBuildItem(*host, itemIdx, param, itemCrossMap);
441 });
442 }
443
444 namespace {
GenerateCacheItemConstraint(std::pair<int32_t,int32_t> cross,Axis axis,const GridPredictLayoutParam & param)445 LayoutConstraintF GenerateCacheItemConstraint(
446 std::pair<int32_t, int32_t> cross, Axis axis, const GridPredictLayoutParam& param)
447 {
448 auto constraint = param.layoutConstraint;
449 int32_t crossStart = cross.first;
450 int32_t crossSpan = cross.second;
451 if (crossSpan > 1) {
452 float itemCrossSize = param.crossGap * (crossSpan - 1);
453 for (int32_t index = 0; index < crossSpan; ++index) {
454 int32_t crossIndex = (crossStart + index) % static_cast<int32_t>(param.itemsCrossSizes.size());
455 if (crossIndex >= 0 && crossIndex < static_cast<int32_t>(param.itemsCrossSizes.size())) {
456 itemCrossSize += GetOrDefault(param.itemsCrossSizes, crossIndex, 0.0f);
457 }
458 }
459 constraint.maxSize.SetCrossSize(itemCrossSize, axis);
460 constraint.selfIdealSize.SetCrossSize(itemCrossSize, axis);
461 }
462 return constraint;
463 }
464 } // namespace
465
PredictBuildItem(FrameNode & host,int32_t itemIdx,const GridPredictLayoutParam & param,std::map<int32_t,std::pair<int32_t,int32_t>> itemCrossMap)466 bool GridScrollWithOptionsLayoutAlgorithm::PredictBuildItem(FrameNode& host, int32_t itemIdx,
467 const GridPredictLayoutParam& param, std::map<int32_t, std::pair<int32_t, int32_t>> itemCrossMap)
468 {
469 // build callback
470 auto wrapper = host.GetOrCreateChildByIndex(itemIdx, false, true);
471 CHECK_NULL_RETURN(wrapper, false);
472 auto itemCross = itemCrossMap.find(itemIdx);
473 if (itemCross == itemCrossMap.end()) {
474 return false;
475 }
476 const Axis axis = host.GetPattern<GridPattern>()->GetAxis();
477
478 auto constraint = GenerateCacheItemConstraint(itemCross->second, axis, param);
479 wrapper->SetActive(false);
480 auto frameNode = wrapper->GetHostNode();
481 CHECK_NULL_RETURN(frameNode, false);
482 frameNode->GetGeometryNode()->SetParentLayoutConstraint(constraint);
483 FrameNode::ProcessOffscreenNode(frameNode);
484 return true;
485 }
486
GetStartingItem(LayoutWrapper * layoutWrapper,int32_t currentIndex)487 int32_t GridScrollWithOptionsLayoutAlgorithm::GetStartingItem(LayoutWrapper* layoutWrapper, int32_t currentIndex)
488 {
489 int32_t firstIndex = 0;
490 currentIndex = currentIndex < info_.GetChildrenCount() ? currentIndex : info_.GetChildrenCount() - 1;
491 auto index = currentIndex;
492 while (index > 0) {
493 auto childLayoutWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
494 if (!childLayoutWrapper) {
495 TAG_LOGW(AceLogTag::ACE_GRID, "item [%{public}d] does not exist, reload to [0]", index);
496 break;
497 }
498
499 AdjustRowColSpan(childLayoutWrapper, layoutWrapper, index);
500 auto crossIndex = info_.axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
501 if (crossIndex == -1 && index % info_.crossCount_ == 0) {
502 firstIndex = index;
503 break;
504 }
505 if (crossIndex == 0) {
506 firstIndex = index;
507 break;
508 }
509 --index;
510 }
511
512 return firstIndex;
513 }
514 } // namespace OHOS::Ace::NG
515