1 /*
2 * Copyright (c) 2022-2023 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_pattern.h"
17
18 #include "base/geometry/axis.h"
19 #include "base/utils/utils.h"
20 #include "base/perfmonitor/perf_monitor.h"
21 #include "base/perfmonitor/perf_constants.h"
22 #include "core/components_ng/pattern/grid/grid_adaptive/grid_adaptive_layout_algorithm.h"
23 #include "core/components_ng/pattern/grid/grid_item_layout_property.h"
24 #include "core/components_ng/pattern/grid/grid_item_pattern.h"
25 #include "core/components_ng/pattern/grid/grid_layout/grid_layout_algorithm.h"
26 #include "core/components_ng/pattern/grid/grid_layout_property.h"
27 #include "core/components_ng/pattern/grid/grid_scroll/grid_scroll_layout_algorithm.h"
28 #include "core/components_ng/pattern/grid/grid_scroll/grid_scroll_with_options_layout_algorithm.h"
29 #include "core/components_ng/pattern/grid/grid_utils.h"
30 #include "core/components_ng/pattern/pattern.h"
31 #include "core/components_ng/property/property.h"
32 #include "core/pipeline_ng/pipeline_context.h"
33
34 namespace OHOS::Ace::NG {
35
36 namespace {
37 const Color ITEM_FILL_COLOR = Color::TRANSPARENT;
38 } // namespace
39
CreateLayoutAlgorithm()40 RefPtr<LayoutAlgorithm> GridPattern::CreateLayoutAlgorithm()
41 {
42 auto gridLayoutProperty = GetLayoutProperty<GridLayoutProperty>();
43 CHECK_NULL_RETURN(gridLayoutProperty, nullptr);
44 std::vector<std::string> cols;
45 StringUtils::StringSplitter(gridLayoutProperty->GetColumnsTemplate().value_or(""), ' ', cols);
46 std::vector<std::string> rows;
47 StringUtils::StringSplitter(gridLayoutProperty->GetRowsTemplate().value_or(""), ' ', rows);
48 auto crossCount = cols.empty() ? Infinity<int32_t>() : static_cast<int32_t>(cols.size());
49 auto mainCount = rows.empty() ? Infinity<int32_t>() : static_cast<int32_t>(rows.size());
50 if (!gridLayoutProperty->IsVertical()) {
51 std::swap(crossCount, mainCount);
52 }
53 gridLayoutInfo_.crossCount_ = crossCount;
54
55 // When rowsTemplate and columnsTemplate is both setting, use static layout algorithm.
56 if (!rows.empty() && !cols.empty()) {
57 return MakeRefPtr<GridLayoutAlgorithm>(gridLayoutInfo_, crossCount, mainCount);
58 }
59
60 // When rowsTemplate and columnsTemplate is both not setting, use adaptive layout algorithm.
61 if (rows.empty() && cols.empty()) {
62 return MakeRefPtr<GridAdaptiveLayoutAlgorithm>(gridLayoutInfo_);
63 }
64
65 // If only set one of rowTemplate and columnsTemplate, use scrollable layout algorithm.
66 if (!gridLayoutProperty->GetLayoutOptions().has_value()) {
67 auto result = MakeRefPtr<GridScrollLayoutAlgorithm>(gridLayoutInfo_, crossCount, mainCount);
68 result->SetCanOverScroll(CanOverScroll(GetScrollSource()));
69 return result;
70 } else {
71 auto result = MakeRefPtr<GridScrollWithOptionsLayoutAlgorithm>(gridLayoutInfo_, crossCount, mainCount);
72 result->SetCanOverScroll(CanOverScroll(GetScrollSource()));
73 return result;
74 }
75 }
76
CreateNodePaintMethod()77 RefPtr<NodePaintMethod> GridPattern::CreateNodePaintMethod()
78 {
79 auto paint = MakeRefPtr<GridPaintMethod>(GetScrollBar());
80 CHECK_NULL_RETURN(paint, nullptr);
81 CreateScrollBarOverlayModifier();
82 paint->SetScrollBarOverlayModifier(GetScrollBarOverlayModifier());
83 auto scrollEffect = GetScrollEdgeEffect();
84 if (scrollEffect && scrollEffect->IsFadeEffect()) {
85 paint->SetEdgeEffect(scrollEffect);
86 }
87 return paint;
88 }
89
InitScrollableEvent()90 void GridPattern::InitScrollableEvent()
91 {
92 auto host = GetHost();
93 CHECK_NULL_VOID(host);
94 auto eventHub = host->GetEventHub<GridEventHub>();
95 CHECK_NULL_VOID(eventHub);
96 auto scrollFrameBeginEvent = eventHub->GetOnScrollFrameBegin();
97 SetScrollFrameBeginCallback(scrollFrameBeginEvent);
98 }
99
OnModifyDone()100 void GridPattern::OnModifyDone()
101 {
102 auto gridLayoutProperty = GetLayoutProperty<GridLayoutProperty>();
103 CHECK_NULL_VOID(gridLayoutProperty);
104
105 if (multiSelectable_ && !isMouseEventInit_) {
106 InitMouseEvent();
107 }
108
109 if (!multiSelectable_ && isMouseEventInit_) {
110 UninitMouseEvent();
111 }
112
113 gridLayoutInfo_.axis_ = gridLayoutProperty->IsVertical() ? Axis::VERTICAL : Axis::HORIZONTAL;
114 isConfigScrollable_ = gridLayoutProperty->IsConfiguredScrollable();
115 if (!isConfigScrollable_) {
116 LOGD("use fixed grid template");
117 return;
118 }
119 SetAxis(gridLayoutInfo_.axis_);
120 if (!GetScrollableEvent()) {
121 AddScrollEvent();
122 InitScrollableEvent();
123 }
124
125 auto edgeEffect = gridLayoutProperty->GetEdgeEffect().value_or(EdgeEffect::NONE);
126 SetEdgeEffect(edgeEffect);
127
128 auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
129 CHECK_NULL_VOID(paintProperty);
130 if (paintProperty->GetScrollBarProperty()) {
131 SetScrollBar(paintProperty->GetScrollBarProperty());
132 }
133
134 auto host = GetHost();
135 CHECK_NULL_VOID(host);
136 auto focusHub = host->GetFocusHub();
137 if (focusHub) {
138 InitOnKeyEvent(focusHub);
139 }
140 SetAccessibilityAction();
141 auto scrollable = GetScrollableEvent()->GetScrollable();
142 if (scrollable) {
143 scrollable->SetOnContinuousSliding([weak = AceType::WeakClaim(this)]() -> double {
144 auto grid = weak.Upgrade();
145 return grid->GetMainContentSize();
146 });
147 }
148 }
149
MultiSelectWithoutKeyboard(const RectF & selectedZone)150 void GridPattern::MultiSelectWithoutKeyboard(const RectF& selectedZone)
151 {
152 auto host = GetHost();
153 CHECK_NULL_VOID(host);
154 auto& children = host->GetFrameChildren();
155 for (const auto& weak : children) {
156 auto itemFrameNode = weak.Upgrade();
157 CHECK_NULL_VOID(itemFrameNode);
158 auto itemEvent = itemFrameNode->GetEventHub<EventHub>();
159 CHECK_NULL_VOID(itemEvent);
160 if (!itemEvent->IsEnabled()) {
161 continue;
162 }
163
164 auto itemPattern = itemFrameNode->GetPattern<GridItemPattern>();
165 CHECK_NULL_VOID(itemPattern);
166 if (!itemPattern->Selectable()) {
167 continue;
168 }
169 auto itemGeometry = itemFrameNode->GetGeometryNode();
170 CHECK_NULL_VOID(itemGeometry);
171 auto context = itemFrameNode->GetRenderContext();
172 CHECK_NULL_VOID(context);
173
174 auto itemRect = itemGeometry->GetFrameRect();
175 auto iter = itemToBeSelected_.find(itemFrameNode->GetId());
176 if (iter == itemToBeSelected_.end()) {
177 auto result = itemToBeSelected_.emplace(itemFrameNode->GetId(), ItemSelectedStatus());
178 iter = result.first;
179 iter->second.onSelected = itemPattern->GetEventHub<GridItemEventHub>()->GetOnSelect();
180 iter->second.selectChangeEvent = itemPattern->GetEventHub<GridItemEventHub>()->GetSelectChangeEvent();
181 auto startMainOffset = mouseStartOffset_.GetMainOffset(gridLayoutInfo_.axis_);
182 if (gridLayoutInfo_.axis_ == Axis::VERTICAL) {
183 iter->second.rect = itemRect + OffsetF(0, totalOffsetOfMousePressed_ - startMainOffset);
184 } else {
185 iter->second.rect = itemRect + OffsetF(totalOffsetOfMousePressed_ - startMainOffset, 0);
186 }
187 }
188
189 if (!selectedZone.IsIntersectWith(itemRect)) {
190 itemPattern->MarkIsSelected(false);
191 iter->second.selected = false;
192 context->OnMouseSelectUpdate(false, ITEM_FILL_COLOR, ITEM_FILL_COLOR);
193 } else {
194 itemPattern->MarkIsSelected(true);
195 iter->second.selected = true;
196 context->OnMouseSelectUpdate(true, ITEM_FILL_COLOR, ITEM_FILL_COLOR);
197 }
198 }
199
200 DrawSelectedZone(selectedZone);
201 }
202
ClearMultiSelect()203 void GridPattern::ClearMultiSelect()
204 {
205 auto host = GetHost();
206 CHECK_NULL_VOID(host);
207 std::list<RefPtr<FrameNode>> children;
208 host->GenerateOneDepthAllFrame(children);
209 for (const auto& item : children) {
210 if (!AceType::InstanceOf<FrameNode>(item)) {
211 continue;
212 }
213
214 auto itemFrameNode = AceType::DynamicCast<FrameNode>(item);
215 auto itemPattern = itemFrameNode->GetPattern<GridItemPattern>();
216 CHECK_NULL_VOID(itemPattern);
217 auto selectedStatus = itemToBeSelected_.find(itemFrameNode->GetId());
218 if (selectedStatus != itemToBeSelected_.end()) {
219 selectedStatus->second.selected = false;
220 }
221 itemPattern->MarkIsSelected(false);
222 auto renderContext = itemFrameNode->GetRenderContext();
223 CHECK_NULL_VOID(renderContext);
224 renderContext->OnMouseSelectUpdate(false, ITEM_FILL_COLOR, ITEM_FILL_COLOR);
225 }
226
227 ClearSelectedZone();
228 }
229
IsItemSelected(const MouseInfo & info)230 bool GridPattern::IsItemSelected(const MouseInfo& info)
231 {
232 auto host = GetHost();
233 CHECK_NULL_RETURN(host, false);
234 auto node = host->FindChildByPosition(info.GetGlobalLocation().GetX(), info.GetGlobalLocation().GetY());
235 CHECK_NULL_RETURN_NOLOG(node, false);
236 auto itemPattern = node->GetPattern<GridItemPattern>();
237 CHECK_NULL_RETURN_NOLOG(itemPattern, false);
238 return itemPattern->IsSelected();
239 }
240
GetMainContentSize() const241 float GridPattern::GetMainContentSize() const
242 {
243 auto host = GetHost();
244 CHECK_NULL_RETURN(host, 0.0);
245 auto geometryNode = host->GetGeometryNode();
246 CHECK_NULL_RETURN(geometryNode, 0.0);
247 return geometryNode->GetPaddingSize().MainSize(gridLayoutInfo_.axis_);
248 }
249
FireOnScrollStart()250 void GridPattern::FireOnScrollStart()
251 {
252 PerfMonitor::GetPerfMonitor()->Start(PerfConstants::APP_LIST_FLING, PerfActionType::FIRST_MOVE, "");
253 if (GetScrollAbort()) {
254 return;
255 }
256 auto scrollBar = GetScrollBar();
257 if (scrollBar) {
258 scrollBar->PlayScrollBarStartAnimation();
259 }
260 StopScrollBarAnimatorByProxy();
261 auto host = GetHost();
262 CHECK_NULL_VOID(host);
263 auto hub = host->GetEventHub<GridEventHub>();
264 CHECK_NULL_VOID_NOLOG(hub);
265 auto onScrollStart = hub->GetOnScrollStart();
266 CHECK_NULL_VOID_NOLOG(onScrollStart);
267 onScrollStart();
268 }
269
OnScrollCallback(float offset,int32_t source)270 bool GridPattern::OnScrollCallback(float offset, int32_t source)
271 {
272 if (source == SCROLL_FROM_START) {
273 FireOnScrollStart();
274 return true;
275 }
276 return ScrollablePattern::OnScrollCallback(offset, source);
277 }
278
GetContentSize() const279 SizeF GridPattern::GetContentSize() const
280 {
281 auto host = GetHost();
282 CHECK_NULL_RETURN(host, SizeF());
283 auto geometryNode = host->GetGeometryNode();
284 CHECK_NULL_RETURN(geometryNode, SizeF());
285 return geometryNode->GetPaddingSize();
286 }
287
CheckRestartSpring()288 void GridPattern::CheckRestartSpring()
289 {
290 if (!ScrollableIdle() || !IsOutOfBoundary()) {
291 return;
292 }
293 auto edgeEffect = GetScrollEdgeEffect();
294 if (!edgeEffect || !edgeEffect->IsSpringEffect()) {
295 return;
296 }
297 if (AnimateRunning()) {
298 return;
299 }
300
301 FireOnScrollStart();
302 edgeEffect->ProcessScrollOver(0);
303 }
304
GetMainGap()305 float GridPattern::GetMainGap()
306 {
307 float mainGap = 0.0;
308 auto host = GetHost();
309 CHECK_NULL_RETURN(host, 0.0);
310 auto geometryNode = host->GetGeometryNode();
311 CHECK_NULL_RETURN(geometryNode, 0.0);
312 auto viewScopeSize = geometryNode->GetPaddingSize();
313 auto layoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
314 mainGap = GridUtils::GetMainGap(layoutProperty, viewScopeSize, gridLayoutInfo_.axis_);
315 return mainGap;
316 }
317
UpdateCurrentOffset(float offset,int32_t source)318 bool GridPattern::UpdateCurrentOffset(float offset, int32_t source)
319 {
320 if (!isConfigScrollable_ || !scrollable_) {
321 return true;
322 }
323
324 auto host = GetHost();
325 CHECK_NULL_RETURN(host, false);
326 // check edgeEffect is not springEffect
327 if (!HandleEdgeEffect(offset, source, GetContentSize())) {
328 if (IsOutOfBoundary()) {
329 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
330 }
331 return false;
332 }
333 SetScrollSource(source);
334
335 // When finger moves down, offset is positive.
336 // When finger moves up, offset is negative.
337 auto itemsHeight = gridLayoutInfo_.GetTotalHeightOfItemsInView(GetMainGap());
338 float overScroll = 0.0f;
339 if (gridLayoutInfo_.offsetEnd_) {
340 overScroll = gridLayoutInfo_.currentOffset_ - (GetMainContentSize() - itemsHeight);
341 if (source == SCROLL_FROM_UPDATE) {
342 auto friction = CalculateFriction(std::abs(overScroll) / GetMainContentSize());
343 gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
344 gridLayoutInfo_.currentOffset_ = gridLayoutInfo_.currentOffset_ + offset * friction;
345 overScroll += offset * friction;
346 } else {
347 gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
348 gridLayoutInfo_.currentOffset_ += offset;
349 overScroll += offset;
350 }
351 HandleScrollBarOutBoundary(overScroll);
352 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
353
354 if (GreatOrEqual(gridLayoutInfo_.currentOffset_, GetMainContentSize() - itemsHeight)) {
355 gridLayoutInfo_.offsetEnd_ = false;
356 gridLayoutInfo_.reachEnd_ = false;
357 }
358 return true;
359 }
360 if (gridLayoutInfo_.reachStart_) {
361 if (source == SCROLL_FROM_UPDATE) {
362 auto friction = CalculateFriction(std::abs(gridLayoutInfo_.currentOffset_) / GetMainContentSize());
363 gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
364 gridLayoutInfo_.currentOffset_ = gridLayoutInfo_.currentOffset_ + offset * friction;
365 } else {
366 gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
367 gridLayoutInfo_.currentOffset_ += offset;
368 }
369 overScroll = gridLayoutInfo_.currentOffset_;
370 HandleScrollBarOutBoundary(overScroll);
371 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
372
373 if (LessOrEqual(gridLayoutInfo_.currentOffset_, 0.0)) {
374 gridLayoutInfo_.reachStart_ = false;
375 }
376 return true;
377 }
378 // maybe no measure after last update
379 if (LessNotEqual(std::abs(gridLayoutInfo_.currentOffset_), gridLayoutInfo_.lastMainSize_)) {
380 gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
381 }
382 gridLayoutInfo_.currentOffset_ += offset;
383 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
384 return true;
385 }
386
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)387 bool GridPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
388 {
389 if (config.skipMeasure && config.skipLayout) {
390 return false;
391 }
392 auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
393 CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
394 auto gridLayoutAlgorithm = DynamicCast<GridLayoutBaseAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
395 CHECK_NULL_RETURN(gridLayoutAlgorithm, false);
396 const auto& gridLayoutInfo = gridLayoutAlgorithm->GetGridLayoutInfo();
397 auto eventhub = GetEventHub<GridEventHub>();
398 CHECK_NULL_RETURN(eventhub, false);
399 Dimension offset(0, DimensionUnit::VP);
400 Dimension offsetPx(gridLayoutInfo.currentOffset_, DimensionUnit::PX);
401 auto offsetVpValue = offsetPx.ConvertToVp();
402 offset.SetValue(offsetVpValue);
403 scrollbarInfo_ = eventhub->FireOnScrollBarUpdate(gridLayoutInfo.startIndex_, offset);
404 if (firstShow_ || gridLayoutInfo_.startIndex_ != gridLayoutInfo.startIndex_) {
405 eventhub->FireOnScrollToIndex(gridLayoutInfo.startIndex_);
406 firstShow_ = false;
407 }
408
409 bool indexChanged = (gridLayoutInfo.startIndex_ != gridLayoutInfo_.startIndex_) ||
410 (gridLayoutInfo.endIndex_ != gridLayoutInfo_.endIndex_);
411 bool offsetEnd = gridLayoutInfo_.offsetEnd_;
412 gridLayoutInfo_ = gridLayoutInfo;
413 gridLayoutInfo_.childrenCount_ = dirty->GetTotalChildCount();
414 currentHeight_ = EstimateHeight();
415 if (!offsetEnd && gridLayoutInfo_.offsetEnd_) {
416 endHeight_ = currentHeight_;
417 }
418 ProcessEvent(indexChanged, currentHeight_ - prevHeight_);
419 prevHeight_ = currentHeight_;
420 SetScrollSource(SCROLL_FROM_NONE);
421 UpdateScrollBarOffset();
422 if (config.frameSizeChange) {
423 if (GetScrollBar() != nullptr) {
424 GetScrollBar()->ScheduleDisapplearDelayTask();
425 }
426 }
427 CheckRestartSpring();
428 CheckScrollable();
429 MarkSelectedItems();
430 return false;
431 }
432
CheckScrollable()433 void GridPattern::CheckScrollable()
434 {
435 auto host = GetHost();
436 CHECK_NULL_VOID(host);
437 auto gridLayoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
438 CHECK_NULL_VOID(gridLayoutProperty);
439 if (((gridLayoutInfo_.endIndex_ - gridLayoutInfo_.startIndex_ + 1) < gridLayoutInfo_.childrenCount_) ||
440 (gridLayoutInfo_.GetTotalHeightOfItemsInView(GetMainGap()) > GetMainContentSize())) {
441 scrollable_ = true;
442 } else {
443 if (gridLayoutInfo_.startMainLineIndex_ != 0) {
444 scrollable_ = true;
445 } else {
446 scrollable_ = false;
447 }
448 }
449
450 SetScrollEnable(scrollable_);
451
452 if (!gridLayoutProperty->GetScrollEnabled().value_or(scrollable_)) {
453 SetScrollEnable(false);
454 }
455 }
456
ProcessEvent(bool indexChanged,float finalOffset)457 void GridPattern::ProcessEvent(bool indexChanged, float finalOffset)
458 {
459 auto host = GetHost();
460 CHECK_NULL_VOID(host);
461 auto gridEventHub = host->GetEventHub<GridEventHub>();
462 CHECK_NULL_VOID(gridEventHub);
463
464 auto onScroll = gridEventHub->GetOnScroll();
465 auto scrollSource = GetScrollSource();
466 if (scrollStop_ && !GetScrollAbort()) {
467 auto offsetPX = Dimension(finalOffset);
468 auto offsetVP = Dimension(offsetPX.ConvertToVp(), DimensionUnit::VP);
469 if (onScroll) {
470 if (scrollSource == SCROLL_FROM_UPDATE || scrollSource == SCROLL_FROM_AXIS ||
471 scrollSource == SCROLL_FROM_BAR) {
472 onScroll(offsetVP, ScrollState::SCROLL);
473 onScroll(0.0_vp, ScrollState::IDLE);
474 } else if (scrollSource == SCROLL_FROM_ANIMATION || scrollSource == SCROLL_FROM_ANIMATION_SPRING ||
475 scrollSource == SCROLL_FROM_ANIMATION_CONTROLLER || scrollSource == SCROLL_FROM_BAR_FLING) {
476 onScroll(offsetVP, ScrollState::FLING);
477 onScroll(0.0_vp, ScrollState::IDLE);
478 } else {
479 onScroll(offsetVP, ScrollState::IDLE);
480 }
481 }
482 } else if (onScroll && !NearZero(finalOffset)) {
483 auto offsetPX = Dimension(finalOffset);
484 auto offsetVP = Dimension(offsetPX.ConvertToVp(), DimensionUnit::VP);
485 if (scrollSource == SCROLL_FROM_UPDATE || scrollSource == SCROLL_FROM_AXIS || scrollSource == SCROLL_FROM_BAR) {
486 onScroll(offsetVP, ScrollState::SCROLL);
487 } else if (scrollSource == SCROLL_FROM_ANIMATION || scrollSource == SCROLL_FROM_ANIMATION_SPRING ||
488 scrollSource == SCROLL_FROM_ANIMATION_CONTROLLER || scrollSource == SCROLL_FROM_BAR_FLING) {
489 onScroll(offsetVP, ScrollState::FLING);
490 } else {
491 onScroll(offsetVP, ScrollState::IDLE);
492 }
493 }
494
495 if (indexChanged) {
496 auto onScrollIndex = gridEventHub->GetOnScrollIndex();
497 if (onScrollIndex) {
498 onScrollIndex(gridLayoutInfo_.startIndex_, gridLayoutInfo_.endIndex_);
499 }
500 }
501
502 auto onReachStart = gridEventHub->GetOnReachStart();
503 if (onReachStart && gridLayoutInfo_.startIndex_ == 0) {
504 if (!initialIndex_) {
505 onReachStart();
506 initialIndex_ = true;
507 }
508
509 if (!NearZero(finalOffset)) {
510 bool scrollUpToStart = GreatOrEqual(prevHeight_, 0.0) && LessOrEqual(currentHeight_, 0.0);
511 bool scrollDownToStart = LessNotEqual(prevHeight_, 0.0) && GreatOrEqual(currentHeight_, 0.0);
512 if (scrollUpToStart || scrollDownToStart) {
513 onReachStart();
514 }
515 }
516 }
517
518 auto onReachEnd = gridEventHub->GetOnReachEnd();
519 if (onReachEnd && gridLayoutInfo_.endIndex_ == (gridLayoutInfo_.childrenCount_ - 1)) {
520 if (!NearZero(finalOffset)) {
521 bool scrollDownToEnd = LessNotEqual(prevHeight_, endHeight_) && GreatOrEqual(currentHeight_, endHeight_);
522 bool scrollUpToEnd = GreatNotEqual(prevHeight_, endHeight_) && LessOrEqual(currentHeight_, endHeight_);
523 if (scrollDownToEnd || scrollUpToEnd) {
524 onReachEnd();
525 }
526 }
527 }
528
529 if (scrollStop_) {
530 auto onScrollStop = gridEventHub->GetOnScrollStop();
531 if (!GetScrollAbort()) {
532 if (onScrollStop) {
533 SetScrollSource(SCROLL_FROM_NONE);
534 onScrollStop();
535 }
536 auto scrollBar = GetScrollBar();
537 if (scrollBar) {
538 scrollBar->ScheduleDisapplearDelayTask();
539 }
540 StartScrollBarAnimatorByProxy();
541 }
542 if (!GetScrollAbort()) {
543 PerfMonitor::GetPerfMonitor()->End(PerfConstants::APP_LIST_FLING, false);
544 }
545 scrollStop_ = false;
546 SetScrollAbort(false);
547 }
548 }
549
MarkDirtyNodeSelf()550 void GridPattern::MarkDirtyNodeSelf()
551 {
552 auto host = GetHost();
553 CHECK_NULL_VOID(host);
554 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
555 }
556
OnScrollEndCallback()557 void GridPattern::OnScrollEndCallback()
558 {
559 scrollStop_ = true;
560 MarkDirtyNodeSelf();
561 }
562
OnScrollStartCallback()563 void GridPattern::OnScrollStartCallback()
564 {
565 FireOnScrollStart();
566 }
567
IsFirstOrLastFocusableChild(int32_t curMainIndex,int32_t curCrossIndex)568 std::pair<bool, bool> GridPattern::IsFirstOrLastFocusableChild(int32_t curMainIndex, int32_t curCrossIndex)
569 {
570 auto crossIndexSet = GetFocusableChildCrossIndexesAt(curMainIndex);
571 auto findLesser = std::find_if(crossIndexSet.begin(), crossIndexSet.end(),
572 [curCrossIndex](int32_t crossIndex) { return curCrossIndex > crossIndex; });
573 auto findGreater = std::find_if(crossIndexSet.begin(), crossIndexSet.end(),
574 [curCrossIndex](int32_t crossIndex) { return curCrossIndex < crossIndex; });
575 return { findLesser == crossIndexSet.end(), findGreater == crossIndexSet.end() };
576 }
577
GetFocusSteps(int32_t curMainIndex,int32_t curCrossIndex,FocusStep step)578 std::pair<FocusStep, FocusStep> GridPattern::GetFocusSteps(int32_t curMainIndex, int32_t curCrossIndex, FocusStep step)
579 {
580 auto firstStep = FocusStep::NONE;
581 auto secondStep = FocusStep::NONE;
582 auto isFirstOrLastFocusable = IsFirstOrLastFocusableChild(curMainIndex, curCrossIndex);
583 auto isFirstFocusable = isFirstOrLastFocusable.first;
584 auto isLastFocusable = isFirstOrLastFocusable.second;
585 if (gridLayoutInfo_.axis_ == Axis::VERTICAL) {
586 if (isFirstFocusable && step == FocusStep::SHIFT_TAB) {
587 firstStep = FocusStep::UP;
588 secondStep = FocusStep::RIGHT_END;
589 } else if (isLastFocusable && step == FocusStep::TAB) {
590 firstStep = FocusStep::DOWN;
591 secondStep = FocusStep::LEFT_END;
592 }
593 } else if (gridLayoutInfo_.axis_ == Axis::HORIZONTAL) {
594 if (isFirstFocusable && step == FocusStep::SHIFT_TAB) {
595 firstStep = FocusStep::LEFT;
596 secondStep = FocusStep::DOWN_END;
597 } else if (isLastFocusable && step == FocusStep::TAB) {
598 firstStep = FocusStep::RIGHT;
599 secondStep = FocusStep::UP_END;
600 }
601 }
602 LOGI("Get focus steps. First step is %{public}d. Second step is %{public}d", firstStep, secondStep);
603 return { firstStep, secondStep };
604 }
605
GetNextFocusNode(FocusStep step,const WeakPtr<FocusHub> & currentFocusNode)606 WeakPtr<FocusHub> GridPattern::GetNextFocusNode(FocusStep step, const WeakPtr<FocusHub>& currentFocusNode)
607 {
608 auto curFocus = currentFocusNode.Upgrade();
609 CHECK_NULL_RETURN(curFocus, nullptr);
610 auto curFrame = curFocus->GetFrameNode();
611 CHECK_NULL_RETURN(curFrame, nullptr);
612 auto curPattern = curFrame->GetPattern();
613 CHECK_NULL_RETURN(curPattern, nullptr);
614 auto curItemPattern = AceType::DynamicCast<GridItemPattern>(curPattern);
615 CHECK_NULL_RETURN(curItemPattern, nullptr);
616 auto curItemProperty = curItemPattern->GetLayoutProperty<GridItemLayoutProperty>();
617 CHECK_NULL_RETURN(curItemProperty, nullptr);
618
619 auto curMainIndex = curItemProperty->GetMainIndex().value_or(-1);
620 auto curCrossIndex = curItemProperty->GetCrossIndex().value_or(-1);
621 auto curMainSpan = curItemProperty->GetMainSpan(gridLayoutInfo_.axis_);
622 auto curCrossSpan = curItemProperty->GetCrossSpan(gridLayoutInfo_.axis_);
623 if (curMainIndex < 0 || curCrossIndex < 0) {
624 LOGE("can't find focused child.");
625 return nullptr;
626 }
627 if (gridLayoutInfo_.gridMatrix_.find(curMainIndex) == gridLayoutInfo_.gridMatrix_.end()) {
628 LOGE("Can not find current main index: %{public}d", curMainIndex);
629 return nullptr;
630 }
631 LOGI("GetNextFocusNode: Current focused item is (%{public}d,%{public}d)-[%{public}d,%{public}d]. Focus step is "
632 "%{public}d",
633 curMainIndex, curCrossIndex, curMainSpan, curCrossSpan, step);
634 auto focusSteps = GetFocusSteps(curMainIndex, curCrossIndex, step);
635 if (focusSteps.first != FocusStep::NONE && focusSteps.second != FocusStep::NONE) {
636 auto firstStepRes = GetNextFocusNode(focusSteps.first, currentFocusNode);
637 if (!firstStepRes.Upgrade()) {
638 return nullptr;
639 }
640 auto secondStepRes = GetNextFocusNode(focusSteps.second, firstStepRes);
641 if (!secondStepRes.Upgrade()) {
642 return firstStepRes;
643 }
644 return secondStepRes;
645 }
646 auto indexes = GetNextIndexByStep(curMainIndex, curCrossIndex, curMainSpan, curCrossSpan, step);
647 auto nextMainIndex = indexes.first;
648 auto nextCrossIndex = indexes.second;
649 while (nextMainIndex >= 0 && nextCrossIndex >= 0) {
650 if (gridLayoutInfo_.gridMatrix_.find(nextMainIndex) == gridLayoutInfo_.gridMatrix_.end()) {
651 LOGE("Can not find next main index: %{public}d", nextMainIndex);
652 return nullptr;
653 }
654 auto nextMaxCrossCount = GetCrossCount();
655 auto flag = (step == FocusStep::LEFT_END) || (step == FocusStep::RIGHT_END);
656 auto weakChild = SearchFocusableChildInCross(
657 nextMainIndex, nextCrossIndex, nextMaxCrossCount, flag ? -1 : curMainIndex, curCrossIndex);
658 auto child = weakChild.Upgrade();
659 if (child && child->IsFocusable()) {
660 ScrollToFocusNode(weakChild);
661 return weakChild;
662 }
663 auto indexes = GetNextIndexByStep(nextMainIndex, nextCrossIndex, 1, 1, step);
664 nextMainIndex = indexes.first;
665 nextCrossIndex = indexes.second;
666 }
667 return nullptr;
668 }
669
GetNextIndexByStep(int32_t curMainIndex,int32_t curCrossIndex,int32_t curMainSpan,int32_t curCrossSpan,FocusStep step)670 std::pair<int32_t, int32_t> GridPattern::GetNextIndexByStep(
671 int32_t curMainIndex, int32_t curCrossIndex, int32_t curMainSpan, int32_t curCrossSpan, FocusStep step)
672 {
673 LOGI("Current item: (%{public}d,%{public}d)-[%{public}d,%{public}d]. Grid axis: %{public}d, step: %{public}d",
674 curMainIndex, curCrossIndex, curMainSpan, curCrossSpan, gridLayoutInfo_.axis_, step);
675 auto curMainStart = gridLayoutInfo_.startMainLineIndex_;
676 auto curMainEnd = gridLayoutInfo_.endMainLineIndex_;
677 auto curChildStartIndex = gridLayoutInfo_.startIndex_;
678 auto curChildEndIndex = gridLayoutInfo_.endIndex_;
679 auto childrenCount = gridLayoutInfo_.childrenCount_;
680 if (gridLayoutInfo_.gridMatrix_.find(curMainIndex) == gridLayoutInfo_.gridMatrix_.end()) {
681 LOGE("Can not find current main index: %{public}d", curMainIndex);
682 return { -1, -1 };
683 }
684 auto curMaxCrossCount = GetCrossCount();
685 LOGD("Current main index start-end: %{public}d-%{public}d, Current cross count: %{public}d, Current child "
686 "index start-end: %{public}d-%{public}d, Total children count: %{public}d",
687 curMainStart, curMainEnd, curMaxCrossCount, curChildStartIndex, curChildEndIndex, childrenCount);
688
689 auto nextMainIndex = curMainIndex;
690 auto nextCrossIndex = curCrossIndex;
691 if ((step == FocusStep::UP_END && gridLayoutInfo_.axis_ == Axis::HORIZONTAL) ||
692 (step == FocusStep::LEFT_END && gridLayoutInfo_.axis_ == Axis::VERTICAL)) {
693 nextMainIndex = curMainIndex;
694 nextCrossIndex = 0;
695 } else if ((step == FocusStep::DOWN_END && gridLayoutInfo_.axis_ == Axis::HORIZONTAL) ||
696 (step == FocusStep::RIGHT_END && gridLayoutInfo_.axis_ == Axis::VERTICAL)) {
697 nextMainIndex = curMainIndex;
698 nextCrossIndex = curMaxCrossCount - 1;
699 } else if (((step == FocusStep::UP || step == FocusStep::SHIFT_TAB) && gridLayoutInfo_.axis_ == Axis::HORIZONTAL) ||
700 ((step == FocusStep::LEFT || step == FocusStep::SHIFT_TAB) && gridLayoutInfo_.axis_ == Axis::VERTICAL)) {
701 nextMainIndex = curMainIndex;
702 nextCrossIndex = curCrossIndex - 1;
703 } else if ((step == FocusStep::UP && gridLayoutInfo_.axis_ == Axis::VERTICAL) ||
704 (step == FocusStep::LEFT && gridLayoutInfo_.axis_ == Axis::HORIZONTAL)) {
705 nextMainIndex = curMainIndex - 1;
706 nextCrossIndex = curCrossIndex + static_cast<int32_t>((curCrossSpan - 1) / 2);
707 } else if (((step == FocusStep::DOWN || step == FocusStep::TAB) && gridLayoutInfo_.axis_ == Axis::HORIZONTAL) ||
708 ((step == FocusStep::RIGHT || step == FocusStep::TAB) && gridLayoutInfo_.axis_ == Axis::VERTICAL)) {
709 nextMainIndex = curMainIndex;
710 nextCrossIndex = curCrossIndex + curCrossSpan;
711 } else if ((step == FocusStep::DOWN && gridLayoutInfo_.axis_ == Axis::VERTICAL) ||
712 (step == FocusStep::RIGHT && gridLayoutInfo_.axis_ == Axis::HORIZONTAL)) {
713 nextMainIndex = curMainIndex + curMainSpan;
714 nextCrossIndex = curCrossIndex + static_cast<int32_t>((curCrossSpan - 1) / 2);
715 } else {
716 LOGE("Next index return: Invalid step: %{public}d and axis: %{public}d", step, gridLayoutInfo_.axis_);
717 return { -1, -1 };
718 }
719 if (curChildStartIndex == 0 && curMainIndex == 0 && nextMainIndex < curMainIndex) {
720 LOGD("Item reach at grid top and next main index less than current main index. Reset next main index.");
721 nextMainIndex = curMainIndex;
722 }
723 if (curChildEndIndex == childrenCount - 1 && curMainIndex == curMainEnd && nextMainIndex > curMainIndex) {
724 LOGD("Item reach at grid top and next main index greater than current main index. Reset next main index.");
725 nextMainIndex = curMainIndex;
726 }
727 if (nextMainIndex == curMainIndex && nextCrossIndex == curCrossIndex) {
728 LOGI("Next index return: Move stoped. Next index: (%{public}d,%{public}d) is same as current.", nextMainIndex,
729 nextCrossIndex);
730 return { -1, -1 };
731 }
732 if (curChildStartIndex != 0 && curMainIndex == curMainStart && nextMainIndex < curMainIndex) {
733 // Scroll item up.
734 LOGD("Item donot reach top and next main index is less than current. Do UpdateStartIndex(%{public}d)",
735 curChildStartIndex - 1);
736 UpdateStartIndex(curChildStartIndex - 1);
737 auto pipeline = PipelineContext::GetCurrentContext();
738 if (pipeline) {
739 pipeline->FlushUITasks();
740 }
741 } else if (curChildEndIndex != childrenCount - 1 && curMainIndex == curMainEnd && nextMainIndex > curMainIndex) {
742 // Scroll item down.
743 LOGD("Item donot reach bottom and next main index is greater than current. Do UpdateStartIndex(%{public}d)",
744 curChildEndIndex + 1);
745 UpdateStartIndex(curChildEndIndex + 1);
746 auto pipeline = PipelineContext::GetCurrentContext();
747 if (pipeline) {
748 pipeline->FlushUITasks();
749 }
750 }
751 curMainStart = gridLayoutInfo_.startMainLineIndex_;
752 curMainEnd = gridLayoutInfo_.endMainLineIndex_;
753 if (nextMainIndex < curMainStart || nextMainIndex > curMainEnd) {
754 LOGW("Next index return: Error. Next main index is out of range(%{public}d,%{public}d)", curMainStart,
755 curMainEnd);
756 return { -1, -1 };
757 }
758 if (nextCrossIndex < 0) {
759 LOGW("Next index return: Error. Next cross index is less than 0.");
760 return { -1, -1 };
761 }
762 if (gridLayoutInfo_.gridMatrix_.find(nextMainIndex) == gridLayoutInfo_.gridMatrix_.end()) {
763 LOGE("Can not find next main index: %{public}d", nextMainIndex);
764 return { -1, -1 };
765 }
766 auto nextMaxCrossCount = GetCrossCount();
767 if (nextCrossIndex >= nextMaxCrossCount) {
768 LOGI("Next index: { %{public}d,%{public}d }. Next cross index is greater than max cross count: %{public}d.",
769 nextMainIndex, nextCrossIndex, nextMaxCrossCount - 1);
770 if (nextMaxCrossCount - 1 != curCrossIndex) {
771 LOGI("Current cross index: %{public}d is not the tail item. Return to the tail: { %{public}d,%{public}d }",
772 curCrossIndex, nextMainIndex, nextMaxCrossCount - 1);
773 return { nextMainIndex, nextMaxCrossCount - 1 };
774 }
775 LOGW("Current cross index: %{public}d is the tail item. No next item can be found!", curCrossIndex);
776 return { -1, -1 };
777 }
778 LOGI("Next index return: { %{public}d,%{public}d }.", nextMainIndex, nextCrossIndex);
779 return { nextMainIndex, nextCrossIndex };
780 }
781
SearchFocusableChildInCross(int32_t tarMainIndex,int32_t tarCrossIndex,int32_t maxCrossCount,int32_t curMainIndex,int32_t curCrossIndex)782 WeakPtr<FocusHub> GridPattern::SearchFocusableChildInCross(
783 int32_t tarMainIndex, int32_t tarCrossIndex, int32_t maxCrossCount, int32_t curMainIndex, int32_t curCrossIndex)
784 {
785 LOGD("Search child from index: (%{public}d,%{public}d). Current index: (%{public}d,%{public}d)", tarMainIndex,
786 tarCrossIndex, curMainIndex, curCrossIndex);
787 bool isDirectionLeft = true;
788 auto indexLeft = tarCrossIndex;
789 auto indexRight = tarCrossIndex;
790 if (curMainIndex == tarMainIndex) {
791 // Search on the same main index. Do not need search on both left and right side.
792 if (tarCrossIndex > curCrossIndex) {
793 // Only search on the right side.
794 indexLeft = -1;
795 } else if (tarCrossIndex < curCrossIndex) {
796 // Only search on the left side.
797 indexRight = maxCrossCount;
798 } else {
799 LOGE("Invalid search index: (%{public}d,%{public}d). It's same as current.", tarMainIndex, tarCrossIndex);
800 return nullptr;
801 }
802 }
803 while (indexLeft >= 0 || indexRight < maxCrossCount) {
804 int32_t curIndex = indexLeft;
805 if (indexLeft < 0) {
806 curIndex = indexRight++;
807 } else if (indexRight >= maxCrossCount) {
808 curIndex = indexLeft--;
809 } else {
810 curIndex = isDirectionLeft ? indexLeft-- : indexRight++;
811 isDirectionLeft = !isDirectionLeft;
812 }
813 auto weakChild = GetChildFocusNodeByIndex(tarMainIndex, curIndex);
814 auto child = weakChild.Upgrade();
815 if (child && child->IsFocusable()) {
816 LOGI("Found child. Index: %{public}d,%{public}d", tarMainIndex, curIndex);
817 return weakChild;
818 }
819 }
820 LOGD("Child can not be found.");
821 return nullptr;
822 }
823
GetChildFocusNodeByIndex(int32_t tarMainIndex,int32_t tarCrossIndex,int32_t tarIndex)824 WeakPtr<FocusHub> GridPattern::GetChildFocusNodeByIndex(int32_t tarMainIndex, int32_t tarCrossIndex, int32_t tarIndex)
825 {
826 LOGD("Get target item location is (%{public}d,%{public}d / %{public}d)", tarMainIndex, tarCrossIndex, tarIndex);
827 auto gridFrame = GetHost();
828 CHECK_NULL_RETURN(gridFrame, nullptr);
829 auto gridFocus = gridFrame->GetFocusHub();
830 CHECK_NULL_RETURN(gridFocus, nullptr);
831 auto childFocusList = gridFocus->GetChildren();
832 for (const auto& childFocus : childFocusList) {
833 auto childFrame = childFocus->GetFrameNode();
834 if (!childFrame) {
835 continue;
836 }
837 auto childPattern = childFrame->GetPattern();
838 if (!childPattern) {
839 continue;
840 }
841 auto childItemPattern = AceType::DynamicCast<GridItemPattern>(childPattern);
842 if (!childItemPattern) {
843 continue;
844 }
845 auto childItemProperty = childItemPattern->GetLayoutProperty<GridItemLayoutProperty>();
846 if (!childItemProperty) {
847 continue;
848 }
849 auto curMainIndex = childItemProperty->GetMainIndex().value_or(-1);
850 auto curCrossIndex = childItemProperty->GetCrossIndex().value_or(-1);
851 if (tarIndex < 0) {
852 auto curMainSpan = childItemProperty->GetMainSpan(gridLayoutInfo_.axis_);
853 auto curCrossSpan = childItemProperty->GetCrossSpan(gridLayoutInfo_.axis_);
854 if (curMainIndex <= tarMainIndex && curMainIndex + curMainSpan > tarMainIndex &&
855 curCrossIndex <= tarCrossIndex && curCrossIndex + curCrossSpan > tarCrossIndex) {
856 return AceType::WeakClaim(AceType::RawPtr(childFocus));
857 }
858 } else {
859 if (gridLayoutInfo_.gridMatrix_.find(curMainIndex) == gridLayoutInfo_.gridMatrix_.end()) {
860 LOGE("Can not find target main index: %{public}d", curMainIndex);
861 continue;
862 }
863 if (gridLayoutInfo_.gridMatrix_[curMainIndex].find(curCrossIndex) ==
864 gridLayoutInfo_.gridMatrix_[curMainIndex].end()) {
865 LOGE("Can not find target cross index: %{public}d", curCrossIndex);
866 continue;
867 }
868 if (gridLayoutInfo_.gridMatrix_[curMainIndex][curCrossIndex] == tarIndex) {
869 return AceType::WeakClaim(AceType::RawPtr(childFocus));
870 }
871 }
872 }
873 LOGD("Item at location(%{public}d,%{public}d / %{public}d) can not found.", tarMainIndex, tarCrossIndex, tarIndex);
874 return nullptr;
875 }
876
GetFocusableChildCrossIndexesAt(int32_t tarMainIndex)877 std::unordered_set<int32_t> GridPattern::GetFocusableChildCrossIndexesAt(int32_t tarMainIndex)
878 {
879 std::unordered_set<int32_t> result;
880 auto gridFrame = GetHost();
881 CHECK_NULL_RETURN(gridFrame, result);
882 auto gridFocus = gridFrame->GetFocusHub();
883 CHECK_NULL_RETURN(gridFocus, result);
884 auto childFocusList = gridFocus->GetChildren();
885 for (const auto& childFocus : childFocusList) {
886 if (!childFocus->IsFocusable()) {
887 continue;
888 }
889 auto childFrame = childFocus->GetFrameNode();
890 if (!childFrame) {
891 continue;
892 }
893 auto childPattern = childFrame->GetPattern();
894 if (!childPattern) {
895 continue;
896 }
897 auto childItemPattern = AceType::DynamicCast<GridItemPattern>(childPattern);
898 if (!childItemPattern) {
899 continue;
900 }
901 auto childItemProperty = childItemPattern->GetLayoutProperty<GridItemLayoutProperty>();
902 if (!childItemProperty) {
903 continue;
904 }
905 auto curMainIndex = childItemProperty->GetMainIndex().value_or(-1);
906 auto curCrossIndex = childItemProperty->GetCrossIndex().value_or(-1);
907 if (curMainIndex == tarMainIndex) {
908 result.emplace(curCrossIndex);
909 }
910 }
911 std::string output;
912 for (const auto& index : result) {
913 output += std::to_string(index);
914 }
915 LOGD("Focusable child cross index list at main(%{public}d) is { %{public}s }", tarMainIndex, output.c_str());
916 return result;
917 }
918
ScrollToFocusNode(const WeakPtr<FocusHub> & focusNode)919 void GridPattern::ScrollToFocusNode(const WeakPtr<FocusHub>& focusNode)
920 {
921 auto nextFocus = focusNode.Upgrade();
922 CHECK_NULL_VOID(nextFocus);
923 UpdateStartIndex(GetFocusNodeIndex(nextFocus));
924 }
925
GetFocusNodeIndex(const RefPtr<FocusHub> & focusNode)926 int32_t GridPattern::GetFocusNodeIndex(const RefPtr<FocusHub>& focusNode)
927 {
928 auto tarFrame = focusNode->GetFrameNode();
929 CHECK_NULL_RETURN(tarFrame, -1);
930 auto tarPattern = tarFrame->GetPattern();
931 CHECK_NULL_RETURN(tarPattern, -1);
932 auto tarItemPattern = AceType::DynamicCast<GridItemPattern>(tarPattern);
933 CHECK_NULL_RETURN(tarItemPattern, -1);
934 auto tarItemProperty = tarItemPattern->GetLayoutProperty<GridItemLayoutProperty>();
935 CHECK_NULL_RETURN(tarItemProperty, -1);
936 auto tarMainIndex = tarItemProperty->GetMainIndex().value_or(-1);
937 auto tarCrossIndex = tarItemProperty->GetCrossIndex().value_or(-1);
938 if (gridLayoutInfo_.gridMatrix_.find(tarMainIndex) == gridLayoutInfo_.gridMatrix_.end()) {
939 LOGE("Can not find target main index: %{public}d", tarMainIndex);
940 if (tarMainIndex == 0) {
941 return 0;
942 }
943 return gridLayoutInfo_.childrenCount_ - 1;
944 }
945 if (gridLayoutInfo_.gridMatrix_[tarMainIndex].find(tarCrossIndex) ==
946 gridLayoutInfo_.gridMatrix_[tarMainIndex].end()) {
947 LOGE("Can not find target cross index: %{public}d", tarCrossIndex);
948 if (tarMainIndex == 0) {
949 return 0;
950 }
951 return gridLayoutInfo_.childrenCount_ - 1;
952 }
953 return gridLayoutInfo_.gridMatrix_[tarMainIndex][tarCrossIndex];
954 }
955
ScrollToFocusNodeIndex(int32_t index)956 void GridPattern::ScrollToFocusNodeIndex(int32_t index)
957 {
958 UpdateStartIndex(index);
959 auto pipeline = PipelineContext::GetCurrentContext();
960 if (pipeline) {
961 pipeline->FlushUITasks();
962 }
963 auto tarFocusNodeWeak = GetChildFocusNodeByIndex(-1, -1, index);
964 auto tarFocusNode = tarFocusNodeWeak.Upgrade();
965 if (tarFocusNode) {
966 tarFocusNode->RequestFocusImmediately();
967 }
968 }
969
ScrollToNode(const RefPtr<FrameNode> & focusFrameNode)970 bool GridPattern::ScrollToNode(const RefPtr<FrameNode>& focusFrameNode)
971 {
972 CHECK_NULL_RETURN_NOLOG(focusFrameNode, false);
973 auto focusHub = focusFrameNode->GetFocusHub();
974 CHECK_NULL_RETURN(focusHub, false);
975 auto scrollToIndex = GetFocusNodeIndex(focusHub);
976 if (scrollToIndex < 0) {
977 return false;
978 }
979 auto ret = UpdateStartIndex(scrollToIndex);
980 auto pipeline = PipelineContext::GetCurrentContext();
981 if (pipeline) {
982 pipeline->FlushUITasks();
983 }
984 return ret;
985 }
986
ScrollBy(float offset)987 void GridPattern::ScrollBy(float offset)
988 {
989 StopAnimate();
990 UpdateCurrentOffset(-offset, SCROLL_FROM_JUMP);
991 auto host = GetHost();
992 CHECK_NULL_VOID_NOLOG(host);
993 host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
994 }
995
ToJsonValue(std::unique_ptr<JsonValue> & json) const996 void GridPattern::ToJsonValue(std::unique_ptr<JsonValue>& json) const
997 {
998 Pattern::ToJsonValue(json);
999 json->Put("multiSelectable", multiSelectable_ ? "true" : "false");
1000 json->Put("supportAnimation", supportAnimation_ ? "true" : "false");
1001 json->Put("friction", GetFriction());
1002 }
1003
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)1004 void GridPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
1005 {
1006 auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
1007 auto pattern = wp.Upgrade();
1008 if (pattern) {
1009 return pattern->OnKeyEvent(event);
1010 }
1011 return false;
1012 };
1013 focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
1014 }
1015
OnKeyEvent(const KeyEvent & event)1016 bool GridPattern::OnKeyEvent(const KeyEvent& event)
1017 {
1018 if (event.action != KeyAction::DOWN) {
1019 return false;
1020 }
1021 if ((event.code == KeyCode::KEY_PAGE_DOWN) || (event.code == KeyCode::KEY_PAGE_UP)) {
1022 ScrollPage(event.code == KeyCode::KEY_PAGE_UP);
1023 }
1024 return false;
1025 }
1026
HandleDirectionKey(KeyCode code)1027 bool GridPattern::HandleDirectionKey(KeyCode code)
1028 {
1029 if (code == KeyCode::KEY_DPAD_UP) {
1030 // Need to update: current selection
1031 return true;
1032 }
1033 if (code == KeyCode::KEY_DPAD_DOWN) {
1034 // Need to update: current selection
1035 return true;
1036 }
1037 return false;
1038 }
1039
SetPositionController(const RefPtr<ScrollableController> & controller)1040 void GridPattern::SetPositionController(const RefPtr<ScrollableController>& controller)
1041 {
1042 positionController_ = DynamicCast<GridPositionController>(controller);
1043 if (controller) {
1044 controller->SetScrollPattern(AceType::WeakClaim<GridPattern>(this));
1045 }
1046 }
1047
ScrollPage(bool reverse)1048 void GridPattern::ScrollPage(bool reverse)
1049 {
1050 StopAnimate();
1051 if (!isConfigScrollable_) {
1052 return;
1053 }
1054 if (!reverse) {
1055 LOGD("PgDn. Scroll offset is %{public}f", -GetMainContentSize());
1056 UpdateCurrentOffset(-GetMainContentSize(), SCROLL_FROM_JUMP);
1057 } else {
1058 LOGD("PgUp. Scroll offset is %{public}f", GetMainContentSize());
1059 UpdateCurrentOffset(GetMainContentSize(), SCROLL_FROM_JUMP);
1060 }
1061 auto host = GetHost();
1062 CHECK_NULL_VOID_NOLOG(host);
1063 host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
1064 }
1065
UpdateStartIndex(int32_t index)1066 bool GridPattern::UpdateStartIndex(int32_t index)
1067 {
1068 if (!isConfigScrollable_) {
1069 return false;
1070 }
1071 auto host = GetHost();
1072 CHECK_NULL_RETURN(host, false);
1073 gridLayoutInfo_.jumpIndex_ = index;
1074 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1075 host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
1076 SetScrollSource(SCROLL_FROM_JUMP);
1077 return true;
1078 }
1079
UpdateStartIndex(int32_t index,ScrollAlign align)1080 bool GridPattern::UpdateStartIndex(int32_t index, ScrollAlign align)
1081 {
1082 gridLayoutInfo_.scrollAlign_ = align;
1083 return UpdateStartIndex(index);
1084 }
1085
OnAnimateStop()1086 void GridPattern::OnAnimateStop()
1087 {
1088 scrollStop_ = true;
1089 MarkDirtyNodeSelf();
1090 auto host = GetHost();
1091 CHECK_NULL_VOID(host);
1092 host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
1093 }
1094
AnimateTo(float position,float duration,const RefPtr<Curve> & curve,bool smooth)1095 void GridPattern::AnimateTo(float position, float duration, const RefPtr<Curve>& curve, bool smooth)
1096 {
1097 if (!isConfigScrollable_) {
1098 return;
1099 }
1100 auto host = GetHost();
1101 CHECK_NULL_VOID(host);
1102 host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_START);
1103 ScrollablePattern::AnimateTo(position, duration, curve, smooth);
1104 FireOnScrollStart();
1105 }
1106
ScrollTo(float position)1107 void GridPattern::ScrollTo(float position)
1108 {
1109 if (!isConfigScrollable_) {
1110 return;
1111 }
1112 LOGI("ScrollTo:%{public}f", position);
1113 StopAnimate();
1114 UpdateCurrentOffset(GetTotalOffset() - position, SCROLL_FROM_JUMP);
1115 auto host = GetHost();
1116 CHECK_NULL_VOID_NOLOG(host);
1117 host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
1118 }
1119
EstimateHeight() const1120 float GridPattern::EstimateHeight() const
1121 {
1122 auto host = GetHost();
1123 CHECK_NULL_RETURN_NOLOG(host, 0.0);
1124 auto geometryNode = host->GetGeometryNode();
1125 CHECK_NULL_RETURN_NOLOG(geometryNode, 0.0);
1126 const auto& info = gridLayoutInfo_;
1127 auto viewScopeSize = geometryNode->GetPaddingSize();
1128 auto layoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
1129
1130 float heightSum = 0;
1131 int32_t itemCount = 0;
1132 float height = 0;
1133 auto mainGap = GridUtils::GetMainGap(layoutProperty, viewScopeSize, info.axis_);
1134 for (const auto& item : info.lineHeightMap_) {
1135 auto line = info.gridMatrix_.find(item.first);
1136 if (line == info.gridMatrix_.end()) {
1137 continue;
1138 }
1139 if (line->second.empty()) {
1140 continue;
1141 }
1142 auto lineStart = line->second.begin()->second;
1143 auto lineEnd = line->second.rbegin()->second;
1144 itemCount += (lineEnd - lineStart + 1);
1145 heightSum += item.second + mainGap;
1146 }
1147 if (itemCount == 0) {
1148 return 0;
1149 }
1150 auto averageHeight = heightSum / itemCount;
1151 height = info.startIndex_ * averageHeight - info.currentOffset_;
1152 if (itemCount >= (info.childrenCount_ - 1)) {
1153 height = info.GetStartLineOffset(mainGap);
1154 }
1155 return height;
1156 }
1157
GetAverageHeight() const1158 float GridPattern::GetAverageHeight() const
1159 {
1160 auto host = GetHost();
1161 CHECK_NULL_RETURN_NOLOG(host, 0.0);
1162 auto geometryNode = host->GetGeometryNode();
1163 CHECK_NULL_RETURN_NOLOG(geometryNode, 0.0);
1164 const auto& info = gridLayoutInfo_;
1165 auto viewScopeSize = geometryNode->GetPaddingSize();
1166 auto layoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
1167
1168 float heightSum = 0;
1169 int32_t itemCount = 0;
1170 auto mainGap = GridUtils::GetMainGap(layoutProperty, viewScopeSize, info.axis_);
1171 for (const auto& item : info.lineHeightMap_) {
1172 auto line = info.gridMatrix_.find(item.first);
1173 if (line == info.gridMatrix_.end()) {
1174 continue;
1175 }
1176 if (line->second.empty()) {
1177 continue;
1178 }
1179 auto lineStart = line->second.begin()->second;
1180 auto lineEnd = line->second.rbegin()->second;
1181 itemCount += (lineEnd - lineStart + 1);
1182 heightSum += item.second + mainGap;
1183 }
1184 if (itemCount == 0) {
1185 return 0;
1186 }
1187 return heightSum / itemCount;
1188 }
1189
GetTotalHeight() const1190 float GridPattern::GetTotalHeight() const
1191 {
1192 auto host = GetHost();
1193 CHECK_NULL_RETURN_NOLOG(host, 0.0f);
1194 auto geometryNode = host->GetGeometryNode();
1195 CHECK_NULL_RETURN_NOLOG(geometryNode, 0.0f);
1196 auto viewScopeSize = geometryNode->GetPaddingSize();
1197 auto layoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
1198 float heightSum = 0;
1199 int32_t itemCount = 0;
1200 float estimatedHeight = 0.f;
1201 if (scrollbarInfo_.first.has_value() && scrollbarInfo_.second.has_value()) {
1202 estimatedHeight = scrollbarInfo_.second.value();
1203 } else {
1204 auto mainGap = GridUtils::GetMainGap(layoutProperty, viewScopeSize, gridLayoutInfo_.axis_);
1205 for (const auto& item : gridLayoutInfo_.lineHeightMap_) {
1206 auto line = gridLayoutInfo_.gridMatrix_.find(item.first);
1207 if (line == gridLayoutInfo_.gridMatrix_.end()) {
1208 continue;
1209 }
1210 if (line->second.empty()) {
1211 continue;
1212 }
1213 auto lineStart = line->second.begin()->second;
1214 auto lineEnd = line->second.rbegin()->second;
1215 itemCount += (lineEnd - lineStart + 1);
1216 heightSum += item.second + mainGap;
1217 }
1218 auto averageHeight = heightSum / itemCount;
1219 if (itemCount >= (gridLayoutInfo_.childrenCount_ - 1)) {
1220 estimatedHeight = heightSum - mainGap;
1221 } else {
1222 estimatedHeight = heightSum + (gridLayoutInfo_.childrenCount_ - itemCount) * averageHeight;
1223 }
1224 }
1225 return estimatedHeight;
1226 }
1227
UpdateScrollBarOffset()1228 void GridPattern::UpdateScrollBarOffset()
1229 {
1230 if (!GetScrollBar() && !GetScrollBarProxy()) {
1231 return;
1232 }
1233 auto host = GetHost();
1234 CHECK_NULL_VOID(host);
1235 auto geometryNode = host->GetGeometryNode();
1236 CHECK_NULL_VOID(geometryNode);
1237 const auto& info = gridLayoutInfo_;
1238 auto viewScopeSize = geometryNode->GetPaddingSize();
1239 auto layoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
1240 float heightSum = 0;
1241 int32_t itemCount = 0;
1242 float offset = 0;
1243 float estimatedHeight = 0.f;
1244 if (scrollbarInfo_.first.has_value() && scrollbarInfo_.second.has_value()) {
1245 offset = scrollbarInfo_.first.value();
1246 estimatedHeight = scrollbarInfo_.second.value();
1247 } else {
1248 auto mainGap = GridUtils::GetMainGap(layoutProperty, viewScopeSize, info.axis_);
1249 for (const auto& item : info.lineHeightMap_) {
1250 auto line = info.gridMatrix_.find(item.first);
1251 if (line == info.gridMatrix_.end()) {
1252 continue;
1253 }
1254 if (line->second.empty()) {
1255 continue;
1256 }
1257 auto lineStart = line->second.begin()->second;
1258 auto lineEnd = line->second.rbegin()->second;
1259 itemCount += (lineEnd - lineStart + 1);
1260 heightSum += item.second + mainGap;
1261 }
1262 auto averageHeight = itemCount == 0 ? 0.0 : heightSum / itemCount;
1263 offset = info.startIndex_ * averageHeight - info.currentOffset_;
1264 if (itemCount >= (info.childrenCount_ - 1)) {
1265 estimatedHeight = heightSum - mainGap;
1266 offset = info.GetStartLineOffset(mainGap);
1267 } else {
1268 estimatedHeight = heightSum + (info.childrenCount_ - itemCount) * averageHeight;
1269 }
1270 }
1271 auto viewSize = geometryNode->GetFrameSize();
1272 if (info.startMainLineIndex_ != 0 && info.startIndex_ == 0) {
1273 for (int32_t lineIndex = info.startMainLineIndex_ - 1; lineIndex >= 0; lineIndex--) {
1274 offset += info.lineHeightMap_.find(lineIndex)->second;
1275 }
1276 }
1277 UpdateScrollBarRegion(offset, estimatedHeight, Size(viewSize.Width(), viewSize.Height()), Offset(0.0f, 0.0f));
1278 }
1279
CreatePaintProperty()1280 RefPtr<PaintProperty> GridPattern::CreatePaintProperty()
1281 {
1282 auto defaultDisplayMode = DisplayMode::OFF;
1283 const static int32_t PLATFORM_VERSION_TEN = 10;
1284 auto pipeline = PipelineContext::GetCurrentContext();
1285 CHECK_NULL_RETURN(pipeline, nullptr);
1286 if (pipeline->GetMinPlatformVersion() >= PLATFORM_VERSION_TEN) {
1287 defaultDisplayMode = DisplayMode::AUTO;
1288 }
1289 auto property = MakeRefPtr<ScrollablePaintProperty>();
1290 // default "scrollBar" attribute of Grid is BarState.Off
1291 property->UpdateScrollBarMode(defaultDisplayMode);
1292 return property;
1293 }
1294
GetOriginalIndex() const1295 int32_t GridPattern::GetOriginalIndex() const
1296 {
1297 return gridLayoutInfo_.GetOriginalIndex();
1298 }
1299
GetCrossCount() const1300 int32_t GridPattern::GetCrossCount() const
1301 {
1302 return gridLayoutInfo_.crossCount_;
1303 }
1304
GetChildrenCount() const1305 int32_t GridPattern::GetChildrenCount() const
1306 {
1307 return gridLayoutInfo_.childrenCount_;
1308 }
1309
ClearDragState()1310 void GridPattern::ClearDragState()
1311 {
1312 gridLayoutInfo_.ClearDragState();
1313 MarkDirtyNodeSelf();
1314 }
1315
UpdateRectOfDraggedInItem(int32_t insertIndex)1316 void GridPattern::UpdateRectOfDraggedInItem(int32_t insertIndex)
1317 {
1318 auto host = GetHost();
1319 CHECK_NULL_VOID(host);
1320 std::list<RefPtr<FrameNode>> children;
1321 host->GenerateOneDepthAllFrame(children);
1322 for (const auto& item : children) {
1323 auto itemPattern = item->GetPattern<GridItemPattern>();
1324 CHECK_NULL_VOID(itemPattern);
1325 auto itemProperty = itemPattern->GetLayoutProperty<GridItemLayoutProperty>();
1326 CHECK_NULL_VOID(itemProperty);
1327 auto mainIndex = itemProperty->GetMainIndex().value_or(-1);
1328 auto crossIndex = itemProperty->GetCrossIndex().value_or(-1);
1329 if (mainIndex * gridLayoutInfo_.crossCount_ + crossIndex == insertIndex) {
1330 auto size = item->GetRenderContext()->GetPaintRectWithTransform();
1331 size.SetOffset(item->GetTransformRelativeOffset());
1332 gridLayoutInfo_.currentRect_ = size;
1333 break;
1334 }
1335 }
1336 }
1337
MoveItems(int32_t itemIndex,int32_t insertIndex)1338 void GridPattern::MoveItems(int32_t itemIndex, int32_t insertIndex)
1339 {
1340 if (insertIndex < 0 ||
1341 insertIndex >= ((itemIndex == -1) ? (gridLayoutInfo_.childrenCount_ + 1) : gridLayoutInfo_.childrenCount_)) {
1342 return;
1343 }
1344
1345 if (itemIndex == -1) {
1346 UpdateRectOfDraggedInItem(insertIndex);
1347 }
1348
1349 gridLayoutInfo_.SwapItems(itemIndex, insertIndex);
1350
1351 auto host = GetHost();
1352 CHECK_NULL_VOID(host);
1353 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1354 auto pipeline = PipelineContext::GetCurrentContext();
1355 if (pipeline) {
1356 pipeline->FlushUITasks();
1357 }
1358 }
1359
IsOutOfBoundary()1360 bool GridPattern::IsOutOfBoundary()
1361 {
1362 bool outOfStart = gridLayoutInfo_.reachStart_ && Positive(gridLayoutInfo_.currentOffset_);
1363 float endPos = gridLayoutInfo_.currentOffset_ + gridLayoutInfo_.totalHeightOfItemsInView_;
1364 bool outOfEnd = (gridLayoutInfo_.endIndex_ == gridLayoutInfo_.childrenCount_ - 1) &&
1365 LessNotEqual(endPos, gridLayoutInfo_.lastMainSize_);
1366 bool scrollable = (gridLayoutInfo_.startIndex_ > 0) ||
1367 (gridLayoutInfo_.endIndex_ < gridLayoutInfo_.childrenCount_ - 1) ||
1368 GreatNotEqual(gridLayoutInfo_.totalHeightOfItemsInView_, gridLayoutInfo_.lastMainSize_);
1369 return (outOfStart || outOfEnd) && scrollable;
1370 }
1371
SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect> & scrollEffect)1372 void GridPattern::SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect>& scrollEffect)
1373 {
1374 scrollEffect->SetCurrentPositionCallback([weak = AceType::WeakClaim(this)]() -> double {
1375 auto grid = weak.Upgrade();
1376 CHECK_NULL_RETURN_NOLOG(grid, 0.0);
1377 return grid->gridLayoutInfo_.currentOffset_;
1378 });
1379 scrollEffect->SetLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
1380 auto grid = weak.Upgrade();
1381 CHECK_NULL_RETURN_NOLOG(grid, 0.0);
1382 return grid->GetMainContentSize() - grid->gridLayoutInfo_.GetTotalHeightOfItemsInView(grid->GetMainGap());
1383 });
1384 scrollEffect->SetTrailingCallback([]() -> double { return 0.0; });
1385 scrollEffect->SetInitLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
1386 auto grid = weak.Upgrade();
1387 CHECK_NULL_RETURN_NOLOG(grid, 0.0);
1388 return grid->GetMainContentSize() - grid->gridLayoutInfo_.GetTotalHeightOfItemsInView(grid->GetMainGap());
1389 });
1390 scrollEffect->SetInitTrailingCallback([]() -> double { return 0.0; });
1391 }
1392
OutBoundaryCallback()1393 bool GridPattern::OutBoundaryCallback()
1394 {
1395 return IsOutOfBoundary();
1396 }
1397
GetOverScrollOffset(double delta) const1398 OverScrollOffset GridPattern::GetOverScrollOffset(double delta) const
1399 {
1400 OverScrollOffset offset = { 0, 0 };
1401 if (gridLayoutInfo_.startIndex_ == 0) {
1402 auto startPos = gridLayoutInfo_.currentOffset_;
1403 auto newStartPos = startPos + delta;
1404 if (startPos > 0 && newStartPos > 0) {
1405 offset.start = delta;
1406 }
1407 if (startPos > 0 && newStartPos <= 0) {
1408 offset.start = -startPos;
1409 }
1410 if (startPos <= 0 && newStartPos > 0) {
1411 offset.start = newStartPos;
1412 }
1413 }
1414 if (gridLayoutInfo_.endIndex_ == gridLayoutInfo_.childrenCount_ - 1) {
1415 auto endPos = gridLayoutInfo_.currentOffset_ + gridLayoutInfo_.totalHeightOfItemsInView_;
1416 auto newEndPos = endPos + delta;
1417 if (endPos < gridLayoutInfo_.lastMainSize_ && newEndPos < gridLayoutInfo_.lastMainSize_) {
1418 offset.end = delta;
1419 }
1420 if (endPos < gridLayoutInfo_.lastMainSize_ && newEndPos >= gridLayoutInfo_.lastMainSize_) {
1421 offset.end = gridLayoutInfo_.lastMainSize_ - endPos;
1422 }
1423 if (endPos >= gridLayoutInfo_.lastMainSize_ && newEndPos < gridLayoutInfo_.lastMainSize_) {
1424 offset.end = newEndPos - gridLayoutInfo_.lastMainSize_;
1425 }
1426 }
1427 return offset;
1428 }
1429
SetAccessibilityAction()1430 void GridPattern::SetAccessibilityAction()
1431 {
1432 auto host = GetHost();
1433 CHECK_NULL_VOID(host);
1434 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1435 CHECK_NULL_VOID(accessibilityProperty);
1436 accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
1437 const auto& pattern = weakPtr.Upgrade();
1438 CHECK_NULL_VOID(pattern);
1439 if (!pattern->IsScrollable()) {
1440 return;
1441 }
1442 pattern->ScrollPage(false);
1443 });
1444
1445 accessibilityProperty->SetActionScrollBackward([weakPtr = WeakClaim(this)]() {
1446 const auto& pattern = weakPtr.Upgrade();
1447 CHECK_NULL_VOID(pattern);
1448 if (!pattern->IsScrollable()) {
1449 return;
1450 }
1451 pattern->ScrollPage(true);
1452 });
1453 }
1454
DumpInfo()1455 void GridPattern::DumpInfo()
1456 {
1457 LOGI("reachStart:%{public}d,reachEnd:%{public}d,offsetEnd:%{public}d", gridLayoutInfo_.reachStart_,
1458 gridLayoutInfo_.reachEnd_, gridLayoutInfo_.offsetEnd_);
1459 auto property = GetLayoutProperty<GridLayoutProperty>();
1460 CHECK_NULL_VOID_NOLOG(property);
1461 LOGI("startIndex:%{public}d,endIndex:%{public}d,startMainLine:%{public}d,endMainLine:%{public}d,cachedCount:%{"
1462 "public}d",
1463 gridLayoutInfo_.startIndex_, gridLayoutInfo_.endIndex_, gridLayoutInfo_.startMainLineIndex_,
1464 gridLayoutInfo_.endMainLineIndex_, property->GetCachedCountValue(1));
1465 }
1466 } // namespace OHOS::Ace::NG
1467