• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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