• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/grid/grid_pattern.h"
17 
18 #include "base/geometry/axis.h"
19 #include "base/log/dump_log.h"
20 #include "base/perfmonitor/perf_constants.h"
21 #include "base/perfmonitor/perf_monitor.h"
22 #include "base/utils/utils.h"
23 #include "core/common/container.h"
24 #include "core/components/scroll/scroll_controller_base.h"
25 #include "core/components_ng/base/inspector_filter.h"
26 #include "core/components_ng/base/observer_handler.h"
27 #include "core/components_ng/pattern/grid/grid_adaptive/grid_adaptive_layout_algorithm.h"
28 #include "core/components_ng/pattern/grid/grid_item_layout_property.h"
29 #include "core/components_ng/pattern/grid/grid_item_pattern.h"
30 #include "core/components_ng/pattern/grid/grid_layout/grid_layout_algorithm.h"
31 #include "core/components_ng/pattern/grid/grid_layout_property.h"
32 #include "core/components_ng/pattern/grid/grid_scroll/grid_scroll_layout_algorithm.h"
33 #include "core/components_ng/pattern/grid/grid_scroll/grid_scroll_with_options_layout_algorithm.h"
34 #include "core/components_ng/pattern/grid/grid_utils.h"
35 #include "core/components_ng/pattern/grid/irregular/grid_irregular_layout_algorithm.h"
36 #include "core/components_ng/pattern/grid/irregular/grid_layout_utils.h"
37 #include "core/components_ng/pattern/scroll_bar/proxy/scroll_bar_proxy.h"
38 #include "core/pipeline_ng/pipeline_context.h"
39 
40 namespace OHOS::Ace::NG {
41 
42 namespace {
43 const Color ITEM_FILL_COLOR = Color::TRANSPARENT;
44 
45 const int32_t MAX_NUM_SIZE = 4;
46 
CalcCoordinatesDistance(double curFocusMain,double curFocusCross,double childMain,double childCross)47 double CalcCoordinatesDistance(double curFocusMain, double curFocusCross, double childMain, double childCross)
48 {
49     return std::sqrt(std::pow((curFocusMain - childMain), 2) + std::pow((curFocusCross - childCross), 2));
50 }
51 } // namespace
52 
CreateLayoutAlgorithm()53 RefPtr<LayoutAlgorithm> GridPattern::CreateLayoutAlgorithm()
54 {
55     auto gridLayoutProperty = GetLayoutProperty<GridLayoutProperty>();
56     CHECK_NULL_RETURN(gridLayoutProperty, nullptr);
57     std::vector<std::string> cols;
58     StringUtils::StringSplitter(gridLayoutProperty->GetColumnsTemplate().value_or(""), ' ', cols);
59     std::vector<std::string> rows;
60     StringUtils::StringSplitter(gridLayoutProperty->GetRowsTemplate().value_or(""), ' ', rows);
61     auto crossCount = cols.empty() ? Infinity<int32_t>() : static_cast<int32_t>(cols.size());
62     auto mainCount = rows.empty() ? Infinity<int32_t>() : static_cast<int32_t>(rows.size());
63     if (!gridLayoutProperty->IsVertical()) {
64         std::swap(crossCount, mainCount);
65     }
66     gridLayoutInfo_.crossCount_ = crossCount;
67     if (targetIndex_.has_value()) {
68         gridLayoutInfo_.targetIndex_ = targetIndex_;
69     }
70     // When rowsTemplate and columnsTemplate is both setting, use static layout algorithm.
71     if (!rows.empty() && !cols.empty()) {
72         return MakeRefPtr<GridLayoutAlgorithm>(gridLayoutInfo_, crossCount, mainCount);
73     }
74 
75     // When rowsTemplate and columnsTemplate is both not setting, use adaptive layout algorithm.
76     if (rows.empty() && cols.empty()) {
77         return MakeRefPtr<GridAdaptiveLayoutAlgorithm>(gridLayoutInfo_);
78     }
79 
80     // If only set one of rowTemplate and columnsTemplate, use scrollable layout algorithm.
81     const bool disableSkip = IsOutOfBoundary(true) || ScrollablePattern::AnimateRunning();
82     const bool overScroll = CanOverScroll(GetScrollSource()) || forceOverScroll_;
83     if (UseIrregularLayout()) {
84         auto algo = MakeRefPtr<GridIrregularLayoutAlgorithm>(gridLayoutInfo_, overScroll);
85         algo->SetEnableSkip(!disableSkip);
86         return algo;
87     }
88     RefPtr<GridScrollLayoutAlgorithm> result;
89     if (!gridLayoutProperty->GetLayoutOptions().has_value()) {
90         result = MakeRefPtr<GridScrollLayoutAlgorithm>(gridLayoutInfo_, crossCount, mainCount);
91     } else {
92         result = MakeRefPtr<GridScrollWithOptionsLayoutAlgorithm>(gridLayoutInfo_, crossCount, mainCount);
93     }
94     result->SetCanOverScroll(overScroll);
95     result->SetScrollSource(GetScrollSource());
96     if (ScrollablePattern::AnimateRunning()) {
97         result->SetLineSkipping(!disableSkip);
98     }
99     return result;
100 }
101 
BeforeCreateLayoutWrapper()102 void GridPattern::BeforeCreateLayoutWrapper()
103 {
104     auto host = GetHost();
105     CHECK_NULL_VOID(host);
106     gridLayoutInfo_.childrenCount_ = host->GetTotalChildCount();
107 }
108 
CreateNodePaintMethod()109 RefPtr<NodePaintMethod> GridPattern::CreateNodePaintMethod()
110 {
111     auto paint = MakeRefPtr<GridPaintMethod>(GetScrollBar());
112     CHECK_NULL_RETURN(paint, nullptr);
113     CreateScrollBarOverlayModifier();
114     paint->SetScrollBarOverlayModifier(GetScrollBarOverlayModifier());
115     auto scrollEffect = GetScrollEdgeEffect();
116     if (scrollEffect && scrollEffect->IsFadeEffect()) {
117         paint->SetEdgeEffect(scrollEffect);
118     }
119     return paint;
120 }
121 
OnModifyDone()122 void GridPattern::OnModifyDone()
123 {
124     Pattern::OnModifyDone();
125     auto gridLayoutProperty = GetLayoutProperty<GridLayoutProperty>();
126     CHECK_NULL_VOID(gridLayoutProperty);
127 
128     if (multiSelectable_ && !isMouseEventInit_) {
129         InitMouseEvent();
130     }
131 
132     if (!multiSelectable_ && isMouseEventInit_) {
133         UninitMouseEvent();
134     }
135 
136     gridLayoutInfo_.axis_ = gridLayoutProperty->IsVertical() ? Axis::VERTICAL : Axis::HORIZONTAL;
137     isConfigScrollable_ = gridLayoutProperty->IsConfiguredScrollable();
138     if (!isConfigScrollable_) {
139         return;
140     }
141     SetAxis(gridLayoutInfo_.axis_);
142     if (!GetScrollableEvent()) {
143         AddScrollEvent();
144     }
145 
146     SetEdgeEffect();
147 
148     auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
149     CHECK_NULL_VOID(paintProperty);
150     if (paintProperty->GetScrollBarProperty()) {
151         SetScrollBar(paintProperty->GetScrollBarProperty());
152     }
153 
154     auto host = GetHost();
155     CHECK_NULL_VOID(host);
156     auto focusHub = host->GetFocusHub();
157     if (focusHub) {
158         InitOnKeyEvent(focusHub);
159     }
160     SetAccessibilityAction();
161     Register2DragDropManager();
162     if (IsNeedInitClickEventRecorder()) {
163         Pattern::InitClickEventRecorder();
164     }
165 }
166 
MultiSelectWithoutKeyboard(const RectF & selectedZone)167 void GridPattern::MultiSelectWithoutKeyboard(const RectF& selectedZone)
168 {
169     auto host = GetHost();
170     CHECK_NULL_VOID(host);
171     std::list<RefPtr<FrameNode>> children;
172     host->GenerateOneDepthVisibleFrame(children);
173     for (const auto& itemFrameNode : children) {
174         auto itemEvent = itemFrameNode->GetEventHub<EventHub>();
175         CHECK_NULL_VOID(itemEvent);
176         if (!itemEvent->IsEnabled()) {
177             continue;
178         }
179 
180         auto itemPattern = itemFrameNode->GetPattern<GridItemPattern>();
181         CHECK_NULL_VOID(itemPattern);
182         if (!itemPattern->Selectable()) {
183             continue;
184         }
185         auto itemGeometry = itemFrameNode->GetGeometryNode();
186         CHECK_NULL_VOID(itemGeometry);
187         auto context = itemFrameNode->GetRenderContext();
188         CHECK_NULL_VOID(context);
189 
190         auto itemRect = itemGeometry->GetFrameRect();
191         auto iter = itemToBeSelected_.find(itemFrameNode->GetId());
192         if (iter == itemToBeSelected_.end()) {
193             auto result = itemToBeSelected_.emplace(itemFrameNode->GetId(), ItemSelectedStatus());
194             iter = result.first;
195             iter->second.onSelected = itemPattern->GetEventHub<GridItemEventHub>()->GetOnSelect();
196             iter->second.selectChangeEvent = itemPattern->GetEventHub<GridItemEventHub>()->GetSelectChangeEvent();
197         }
198         auto startMainOffset = mouseStartOffset_.GetMainOffset(gridLayoutInfo_.axis_);
199         if (gridLayoutInfo_.axis_ == Axis::VERTICAL) {
200             iter->second.rect = itemRect + OffsetF(0, totalOffsetOfMousePressed_ - startMainOffset);
201         } else {
202             iter->second.rect = itemRect + OffsetF(totalOffsetOfMousePressed_ - startMainOffset, 0);
203         }
204 
205         if (!selectedZone.IsIntersectWith(itemRect)) {
206             itemPattern->MarkIsSelected(false);
207             iter->second.selected = false;
208             context->OnMouseSelectUpdate(false, ITEM_FILL_COLOR, ITEM_FILL_COLOR);
209         } else {
210             itemPattern->MarkIsSelected(true);
211             iter->second.selected = true;
212             context->OnMouseSelectUpdate(true, ITEM_FILL_COLOR, ITEM_FILL_COLOR);
213         }
214     }
215 
216     DrawSelectedZone(selectedZone);
217 }
218 
ClearMultiSelect()219 void GridPattern::ClearMultiSelect()
220 {
221     auto host = GetHost();
222     CHECK_NULL_VOID(host);
223     std::list<RefPtr<FrameNode>> children;
224     host->GenerateOneDepthAllFrame(children);
225     for (const auto& item : children) {
226         if (!AceType::InstanceOf<FrameNode>(item)) {
227             continue;
228         }
229 
230         auto itemFrameNode = AceType::DynamicCast<FrameNode>(item);
231         auto itemPattern = itemFrameNode->GetPattern<GridItemPattern>();
232         CHECK_NULL_VOID(itemPattern);
233         auto selectedStatus = itemToBeSelected_.find(itemFrameNode->GetId());
234         if (selectedStatus != itemToBeSelected_.end()) {
235             selectedStatus->second.selected = false;
236         }
237         itemPattern->MarkIsSelected(false);
238         auto renderContext = itemFrameNode->GetRenderContext();
239         CHECK_NULL_VOID(renderContext);
240         renderContext->OnMouseSelectUpdate(false, ITEM_FILL_COLOR, ITEM_FILL_COLOR);
241     }
242 
243     ClearSelectedZone();
244 }
245 
IsItemSelected(const GestureEvent & info)246 bool GridPattern::IsItemSelected(const GestureEvent& info)
247 {
248     auto host = GetHost();
249     CHECK_NULL_RETURN(host, false);
250     auto node = host->FindChildByPosition(info.GetGlobalLocation().GetX(), info.GetGlobalLocation().GetY());
251     CHECK_NULL_RETURN(node, false);
252     auto itemPattern = node->GetPattern<GridItemPattern>();
253     CHECK_NULL_RETURN(itemPattern, false);
254     return itemPattern->IsSelected();
255 }
256 
FireOnScrollStart()257 void GridPattern::FireOnScrollStart()
258 {
259     UIObserverHandler::GetInstance().NotifyScrollEventStateChange(
260         AceType::WeakClaim(this), ScrollEventType::SCROLL_START);
261     SuggestOpIncGroup(true);
262     PerfMonitor::GetPerfMonitor()->Start(PerfConstants::APP_LIST_FLING, PerfActionType::FIRST_MOVE, "");
263     if (GetScrollAbort()) {
264         return;
265     }
266     if (scrollStop_) {
267         // onScrollStart triggers immediately on gesture dragStart, but onScrollStop marks scrollStop_ to true on
268         // gesture dragEnd, and consumes it/fires onScrollStop after layout. When the user quickly swipes twice, the
269         // second onScrollStart can trigger before the first onScrollEnd. In this case, we let the two events annihilate
270         // each other and fire neither.
271         scrollStop_ = false;
272         return;
273     }
274     auto scrollBar = GetScrollBar();
275     if (scrollBar) {
276         scrollBar->PlayScrollBarAppearAnimation();
277     }
278     StopScrollBarAnimatorByProxy();
279     FireObserverOnScrollStart();
280     auto host = GetHost();
281     CHECK_NULL_VOID(host);
282     auto hub = host->GetEventHub<GridEventHub>();
283     CHECK_NULL_VOID(hub);
284     auto onScrollStart = hub->GetOnScrollStart();
285     CHECK_NULL_VOID(onScrollStart);
286     onScrollStart();
287 }
288 
FireOnReachStart(const OnReachEvent & onReachStart)289 void GridPattern::FireOnReachStart(const OnReachEvent& onReachStart)
290 {
291     auto host = GetHost();
292     CHECK_NULL_VOID(host);
293     if (gridLayoutInfo_.startIndex_ == 0) {
294         if (!isInitialized_) {
295             FireObserverOnReachStart();
296             if (onReachStart) {
297                 onReachStart();
298                 AddEventsFiredInfo(ScrollableEventType::ON_REACH_START);
299             }
300         }
301         auto finalOffset = gridLayoutInfo_.currentHeight_ - gridLayoutInfo_.prevHeight_;
302         if (!NearZero(finalOffset)) {
303             bool scrollUpToStart =
304                 GreatOrEqual(gridLayoutInfo_.prevHeight_, 0.0) && LessOrEqual(gridLayoutInfo_.currentHeight_, 0.0);
305             bool scrollDownToStart =
306                 LessNotEqual(gridLayoutInfo_.prevHeight_, 0.0) && GreatOrEqual(gridLayoutInfo_.currentHeight_, 0.0);
307             if (scrollUpToStart || scrollDownToStart) {
308                 FireObserverOnReachStart();
309                 CHECK_NULL_VOID(onReachStart);
310                 ACE_SCOPED_TRACE("OnReachStart, scrollUpToStart:%u, scrollDownToStart:%u, id:%d, tag:Grid",
311                     scrollUpToStart, scrollDownToStart, static_cast<int32_t>(host->GetAccessibilityId()));
312                 onReachStart();
313                 AddEventsFiredInfo(ScrollableEventType::ON_REACH_START);
314             }
315         }
316     }
317 }
318 
FireOnReachEnd(const OnReachEvent & onReachEnd)319 void GridPattern::FireOnReachEnd(const OnReachEvent& onReachEnd)
320 {
321     auto host = GetHost();
322     CHECK_NULL_VOID(host);
323     if (gridLayoutInfo_.endIndex_ == (gridLayoutInfo_.childrenCount_ - 1)) {
324         if (!isInitialized_) {
325             FireObserverOnReachEnd();
326         }
327         auto finalOffset = gridLayoutInfo_.currentHeight_ - gridLayoutInfo_.prevHeight_;
328         if (!NearZero(finalOffset)) {
329             bool scrollDownToEnd = LessNotEqual(gridLayoutInfo_.prevHeight_, endHeight_) &&
330                                    GreatOrEqual(gridLayoutInfo_.currentHeight_, endHeight_);
331             bool scrollUpToEnd = GreatNotEqual(gridLayoutInfo_.prevHeight_, endHeight_) &&
332                                  LessOrEqual(gridLayoutInfo_.currentHeight_, endHeight_);
333             if (scrollDownToEnd || scrollUpToEnd) {
334                 FireObserverOnReachEnd();
335                 CHECK_NULL_VOID(onReachEnd);
336                 ACE_SCOPED_TRACE("OnReachEnd, scrollUpToEnd:%u, scrollDownToEnd:%u, id:%d, tag:Grid", scrollUpToEnd,
337                     scrollDownToEnd, static_cast<int32_t>(host->GetAccessibilityId()));
338                 onReachEnd();
339                 AddEventsFiredInfo(ScrollableEventType::ON_REACH_END);
340             }
341         }
342     }
343 }
344 
FireOnScrollIndex(bool indexChanged,const ScrollIndexFunc & onScrollIndex)345 void GridPattern::FireOnScrollIndex(bool indexChanged, const ScrollIndexFunc& onScrollIndex)
346 {
347     CHECK_NULL_VOID(indexChanged && onScrollIndex);
348     onScrollIndex(gridLayoutInfo_.startIndex_, gridLayoutInfo_.endIndex_);
349 }
350 
GetContentSize() const351 SizeF GridPattern::GetContentSize() const
352 {
353     auto host = GetHost();
354     CHECK_NULL_RETURN(host, SizeF());
355     auto geometryNode = host->GetGeometryNode();
356     CHECK_NULL_RETURN(geometryNode, SizeF());
357     return geometryNode->GetPaddingSize();
358 }
359 
GetMainGap() const360 float GridPattern::GetMainGap() const
361 {
362     float mainGap = 0.0;
363     auto host = GetHost();
364     CHECK_NULL_RETURN(host, 0.0);
365     auto geometryNode = host->GetGeometryNode();
366     CHECK_NULL_RETURN(geometryNode, 0.0);
367     auto viewScopeSize = geometryNode->GetPaddingSize();
368     auto layoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
369     mainGap = GridUtils::GetMainGap(layoutProperty, viewScopeSize, gridLayoutInfo_.axis_);
370     return mainGap;
371 }
372 
UpdateCurrentOffset(float offset,int32_t source)373 bool GridPattern::UpdateCurrentOffset(float offset, int32_t source)
374 {
375     if (!isConfigScrollable_ || !scrollable_) {
376         return true;
377     }
378 
379     auto host = GetHost();
380     CHECK_NULL_RETURN(host, false);
381 
382     // check edgeEffect is not springEffect
383     if (!HandleEdgeEffect(offset, source, GetContentSize())) {
384         if (IsOutOfBoundary(true)) {
385             host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
386         }
387         return false;
388     }
389     SetScrollSource(source);
390     FireAndCleanScrollingListener();
391     if (gridLayoutInfo_.synced_) {
392         gridLayoutInfo_.prevOffset_ = gridLayoutInfo_.currentOffset_;
393         gridLayoutInfo_.synced_ = false;
394     }
395     // When finger moves down, offset is positive.
396     // When finger moves up, offset is negative.
397     float mainGap = GetMainGap();
398     bool regular = !UseIrregularLayout();
399     auto itemsHeight = gridLayoutInfo_.GetTotalHeightOfItemsInView(mainGap, regular);
400     if (gridLayoutInfo_.offsetEnd_) {
401         if (source == SCROLL_FROM_UPDATE) {
402             float overScroll = 0.0f;
403             if (!regular) {
404                 overScroll = gridLayoutInfo_.GetDistanceToBottom(GetMainContentSize(), itemsHeight, mainGap);
405             } else {
406                 overScroll = gridLayoutInfo_.currentOffset_ - (GetMainContentSize() - itemsHeight);
407             }
408             auto friction = ScrollablePattern::CalculateFriction(std::abs(overScroll) / GetMainContentSize());
409             offset *= friction;
410         }
411         auto userOffset = FireOnWillScroll(-offset);
412         gridLayoutInfo_.currentOffset_ -= userOffset;
413 
414         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
415 
416         if (GreatNotEqual(gridLayoutInfo_.currentOffset_, GetMainContentSize() - itemsHeight)) {
417             gridLayoutInfo_.offsetEnd_ = false;
418             gridLayoutInfo_.reachEnd_ = false;
419         }
420         return true;
421     }
422     if (gridLayoutInfo_.reachStart_) {
423         if (source == SCROLL_FROM_UPDATE) {
424             auto friction =
425                 ScrollablePattern::CalculateFriction(std::abs(gridLayoutInfo_.currentOffset_) / GetMainContentSize());
426             offset *= friction;
427         }
428         auto userOffset = FireOnWillScroll(-offset);
429         gridLayoutInfo_.currentOffset_ -= userOffset;
430 
431         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
432 
433         if (LessNotEqual(gridLayoutInfo_.currentOffset_, 0.0)) {
434             gridLayoutInfo_.reachStart_ = false;
435         }
436         return true;
437     }
438     auto userOffset = FireOnWillScroll(-offset);
439     gridLayoutInfo_.currentOffset_ -= userOffset;
440     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
441     return true;
442 }
443 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)444 bool GridPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
445 {
446     if (config.skipMeasure && config.skipLayout) {
447         return false;
448     }
449     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
450     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
451     auto gridLayoutAlgorithm = DynamicCast<GridLayoutBaseAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
452     CHECK_NULL_RETURN(gridLayoutAlgorithm, false);
453     const auto& gridLayoutInfo = gridLayoutAlgorithm->GetGridLayoutInfo();
454     auto eventhub = GetEventHub<GridEventHub>();
455     CHECK_NULL_RETURN(eventhub, false);
456     Dimension offset(0, DimensionUnit::VP);
457     Dimension offsetPx(gridLayoutInfo.currentOffset_, DimensionUnit::PX);
458     auto offsetVpValue = offsetPx.ConvertToVp();
459     offset.SetValue(offsetVpValue);
460     scrollbarInfo_ = eventhub->FireOnScrollBarUpdate(gridLayoutInfo.startIndex_, offset);
461     if (!isInitialized_ || gridLayoutInfo_.startIndex_ != gridLayoutInfo.startIndex_) {
462         eventhub->FireOnScrollToIndex(gridLayoutInfo.startIndex_);
463     }
464 
465     bool indexChanged = (gridLayoutInfo.startIndex_ != gridLayoutInfo_.startIndex_) ||
466                         (gridLayoutInfo.endIndex_ != gridLayoutInfo_.endIndex_);
467     bool offsetEnd = gridLayoutInfo_.offsetEnd_;
468     gridLayoutInfo_ = gridLayoutInfo;
469     gridLayoutInfo_.synced_ = true;
470     AnimateToTarget(scrollAlign_, layoutAlgorithmWrapper);
471 
472     gridLayoutInfo_.reachStart_ =
473         gridLayoutInfo_.startIndex_ == 0 && GreatOrEqual(gridLayoutInfo_.currentOffset_, 0.0f);
474 
475     gridLayoutInfo_.currentHeight_ = EstimateHeight();
476     if (!offsetEnd && gridLayoutInfo_.offsetEnd_) {
477         endHeight_ = gridLayoutInfo_.currentHeight_;
478     }
479     ProcessEvent(indexChanged, gridLayoutInfo_.currentHeight_ - gridLayoutInfo_.prevHeight_);
480     gridLayoutInfo_.prevHeight_ = gridLayoutInfo_.currentHeight_;
481     gridLayoutInfo_.extraOffset_.reset();
482     SetScrollSource(SCROLL_FROM_NONE);
483     UpdateScrollBarOffset();
484     if (config.frameSizeChange) {
485         if (GetScrollBar() != nullptr) {
486             GetScrollBar()->ScheduleDisappearDelayTask();
487         }
488     }
489     CheckRestartSpring(false);
490     CheckScrollable();
491     MarkSelectedItems();
492     isInitialized_ = true;
493     return false;
494 }
495 
CheckScrollable()496 void GridPattern::CheckScrollable()
497 {
498     auto host = GetHost();
499     CHECK_NULL_VOID(host);
500     auto gridLayoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
501     CHECK_NULL_VOID(gridLayoutProperty);
502     if (((gridLayoutInfo_.endIndex_ - gridLayoutInfo_.startIndex_ + 1) < gridLayoutInfo_.childrenCount_) ||
503         (gridLayoutInfo_.GetTotalHeightOfItemsInView(GetMainGap(), !UseIrregularLayout()) > GetMainContentSize())) {
504         scrollable_ = true;
505     } else {
506         if (gridLayoutInfo_.startMainLineIndex_ != 0 || GetAlwaysEnabled()) {
507             scrollable_ = true;
508         } else {
509             scrollable_ = false;
510         }
511     }
512 
513     SetScrollEnabled(scrollable_);
514 
515     if (!gridLayoutProperty->GetScrollEnabled().value_or(scrollable_)) {
516         SetScrollEnabled(false);
517     }
518 }
519 
ProcessEvent(bool indexChanged,float finalOffset)520 void GridPattern::ProcessEvent(bool indexChanged, float finalOffset)
521 {
522     auto host = GetHost();
523     CHECK_NULL_VOID(host);
524     auto gridEventHub = host->GetEventHub<GridEventHub>();
525     CHECK_NULL_VOID(gridEventHub);
526 
527     auto onScroll = gridEventHub->GetOnScroll();
528     PrintOffsetLog(AceLogTag::ACE_GRID, host->GetId(), finalOffset);
529     if (onScroll) {
530         FireOnScroll(finalOffset, onScroll);
531     }
532     FireObserverOnDidScroll(finalOffset);
533     auto onDidScroll = gridEventHub->GetOnDidScroll();
534     if (onDidScroll) {
535         FireOnScroll(finalOffset, onDidScroll);
536     }
537     auto onScrollIndex = gridEventHub->GetOnScrollIndex();
538     FireOnScrollIndex(indexChanged, onScrollIndex);
539     auto onReachStart = gridEventHub->GetOnReachStart();
540     FireOnReachStart(onReachStart);
541     auto onReachEnd = gridEventHub->GetOnReachEnd();
542     FireOnReachEnd(onReachEnd);
543     OnScrollStop(gridEventHub->GetOnScrollStop());
544 }
545 
MarkDirtyNodeSelf()546 void GridPattern::MarkDirtyNodeSelf()
547 {
548     auto host = GetHost();
549     CHECK_NULL_VOID(host);
550     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
551 }
552 
OnScrollEndCallback()553 void GridPattern::OnScrollEndCallback()
554 {
555     isSmoothScrolling_ = false;
556     if (AnimateStoped()) {
557         scrollStop_ = true;
558         MarkDirtyNodeSelf();
559     }
560 }
561 
IsFirstOrLastFocusableChild(int32_t curMainIndex,int32_t curCrossIndex)562 std::pair<bool, bool> GridPattern::IsFirstOrLastFocusableChild(int32_t curMainIndex, int32_t curCrossIndex)
563 {
564     std::unordered_set<int32_t> crossIndexSet;
565     size_t maxSize = 0;
566     for (int32_t index = curMainIndex - curFocusIndexInfo_.mainSpan + 1; index <= curMainIndex; index++) {
567         auto tempIndexSet = GetFocusableChildCrossIndexesAt(index);
568         if (tempIndexSet.size() > maxSize) {
569             maxSize = tempIndexSet.size();
570             crossIndexSet = tempIndexSet;
571         }
572     }
573     auto findLesser = std::find_if(crossIndexSet.begin(), crossIndexSet.end(),
574         [curCrossIndex](int32_t crossIndex) { return curCrossIndex > crossIndex; });
575     auto findGreater = std::find_if(crossIndexSet.begin(), crossIndexSet.end(),
576         [curCrossIndex](int32_t crossIndex) { return curCrossIndex < crossIndex; });
577     return { curCrossIndex == 0 || findLesser == crossIndexSet.end(),
578         curCrossIndex == gridLayoutInfo_.crossCount_ - 1 || findGreater == crossIndexSet.end() };
579 }
580 
GetFocusSteps(int32_t curMainIndex,int32_t curCrossIndex,FocusStep step)581 std::pair<FocusStep, FocusStep> GridPattern::GetFocusSteps(int32_t curMainIndex, int32_t curCrossIndex, FocusStep step)
582 {
583     auto firstStep = FocusStep::NONE;
584     auto secondStep = FocusStep::NONE;
585     auto isFirstOrLastFocusable = IsFirstOrLastFocusableChild(curMainIndex, curCrossIndex);
586     auto isFirstFocusable = isFirstOrLastFocusable.first;
587     auto isLastFocusable = isFirstOrLastFocusable.second;
588     if (gridLayoutInfo_.axis_ == Axis::VERTICAL) {
589         if (isFirstFocusable && step == FocusStep::SHIFT_TAB) {
590             firstStep = FocusStep::UP;
591             secondStep = FocusStep::RIGHT_END;
592         } else if (isLastFocusable && step == FocusStep::TAB) {
593             firstStep = FocusStep::DOWN;
594             secondStep = FocusStep::LEFT_END;
595         }
596     } else if (gridLayoutInfo_.axis_ == Axis::HORIZONTAL) {
597         if (isFirstFocusable && step == FocusStep::SHIFT_TAB) {
598             firstStep = FocusStep::LEFT;
599             secondStep = FocusStep::DOWN_END;
600         } else if (isLastFocusable && step == FocusStep::TAB) {
601             firstStep = FocusStep::RIGHT;
602             secondStep = FocusStep::UP_END;
603         }
604     }
605     TAG_LOGI(AceLogTag::ACE_GRID, "Get focus steps. First step is %{public}d. Second step is %{public}d", firstStep,
606         secondStep);
607     return { firstStep, secondStep };
608 }
609 
GetNextFocusNode(FocusStep step,const WeakPtr<FocusHub> & currentFocusNode)610 WeakPtr<FocusHub> GridPattern::GetNextFocusNode(FocusStep step, const WeakPtr<FocusHub>& currentFocusNode)
611 {
612     auto curFocus = currentFocusNode.Upgrade();
613     CHECK_NULL_RETURN(curFocus, nullptr);
614     auto curFrame = curFocus->GetFrameNode();
615     CHECK_NULL_RETURN(curFrame, nullptr);
616     auto curPattern = curFrame->GetPattern();
617     CHECK_NULL_RETURN(curPattern, nullptr);
618     auto curItemPattern = AceType::DynamicCast<GridItemPattern>(curPattern);
619     CHECK_NULL_RETURN(curItemPattern, nullptr);
620     auto curItemProperty = curItemPattern->GetLayoutProperty<GridItemLayoutProperty>();
621     CHECK_NULL_RETURN(curItemProperty, nullptr);
622     auto irregularInfo = curItemPattern->GetIrregularItemInfo();
623     bool hasIrregularItemInfo = irregularInfo.has_value();
624 
625     auto curMainIndex = curItemProperty->GetMainIndex().value_or(-1);
626     auto curCrossIndex = curItemProperty->GetCrossIndex().value_or(-1);
627     auto curMainSpan =
628         hasIrregularItemInfo ? irregularInfo.value().mainSpan : curItemProperty->GetMainSpan(gridLayoutInfo_.axis_);
629     auto curCrossSpan =
630         hasIrregularItemInfo ? irregularInfo.value().crossSpan : curItemProperty->GetCrossSpan(gridLayoutInfo_.axis_);
631     auto curMainStart =
632         hasIrregularItemInfo ? irregularInfo.value().mainStart : curItemProperty->GetMainStart(gridLayoutInfo_.axis_);
633     auto curCrossStart =
634         hasIrregularItemInfo ? irregularInfo.value().crossStart : curItemProperty->GetCrossStart(gridLayoutInfo_.axis_);
635     auto curMainEnd =
636         hasIrregularItemInfo ? irregularInfo.value().mainEnd : curItemProperty->GetMainEnd(gridLayoutInfo_.axis_);
637     auto curCrossEnd =
638         hasIrregularItemInfo ? irregularInfo.value().crossEnd : curItemProperty->GetCrossEnd(gridLayoutInfo_.axis_);
639 
640     curFocusIndexInfo_.mainIndex = curMainIndex;
641     curFocusIndexInfo_.crossIndex = curCrossIndex;
642     curFocusIndexInfo_.mainSpan = curMainSpan;
643     curFocusIndexInfo_.crossSpan = curCrossSpan;
644     curFocusIndexInfo_.mainStart = curMainStart;
645     curFocusIndexInfo_.mainEnd = curMainEnd;
646     curFocusIndexInfo_.crossStart = curCrossStart;
647     curFocusIndexInfo_.crossEnd = curCrossEnd;
648 
649     if (curMainIndex < 0 || curCrossIndex < 0) {
650         TAG_LOGW(AceLogTag::ACE_GRID, "can't find focused child.");
651         return nullptr;
652     }
653     if (gridLayoutInfo_.gridMatrix_.find(curMainIndex) == gridLayoutInfo_.gridMatrix_.end()) {
654         TAG_LOGW(AceLogTag::ACE_GRID, "Can not find current main index: %{public}d", curMainIndex);
655         return nullptr;
656     }
657     TAG_LOGI(AceLogTag::ACE_GRID,
658         "GetNextFocusNode: Current:(%{public}d,%{public}d)-[%{public}d,%{public}d]. Focus: %{public}d", curMainIndex,
659         curCrossIndex, curMainSpan, curCrossSpan, step);
660     auto focusSteps = GetFocusSteps(curMainIndex, curCrossIndex, step);
661     if (focusSteps.first != FocusStep::NONE && focusSteps.second != FocusStep::NONE) {
662         auto firstStepRes = GetNextFocusNode(focusSteps.first, currentFocusNode);
663         if (!firstStepRes.Upgrade()) {
664             return nullptr;
665         }
666         auto secondStepRes = GetNextFocusNode(focusSteps.second, firstStepRes);
667         if (!secondStepRes.Upgrade()) {
668             return firstStepRes;
669         }
670         return secondStepRes;
671     }
672     auto indexes = GetNextIndexByStep(curMainIndex, curCrossIndex, curMainSpan, curCrossSpan, step);
673     auto nextMainIndex = indexes.first;
674     auto nextCrossIndex = indexes.second;
675     while (nextMainIndex >= 0 && nextCrossIndex >= 0) {
676         if (gridLayoutInfo_.gridMatrix_.find(nextMainIndex) == gridLayoutInfo_.gridMatrix_.end()) {
677             TAG_LOGW(AceLogTag::ACE_GRID, "Can not find next main index: %{public}d", nextMainIndex);
678             return nullptr;
679         }
680         auto nextMaxCrossCount = GetCrossCount();
681         auto flag = (step == FocusStep::LEFT_END) || (step == FocusStep::RIGHT_END);
682         auto weakChild = gridLayoutInfo_.hasBigItem_ ? SearchIrregularFocusableChild(nextMainIndex, nextCrossIndex)
683                                                      : SearchFocusableChildInCross(nextMainIndex, nextCrossIndex,
684                                                            nextMaxCrossCount, flag ? -1 : curMainIndex, curCrossIndex);
685         auto child = weakChild.Upgrade();
686         if (child && child->IsFocusable()) {
687             ScrollToFocusNode(weakChild);
688             return weakChild;
689         }
690         auto indexes = GetNextIndexByStep(nextMainIndex, nextCrossIndex, 1, 1, step);
691         nextMainIndex = indexes.first;
692         nextCrossIndex = indexes.second;
693     }
694     return nullptr;
695 }
696 
GetNextIndexByStep(int32_t curMainIndex,int32_t curCrossIndex,int32_t curMainSpan,int32_t curCrossSpan,FocusStep step)697 std::pair<int32_t, int32_t> GridPattern::GetNextIndexByStep(
698     int32_t curMainIndex, int32_t curCrossIndex, int32_t curMainSpan, int32_t curCrossSpan, FocusStep step)
699 {
700     auto curMainStart = gridLayoutInfo_.startMainLineIndex_;
701     auto curMainEnd = gridLayoutInfo_.endMainLineIndex_;
702     auto curChildStartIndex = gridLayoutInfo_.startIndex_;
703     auto curChildEndIndex = gridLayoutInfo_.endIndex_;
704     auto childrenCount = gridLayoutInfo_.childrenCount_;
705     auto hasIrregularItems = gridLayoutInfo_.hasBigItem_;
706     if (gridLayoutInfo_.gridMatrix_.find(curMainIndex) == gridLayoutInfo_.gridMatrix_.end()) {
707         TAG_LOGW(AceLogTag::ACE_GRID, "Can not find current main index: %{public}d", curMainIndex);
708         return { -1, -1 };
709     }
710     TAG_LOGI(AceLogTag::ACE_GRID,
711         "Current: (%{public}d,%{public}d)-[%{public}d,%{public}d]. axis: %{public}d, step: %{public}d",
712         curMainIndex, curCrossIndex, curMainSpan, curCrossSpan, gridLayoutInfo_.axis_, step);
713     auto curMaxCrossCount = GetCrossCount();
714     auto nextMainIndex = curMainIndex;
715     auto nextCrossIndex = curCrossIndex;
716     if ((step == FocusStep::UP_END && gridLayoutInfo_.axis_ == Axis::HORIZONTAL) ||
717         (step == FocusStep::LEFT_END && gridLayoutInfo_.axis_ == Axis::VERTICAL)) {
718         nextMainIndex = curMainIndex;
719         nextCrossIndex = 0;
720         isLeftEndStep_ = hasIrregularItems ? true : false;
721     } else if ((step == FocusStep::DOWN_END && gridLayoutInfo_.axis_ == Axis::HORIZONTAL) ||
722                (step == FocusStep::RIGHT_END && gridLayoutInfo_.axis_ == Axis::VERTICAL)) {
723         nextMainIndex = curMainIndex;
724         nextCrossIndex = curMaxCrossCount - 1;
725         isRightEndStep_ = hasIrregularItems ? true : false;
726     } else if (((step == FocusStep::UP || step == FocusStep::SHIFT_TAB) && gridLayoutInfo_.axis_ == Axis::HORIZONTAL) ||
727                ((step == FocusStep::LEFT || step == FocusStep::SHIFT_TAB) && gridLayoutInfo_.axis_ == Axis::VERTICAL)) {
728         nextMainIndex = curMainIndex;
729         nextCrossIndex = curCrossIndex - 1;
730         isLeftStep_ = hasIrregularItems ? true : false;
731     } else if ((step == FocusStep::UP && gridLayoutInfo_.axis_ == Axis::VERTICAL) ||
732                (step == FocusStep::LEFT && gridLayoutInfo_.axis_ == Axis::HORIZONTAL)) {
733         nextMainIndex = hasIrregularItems ? curMainIndex - curMainSpan : curMainIndex - 1;
734         nextCrossIndex = curCrossIndex + static_cast<int32_t>((curCrossSpan - 1) / 2);
735         isUpStep_ = hasIrregularItems ? true : false;
736     } else if (((step == FocusStep::DOWN || step == FocusStep::TAB) && gridLayoutInfo_.axis_ == Axis::HORIZONTAL) ||
737                ((step == FocusStep::RIGHT || step == FocusStep::TAB) && gridLayoutInfo_.axis_ == Axis::VERTICAL)) {
738         nextMainIndex = curMainIndex;
739         nextCrossIndex = curCrossIndex + curCrossSpan;
740         isRightStep_ = hasIrregularItems ? true : false;
741     } else if ((step == FocusStep::DOWN && gridLayoutInfo_.axis_ == Axis::VERTICAL) ||
742                (step == FocusStep::RIGHT && gridLayoutInfo_.axis_ == Axis::HORIZONTAL)) {
743         nextMainIndex = hasIrregularItems ? curMainIndex + 1 : curMainIndex + curMainSpan;
744         nextCrossIndex = curCrossIndex + static_cast<int32_t>((curCrossSpan - 1) / 2);
745         isDownStep_ = hasIrregularItems ? true : false;
746     } else {
747         TAG_LOGW(AceLogTag::ACE_GRID, "Next index return: Invalid step: %{public}d and axis: %{public}d", step,
748             gridLayoutInfo_.axis_);
749         return { -1, -1 };
750     }
751     if (curChildStartIndex == 0 && curMainIndex == 0 && nextMainIndex < curMainIndex) {
752         nextMainIndex = curMainIndex;
753     }
754     if (curChildEndIndex == childrenCount - 1 && curMainIndex == curMainEnd && nextMainIndex > curMainIndex) {
755         nextMainIndex = curMainIndex;
756     }
757     if (nextMainIndex == curMainIndex && nextCrossIndex == curCrossIndex) {
758         TAG_LOGI(AceLogTag::ACE_GRID,
759             "Next index return: Move stoped. Next index: (%{public}d,%{public}d) is same as current.", nextMainIndex,
760             nextCrossIndex);
761         ResetAllDirectionsStep();
762         return { -1, -1 };
763     }
764     if (curChildStartIndex != 0 && curMainIndex == curMainStart && nextMainIndex < curMainIndex) {
765         // Scroll item up.
766         UpdateStartIndex(curChildStartIndex - 1);
767         auto pipeline = PipelineContext::GetCurrentContext();
768         if (pipeline) {
769             pipeline->FlushUITasks();
770         }
771     } else if (curChildEndIndex != childrenCount - 1 && curMainIndex == curMainEnd && nextMainIndex > curMainIndex) {
772         // Scroll item down.
773         UpdateStartIndex(curChildEndIndex + 1);
774         auto pipeline = PipelineContext::GetCurrentContext();
775         if (pipeline) {
776             pipeline->FlushUITasks();
777         }
778     }
779     curMainStart = gridLayoutInfo_.startMainLineIndex_;
780     curMainEnd = gridLayoutInfo_.endMainLineIndex_;
781     if (nextMainIndex < curMainStart || nextMainIndex > curMainEnd) {
782         ResetAllDirectionsStep();
783         return { -1, -1 };
784     }
785     if (nextCrossIndex < 0) {
786         ResetAllDirectionsStep();
787         return { -1, -1 };
788     }
789     if (gridLayoutInfo_.gridMatrix_.find(nextMainIndex) == gridLayoutInfo_.gridMatrix_.end()) {
790         ResetAllDirectionsStep();
791         return { -1, -1 };
792     }
793     auto nextMaxCrossCount = GetCrossCount();
794     if (nextCrossIndex >= nextMaxCrossCount) {
795         TAG_LOGI(AceLogTag::ACE_GRID,
796             "Next index: { %{public}d,%{public}d }. Next cross index is greater than max cross count: %{public}d.",
797             nextMainIndex, nextCrossIndex, nextMaxCrossCount - 1);
798         if (nextMaxCrossCount - 1 != (curCrossIndex + curCrossSpan - 1)) {
799             TAG_LOGI(AceLogTag::ACE_GRID,
800                 "Current cross index: %{public}d is not the tail item. Return to the tail: { %{public}d,%{public}d }",
801                 curCrossIndex, nextMainIndex, nextMaxCrossCount - 1);
802             return { nextMainIndex, nextMaxCrossCount - 1 };
803         }
804         ResetAllDirectionsStep();
805         TAG_LOGI(AceLogTag::ACE_GRID, "Current cross index: %{public}d is the tail item. No next item can be found!",
806             curCrossIndex);
807         return { -1, -1 };
808     }
809     TAG_LOGI(AceLogTag::ACE_GRID, "Next index return: { %{public}d,%{public}d }.", nextMainIndex, nextCrossIndex);
810     return { nextMainIndex, nextCrossIndex };
811 }
812 
SearchFocusableChildInCross(int32_t tarMainIndex,int32_t tarCrossIndex,int32_t maxCrossCount,int32_t curMainIndex,int32_t curCrossIndex)813 WeakPtr<FocusHub> GridPattern::SearchFocusableChildInCross(
814     int32_t tarMainIndex, int32_t tarCrossIndex, int32_t maxCrossCount, int32_t curMainIndex, int32_t curCrossIndex)
815 {
816     bool isDirectionLeft = true;
817     auto indexLeft = tarCrossIndex;
818     auto indexRight = tarCrossIndex;
819     if (curMainIndex == tarMainIndex) {
820         // Search on the same main index. Do not need search on both left and right side.
821         if (tarCrossIndex > curCrossIndex) {
822             // Only search on the right side.
823             indexLeft = -1;
824         } else if (tarCrossIndex < curCrossIndex) {
825             // Only search on the left side.
826             indexRight = maxCrossCount;
827         } else {
828             TAG_LOGW(AceLogTag::ACE_GRID, "Invalid search index: (%{public}d,%{public}d). It's same as current.",
829                 tarMainIndex, tarCrossIndex);
830             return nullptr;
831         }
832     }
833     while (indexLeft >= 0 || indexRight < maxCrossCount) {
834         int32_t curIndex = indexLeft;
835         if (indexLeft < 0) {
836             curIndex = indexRight++;
837         } else if (indexRight >= maxCrossCount) {
838             curIndex = indexLeft--;
839         } else {
840             curIndex = isDirectionLeft ? indexLeft-- : indexRight++;
841             isDirectionLeft = !isDirectionLeft;
842         }
843         auto weakChild = GetChildFocusNodeByIndex(tarMainIndex, curIndex);
844         auto child = weakChild.Upgrade();
845         if (child && child->IsFocusable()) {
846             TAG_LOGI(AceLogTag::ACE_GRID, "Found child. Index: %{public}d,%{public}d", tarMainIndex, curIndex);
847             return weakChild;
848         }
849     }
850     return nullptr;
851 }
852 
SearchIrregularFocusableChild(int32_t tarMainIndex,int32_t tarCrossIndex)853 WeakPtr<FocusHub> GridPattern::SearchIrregularFocusableChild(int32_t tarMainIndex, int32_t tarCrossIndex)
854 {
855     double minDistance = std::numeric_limits<double>::max();
856     int32_t minMainIndex = std::numeric_limits<int32_t>::max();
857     int32_t minCrossIndex = std::numeric_limits<int32_t>::max();
858     int32_t maxAreaInMainShadow = -1;
859     int32_t maxAreaInCrossShadow = -1;
860     WeakPtr<FocusHub> targetFocusHubWeak;
861 
862     auto gridFrame = GetHost();
863     CHECK_NULL_RETURN(gridFrame, nullptr);
864     auto gridFocus = gridFrame->GetFocusHub();
865     CHECK_NULL_RETURN(gridFocus, nullptr);
866     auto childFocusList = gridFocus->GetChildren();
867     for (const auto& childFocus : childFocusList) {
868         if (!childFocus->IsFocusable()) {
869             continue;
870         }
871         auto childFrame = childFocus->GetFrameNode();
872         if (!childFrame) {
873             continue;
874         }
875         auto childPattern = childFrame->GetPattern<GridItemPattern>();
876         if (!childPattern) {
877             continue;
878         }
879         auto childItemProperty = childFrame->GetLayoutProperty<GridItemLayoutProperty>();
880         if (!childItemProperty) {
881             continue;
882         }
883         auto irregularInfo = childPattern->GetIrregularItemInfo();
884         bool hasIrregularItemInfo = irregularInfo.has_value();
885 
886         auto childMainIndex = childItemProperty->GetMainIndex().value_or(-1);
887         auto childCrossIndex = childItemProperty->GetCrossIndex().value_or(-1);
888         auto childMainStart = hasIrregularItemInfo ? irregularInfo.value().mainStart
889                                                    : childItemProperty->GetMainStart(gridLayoutInfo_.axis_);
890         auto childMainEnd =
891             hasIrregularItemInfo ? irregularInfo.value().mainEnd : childItemProperty->GetMainEnd(gridLayoutInfo_.axis_);
892         auto chidCrossStart = hasIrregularItemInfo ? irregularInfo.value().crossStart
893                                                    : childItemProperty->GetCrossStart(gridLayoutInfo_.axis_);
894         auto chidCrossEnd = hasIrregularItemInfo ? irregularInfo.value().crossEnd
895                                                  : childItemProperty->GetCrossEnd(gridLayoutInfo_.axis_);
896         auto childCrossSpan = hasIrregularItemInfo ? irregularInfo.value().crossSpan
897                                                    : childItemProperty->GetCrossSpan(gridLayoutInfo_.axis_);
898         auto childMainSpan = hasIrregularItemInfo ? irregularInfo.value().mainSpan
899                                                   : childItemProperty->GetMainSpan(gridLayoutInfo_.axis_);
900 
901         GridItemIndexInfo childInfo;
902         childInfo.mainIndex = childMainIndex;
903         childInfo.crossIndex = childCrossIndex;
904         childInfo.mainStart = childMainStart;
905         childInfo.mainEnd = childMainEnd;
906         childInfo.crossStart = chidCrossStart;
907         childInfo.crossEnd = chidCrossEnd;
908 
909         if (childMainIndex < 0 || childCrossIndex < 0) {
910             continue;
911         }
912 
913         if ((isLeftStep_ && ((childCrossIndex == tarCrossIndex && childCrossSpan == 1) ||
914                                 (chidCrossEnd >= 0 && chidCrossEnd == tarCrossIndex))) ||
915             (isRightStep_ && childCrossIndex == tarCrossIndex)) {
916             double nearestDistance = GetNearestDistanceFromChildToCurFocusItemInMainAxis(tarCrossIndex, childInfo);
917             int32_t intersectAreaSize = CalcIntersectAreaInTargetDirectionShadow(childInfo, true);
918             if (LessNotEqual(nearestDistance, minDistance) ||
919                 (NearEqual(nearestDistance, minDistance) && intersectAreaSize > maxAreaInCrossShadow) ||
920                 (NearEqual(nearestDistance, minDistance) && intersectAreaSize == maxAreaInCrossShadow &&
921                     childMainIndex < minMainIndex)) {
922                 minDistance = nearestDistance;
923                 maxAreaInCrossShadow = intersectAreaSize;
924                 minMainIndex = childMainIndex;
925                 targetFocusHubWeak = AceType::WeakClaim(AceType::RawPtr(childFocus));
926             }
927         } else if ((isUpStep_ && childMainIndex == tarMainIndex) ||
928                    (isDownStep_ && ((childMainIndex == tarMainIndex && childMainSpan == 1) ||
929                                        (childMainStart >= 0 && childMainStart == tarMainIndex)))) {
930             double nearestDistance = GetNearestDistanceFromChildToCurFocusItemInCrossAxis(tarMainIndex, childInfo);
931             int32_t intersectAreaSize = CalcIntersectAreaInTargetDirectionShadow(childInfo, false);
932             if (LessNotEqual(nearestDistance, minDistance) ||
933                 (NearEqual(nearestDistance, minDistance) && intersectAreaSize > maxAreaInMainShadow) ||
934                 (NearEqual(nearestDistance, minDistance) && intersectAreaSize == maxAreaInMainShadow &&
935                     childCrossIndex < minCrossIndex)) {
936                 minDistance = nearestDistance;
937                 minCrossIndex = childCrossIndex;
938                 maxAreaInMainShadow = intersectAreaSize;
939                 targetFocusHubWeak = AceType::WeakClaim(AceType::RawPtr(childFocus));
940             }
941         } else if ((isLeftEndStep_ || isRightEndStep_) &&
942                    ((tarMainIndex == childMainIndex && tarCrossIndex == childCrossIndex) ||
943                        (childMainStart >= 0 && childMainStart <= tarMainIndex && tarMainIndex <= childMainIndex &&
944                            tarCrossIndex == childCrossIndex))) {
945             targetFocusHubWeak = AceType::WeakClaim(AceType::RawPtr(childFocus));
946         }
947     }
948     ResetAllDirectionsStep();
949     return targetFocusHubWeak;
950 }
951 
CalcIntersectAreaInTargetDirectionShadow(GridItemIndexInfo itemIndexInfo,bool isFindInMainAxis)952 int32_t GridPattern::CalcIntersectAreaInTargetDirectionShadow(GridItemIndexInfo itemIndexInfo, bool isFindInMainAxis)
953 {
954     int32_t curFocusLeftTopX = -1;
955     int32_t curFocusLeftTopY = -1;
956     int32_t curFocusRightBottonX = -1;
957     int32_t curFocusRightBottonY = -1;
958 
959     if (isFindInMainAxis) {
960         curFocusLeftTopX =
961             curFocusIndexInfo_.mainStart == -1 ? curFocusIndexInfo_.mainIndex : curFocusIndexInfo_.mainStart;
962         curFocusLeftTopY = 0;
963         curFocusRightBottonX =
964             curFocusIndexInfo_.mainEnd == -1 ? curFocusIndexInfo_.mainIndex : curFocusIndexInfo_.mainEnd;
965         curFocusRightBottonY = GetCrossCount();
966     } else {
967         curFocusLeftTopX = gridLayoutInfo_.startMainLineIndex_;
968         curFocusLeftTopY =
969             curFocusIndexInfo_.crossStart == -1 ? curFocusIndexInfo_.crossIndex : curFocusIndexInfo_.crossStart;
970         curFocusRightBottonX = gridLayoutInfo_.endMainLineIndex_;
971         curFocusRightBottonY =
972             curFocusIndexInfo_.crossEnd == -1 ? curFocusIndexInfo_.crossIndex : curFocusIndexInfo_.crossEnd;
973     }
974     int32_t childLeftTopX = itemIndexInfo.mainStart == -1 ? itemIndexInfo.mainIndex : itemIndexInfo.mainStart;
975     int32_t childLeftTopY = itemIndexInfo.crossStart == -1 ? itemIndexInfo.crossIndex : itemIndexInfo.crossStart;
976     int32_t childRightBottonX = itemIndexInfo.mainEnd == -1 ? itemIndexInfo.mainIndex : itemIndexInfo.mainEnd;
977     int32_t childRightBottonY = itemIndexInfo.crossEnd == -1 ? itemIndexInfo.crossIndex : itemIndexInfo.crossEnd;
978 
979     int32_t intersectAreaLeftTopX = std::max(curFocusLeftTopX, childLeftTopX);
980     int32_t intersectAreaLeftTopY = std::max(curFocusLeftTopY, childLeftTopY);
981     int32_t intersectAreaRightBottonX = std::min(curFocusRightBottonX, childRightBottonX);
982     int32_t intersectAreaRightBottonY = std::min(curFocusRightBottonY, childRightBottonY);
983 
984     int32_t intersectWidth = intersectAreaRightBottonX - intersectAreaLeftTopX + 1;
985     int32_t intersectHeight = intersectAreaRightBottonY - intersectAreaLeftTopY + 1;
986 
987     return (intersectWidth < 0 || intersectHeight < 0) ? -1 : intersectWidth * intersectHeight;
988 }
989 
GetNearestDistanceFromChildToCurFocusItemInMainAxis(int32_t targetIndex,GridItemIndexInfo itemIndexInfo)990 double GridPattern::GetNearestDistanceFromChildToCurFocusItemInMainAxis(
991     int32_t targetIndex, GridItemIndexInfo itemIndexInfo)
992 {
993     double minDistance = std::numeric_limits<double>::max();
994     auto mainAxisIndex =
995         curFocusIndexInfo_.mainStart == -1 ? curFocusIndexInfo_.mainIndex : curFocusIndexInfo_.mainStart;
996     auto mainAxisEndIndex =
997         curFocusIndexInfo_.mainEnd == -1 ? curFocusIndexInfo_.mainIndex : curFocusIndexInfo_.mainEnd;
998     for (int32_t i = mainAxisIndex; i <= mainAxisEndIndex; i++) {
999         double childMainIndexDistance =
1000             CalcCoordinatesDistance(i, curFocusIndexInfo_.crossIndex, itemIndexInfo.mainIndex, targetIndex);
1001         double childMainStartDistance =
1002             itemIndexInfo.mainStart == -1
1003                 ? std::numeric_limits<double>::max()
1004                 : CalcCoordinatesDistance(i, curFocusIndexInfo_.crossIndex, itemIndexInfo.mainStart, targetIndex);
1005         double distance = std::min(childMainIndexDistance, childMainStartDistance);
1006         if (LessNotEqual(distance, minDistance)) {
1007             minDistance = distance;
1008         }
1009     }
1010     return minDistance;
1011 }
1012 
GetNearestDistanceFromChildToCurFocusItemInCrossAxis(int32_t targetIndex,GridItemIndexInfo itemIndexInfo)1013 double GridPattern::GetNearestDistanceFromChildToCurFocusItemInCrossAxis(
1014     int32_t targetIndex, GridItemIndexInfo itemIndexInfo)
1015 {
1016     double minDistance = std::numeric_limits<double>::max();
1017     auto crossAxisIndex =
1018         curFocusIndexInfo_.crossStart == -1 ? curFocusIndexInfo_.crossIndex : curFocusIndexInfo_.crossStart;
1019     auto crossAxisEndIndex =
1020         curFocusIndexInfo_.crossEnd == -1 ? curFocusIndexInfo_.crossIndex : curFocusIndexInfo_.crossEnd;
1021     for (int32_t i = crossAxisIndex; i <= crossAxisEndIndex; i++) {
1022         double childCrossIndexDistance =
1023             CalcCoordinatesDistance(curFocusIndexInfo_.mainIndex, i, targetIndex, itemIndexInfo.crossIndex);
1024         double childCrossEndDistance =
1025             itemIndexInfo.crossEnd == -1
1026                 ? std::numeric_limits<double>::max()
1027                 : CalcCoordinatesDistance(curFocusIndexInfo_.mainIndex, i, targetIndex, itemIndexInfo.crossEnd);
1028         double distance = std::min(childCrossIndexDistance, childCrossEndDistance);
1029         if (LessNotEqual(distance, minDistance)) {
1030             minDistance = distance;
1031         }
1032     }
1033     return minDistance;
1034 }
1035 
ResetAllDirectionsStep()1036 void GridPattern::ResetAllDirectionsStep()
1037 {
1038     isLeftStep_ = false;
1039     isRightStep_ = false;
1040     isUpStep_ = false;
1041     isDownStep_ = false;
1042     isLeftEndStep_ = false;
1043     isRightEndStep_ = false;
1044 }
1045 
GetChildFocusNodeByIndex(int32_t tarMainIndex,int32_t tarCrossIndex,int32_t tarIndex)1046 WeakPtr<FocusHub> GridPattern::GetChildFocusNodeByIndex(int32_t tarMainIndex, int32_t tarCrossIndex, int32_t tarIndex)
1047 {
1048     auto gridFrame = GetHost();
1049     CHECK_NULL_RETURN(gridFrame, nullptr);
1050     auto gridFocus = gridFrame->GetFocusHub();
1051     CHECK_NULL_RETURN(gridFocus, nullptr);
1052     auto childFocusList = gridFocus->GetChildren();
1053     for (const auto& childFocus : childFocusList) {
1054         auto childFrame = childFocus->GetFrameNode();
1055         if (!childFrame) {
1056             continue;
1057         }
1058         auto childPattern = childFrame->GetPattern();
1059         if (!childPattern) {
1060             continue;
1061         }
1062         auto childItemPattern = AceType::DynamicCast<GridItemPattern>(childPattern);
1063         if (!childItemPattern) {
1064             continue;
1065         }
1066         auto childItemProperty = childItemPattern->GetLayoutProperty<GridItemLayoutProperty>();
1067         if (!childItemProperty) {
1068             continue;
1069         }
1070         auto curMainIndex = childItemProperty->GetMainIndex().value_or(-1);
1071         auto curCrossIndex = childItemProperty->GetCrossIndex().value_or(-1);
1072         if (tarIndex < 0) {
1073             auto curMainSpan = childItemProperty->GetMainSpan(gridLayoutInfo_.axis_);
1074             auto curCrossSpan = childItemProperty->GetCrossSpan(gridLayoutInfo_.axis_);
1075             if (curMainIndex <= tarMainIndex && curMainIndex + curMainSpan > tarMainIndex &&
1076                 curCrossIndex <= tarCrossIndex && curCrossIndex + curCrossSpan > tarCrossIndex) {
1077                 return AceType::WeakClaim(AceType::RawPtr(childFocus));
1078             }
1079         } else {
1080             if (gridLayoutInfo_.gridMatrix_.find(curMainIndex) == gridLayoutInfo_.gridMatrix_.end()) {
1081                 TAG_LOGW(AceLogTag::ACE_GRID, "Can not find target main index: %{public}d", curMainIndex);
1082                 continue;
1083             }
1084             if (gridLayoutInfo_.gridMatrix_[curMainIndex].find(curCrossIndex) ==
1085                 gridLayoutInfo_.gridMatrix_[curMainIndex].end()) {
1086                 TAG_LOGW(AceLogTag::ACE_GRID, "Can not find target cross index: %{public}d", curCrossIndex);
1087                 continue;
1088             }
1089             if (gridLayoutInfo_.gridMatrix_[curMainIndex][curCrossIndex] == tarIndex) {
1090                 return AceType::WeakClaim(AceType::RawPtr(childFocus));
1091             }
1092         }
1093     }
1094     return nullptr;
1095 }
1096 
GetFocusableChildCrossIndexesAt(int32_t tarMainIndex)1097 std::unordered_set<int32_t> GridPattern::GetFocusableChildCrossIndexesAt(int32_t tarMainIndex)
1098 {
1099     std::unordered_set<int32_t> result;
1100     auto gridFrame = GetHost();
1101     CHECK_NULL_RETURN(gridFrame, result);
1102     auto gridFocus = gridFrame->GetFocusHub();
1103     CHECK_NULL_RETURN(gridFocus, result);
1104     auto childFocusList = gridFocus->GetChildren();
1105     for (const auto& childFocus : childFocusList) {
1106         if (!childFocus->IsFocusable()) {
1107             continue;
1108         }
1109         auto childFrame = childFocus->GetFrameNode();
1110         if (!childFrame) {
1111             continue;
1112         }
1113         auto childPattern = childFrame->GetPattern();
1114         if (!childPattern) {
1115             continue;
1116         }
1117         auto childItemPattern = AceType::DynamicCast<GridItemPattern>(childPattern);
1118         if (!childItemPattern) {
1119             continue;
1120         }
1121         auto childItemProperty = childItemPattern->GetLayoutProperty<GridItemLayoutProperty>();
1122         if (!childItemProperty) {
1123             continue;
1124         }
1125         auto irregularInfo = childItemPattern->GetIrregularItemInfo();
1126         bool hasIrregularItemInfo = irregularInfo.has_value();
1127         auto curMainIndex = childItemProperty->GetMainIndex().value_or(-1);
1128         auto curCrossIndex = childItemProperty->GetCrossIndex().value_or(-1);
1129         auto curMainStart = hasIrregularItemInfo ? irregularInfo.value().mainStart
1130                                                  : childItemProperty->GetMainStart(gridLayoutInfo_.axis_);
1131         auto curMainEnd =
1132             hasIrregularItemInfo ? irregularInfo.value().mainEnd : childItemProperty->GetMainEnd(gridLayoutInfo_.axis_);
1133         if ((curMainIndex == tarMainIndex) ||
1134             (curMainStart >= 0 && curMainStart <= tarMainIndex && tarMainIndex <= curMainEnd)) {
1135             result.emplace(curCrossIndex);
1136         }
1137     }
1138     std::string output;
1139     for (const auto& index : result) {
1140         output += std::to_string(index);
1141     }
1142     return result;
1143 }
1144 
ScrollToFocusNode(const WeakPtr<FocusHub> & focusNode)1145 void GridPattern::ScrollToFocusNode(const WeakPtr<FocusHub>& focusNode)
1146 {
1147     auto nextFocus = focusNode.Upgrade();
1148     CHECK_NULL_VOID(nextFocus);
1149     UpdateStartIndex(GetFocusNodeIndex(nextFocus));
1150 }
1151 
GetFocusNodeIndex(const RefPtr<FocusHub> & focusNode)1152 int32_t GridPattern::GetFocusNodeIndex(const RefPtr<FocusHub>& focusNode)
1153 {
1154     auto tarFrame = focusNode->GetFrameNode();
1155     CHECK_NULL_RETURN(tarFrame, -1);
1156     auto tarPattern = tarFrame->GetPattern();
1157     CHECK_NULL_RETURN(tarPattern, -1);
1158     auto tarItemPattern = AceType::DynamicCast<GridItemPattern>(tarPattern);
1159     CHECK_NULL_RETURN(tarItemPattern, -1);
1160     auto tarItemProperty = tarItemPattern->GetLayoutProperty<GridItemLayoutProperty>();
1161     CHECK_NULL_RETURN(tarItemProperty, -1);
1162     auto tarMainIndex = tarItemProperty->GetMainIndex().value_or(-1);
1163     auto tarCrossIndex = tarItemProperty->GetCrossIndex().value_or(-1);
1164     if (gridLayoutInfo_.gridMatrix_.find(tarMainIndex) == gridLayoutInfo_.gridMatrix_.end()) {
1165         TAG_LOGW(AceLogTag::ACE_GRID, "Can not find target main index: %{public}d", tarMainIndex);
1166         if (tarMainIndex == 0) {
1167             return 0;
1168         }
1169         return gridLayoutInfo_.childrenCount_ - 1;
1170     }
1171     if (gridLayoutInfo_.gridMatrix_[tarMainIndex].find(tarCrossIndex) ==
1172         gridLayoutInfo_.gridMatrix_[tarMainIndex].end()) {
1173         TAG_LOGW(AceLogTag::ACE_GRID, "Can not find target cross index: %{public}d", tarCrossIndex);
1174         if (tarMainIndex == 0) {
1175             return 0;
1176         }
1177         return gridLayoutInfo_.childrenCount_ - 1;
1178     }
1179     return gridLayoutInfo_.gridMatrix_[tarMainIndex][tarCrossIndex];
1180 }
1181 
ScrollToFocusNodeIndex(int32_t index)1182 void GridPattern::ScrollToFocusNodeIndex(int32_t index)
1183 {
1184     UpdateStartIndex(index);
1185     auto pipeline = PipelineContext::GetCurrentContext();
1186     if (pipeline) {
1187         pipeline->FlushUITasks();
1188     }
1189     auto tarFocusNodeWeak = GetChildFocusNodeByIndex(-1, -1, index);
1190     auto tarFocusNode = tarFocusNodeWeak.Upgrade();
1191     if (tarFocusNode) {
1192         tarFocusNode->RequestFocusImmediately();
1193     }
1194 }
1195 
ScrollToNode(const RefPtr<FrameNode> & focusFrameNode)1196 bool GridPattern::ScrollToNode(const RefPtr<FrameNode>& focusFrameNode)
1197 {
1198     CHECK_NULL_RETURN(focusFrameNode, false);
1199     auto focusHub = focusFrameNode->GetFocusHub();
1200     CHECK_NULL_RETURN(focusHub, false);
1201     auto scrollToIndex = GetFocusNodeIndex(focusHub);
1202     if (scrollToIndex < 0) {
1203         return false;
1204     }
1205     auto ret = UpdateStartIndex(scrollToIndex);
1206     auto pipeline = PipelineContext::GetCurrentContext();
1207     if (pipeline) {
1208         pipeline->FlushUITasks();
1209     }
1210     return ret;
1211 }
1212 
GetScrollOffsetAbility()1213 std::pair<std::function<bool(float)>, Axis> GridPattern::GetScrollOffsetAbility()
1214 {
1215     return { [wp = WeakClaim(this)](float moveOffset) -> bool {
1216                 auto pattern = wp.Upgrade();
1217                 CHECK_NULL_RETURN(pattern, false);
1218                 pattern->ScrollBy(-moveOffset);
1219                 return true;
1220             },
1221         GetAxis() };
1222 }
1223 
GetScrollIndexAbility()1224 std::function<bool(int32_t)> GridPattern::GetScrollIndexAbility()
1225 {
1226     return [wp = WeakClaim(this)](int32_t index) -> bool {
1227         auto pattern = wp.Upgrade();
1228         CHECK_NULL_RETURN(pattern, false);
1229         if (index == FocusHub::SCROLL_TO_HEAD) {
1230             pattern->ScrollToEdge(ScrollEdgeType::SCROLL_TOP, false);
1231         } else if (index == FocusHub::SCROLL_TO_TAIL) {
1232             pattern->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false);
1233         } else {
1234             pattern->UpdateStartIndex(index);
1235         }
1236         return true;
1237     };
1238 }
1239 
ScrollBy(float offset)1240 void GridPattern::ScrollBy(float offset)
1241 {
1242     StopAnimate();
1243     UpdateCurrentOffset(-offset, SCROLL_FROM_JUMP);
1244     // AccessibilityEventType::SCROLL_END
1245 }
1246 
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const1247 void GridPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
1248 {
1249     ScrollablePattern::ToJsonValue(json, filter);
1250     /* no fixed attr below, just return */
1251     if (filter.IsFastFilter()) {
1252         return;
1253     }
1254     json->PutExtAttr("multiSelectable", multiSelectable_ ? "true" : "false", filter);
1255     json->PutExtAttr("supportAnimation", supportAnimation_ ? "true" : "false", filter);
1256 }
1257 
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)1258 void GridPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
1259 {
1260     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
1261         auto pattern = wp.Upgrade();
1262         if (pattern) {
1263             return pattern->OnKeyEvent(event);
1264         }
1265         return false;
1266     };
1267     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
1268 }
1269 
OnKeyEvent(const KeyEvent & event)1270 bool GridPattern::OnKeyEvent(const KeyEvent& event)
1271 {
1272     if (event.action != KeyAction::DOWN) {
1273         return false;
1274     }
1275     if ((event.code == KeyCode::KEY_PAGE_DOWN) || (event.code == KeyCode::KEY_PAGE_UP)) {
1276         ScrollPage(event.code == KeyCode::KEY_PAGE_UP);
1277     }
1278     return false;
1279 }
1280 
HandleDirectionKey(KeyCode code)1281 bool GridPattern::HandleDirectionKey(KeyCode code)
1282 {
1283     if (code == KeyCode::KEY_DPAD_UP) {
1284         // Need to update: current selection
1285         return true;
1286     }
1287     if (code == KeyCode::KEY_DPAD_DOWN) {
1288         // Need to update: current selection
1289         return true;
1290     }
1291     return false;
1292 }
1293 
ScrollPage(bool reverse,AccessibilityScrollType scrollType)1294 void GridPattern::ScrollPage(bool reverse, AccessibilityScrollType scrollType)
1295 {
1296     StopAnimate();
1297     if (!isConfigScrollable_) {
1298         return;
1299     }
1300     float distance = reverse ? GetMainContentSize() : -GetMainContentSize();
1301     if (scrollType == AccessibilityScrollType::SCROLL_HALF) {
1302         distance = distance / 2.f;
1303     }
1304     UpdateCurrentOffset(distance, SCROLL_FROM_JUMP);
1305     // AccessibilityEventType::SCROLL_END
1306 }
1307 
UpdateStartIndex(int32_t index)1308 bool GridPattern::UpdateStartIndex(int32_t index)
1309 {
1310     if (!isConfigScrollable_) {
1311         return false;
1312     }
1313     auto host = GetHost();
1314     CHECK_NULL_RETURN(host, false);
1315     gridLayoutInfo_.jumpIndex_ = index;
1316     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1317     // AccessibilityEventType::SCROLL_END
1318     SetScrollSource(SCROLL_FROM_JUMP);
1319     return true;
1320 }
1321 
UpdateStartIndex(int32_t index,ScrollAlign align)1322 bool GridPattern::UpdateStartIndex(int32_t index, ScrollAlign align)
1323 {
1324     gridLayoutInfo_.scrollAlign_ = align;
1325     return UpdateStartIndex(index);
1326 }
1327 
OnAnimateStop()1328 void GridPattern::OnAnimateStop()
1329 {
1330     if (!GetIsDragging() || GetScrollAbort()) {
1331         scrollStop_ = true;
1332         MarkDirtyNodeSelf();
1333     }
1334 }
1335 
AnimateTo(float position,float duration,const RefPtr<Curve> & curve,bool smooth,bool canOverScroll,bool useTotalOffset)1336 void GridPattern::AnimateTo(
1337     float position, float duration, const RefPtr<Curve>& curve, bool smooth, bool canOverScroll, bool useTotalOffset)
1338 {
1339     if (!isConfigScrollable_) {
1340         return;
1341     }
1342     ScrollablePattern::AnimateTo(position, duration, curve, smooth, canOverScroll);
1343 }
1344 
ScrollTo(float position)1345 void GridPattern::ScrollTo(float position)
1346 {
1347     if (!isConfigScrollable_) {
1348         return;
1349     }
1350     TAG_LOGI(AceLogTag::ACE_GRID, "ScrollTo:%{public}f", position);
1351     StopAnimate();
1352     UpdateCurrentOffset(GetTotalOffset() - position, SCROLL_FROM_JUMP);
1353     // AccessibilityEventType::SCROLL_END
1354 }
1355 
EstimateHeight() const1356 float GridPattern::EstimateHeight() const
1357 {
1358     if (!isConfigScrollable_) {
1359         return 0.0f;
1360     }
1361     // During the scrolling animation, the exact current position is used. Other times use the estimated location
1362     if (isSmoothScrolling_) {
1363         const auto* infoPtr = UseIrregularLayout() ? &gridLayoutInfo_ : &scrollGridLayoutInfo_;
1364         int32_t lineIndex = 0;
1365         infoPtr->GetLineIndexByIndex(gridLayoutInfo_.startIndex_, lineIndex);
1366         return infoPtr->GetTotalHeightFromZeroIndex(lineIndex, GetMainGap()) - gridLayoutInfo_.currentOffset_;
1367     }
1368     auto host = GetHost();
1369     CHECK_NULL_RETURN(host, 0.0);
1370     auto geometryNode = host->GetGeometryNode();
1371     CHECK_NULL_RETURN(geometryNode, 0.0);
1372     const auto& info = gridLayoutInfo_;
1373     auto viewScopeSize = geometryNode->GetPaddingSize();
1374     auto layoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
1375     auto mainGap = GridUtils::GetMainGap(layoutProperty, viewScopeSize, info.axis_);
1376     if (UseIrregularLayout()) {
1377         return info.GetIrregularOffset(mainGap);
1378     }
1379     if (!layoutProperty->GetLayoutOptions().has_value()) {
1380         return info.GetContentOffset(mainGap);
1381     }
1382 
1383     return info.GetContentOffset(layoutProperty->GetLayoutOptions().value(), mainGap);
1384 }
1385 
GetAverageHeight() const1386 float GridPattern::GetAverageHeight() const
1387 {
1388     auto host = GetHost();
1389     CHECK_NULL_RETURN(host, 0.0);
1390     auto geometryNode = host->GetGeometryNode();
1391     CHECK_NULL_RETURN(geometryNode, 0.0);
1392     const auto& info = gridLayoutInfo_;
1393     auto viewScopeSize = geometryNode->GetPaddingSize();
1394     auto layoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
1395 
1396     float heightSum = 0;
1397     int32_t itemCount = 0;
1398     auto mainGap = GridUtils::GetMainGap(layoutProperty, viewScopeSize, info.axis_);
1399     for (const auto& item : info.lineHeightMap_) {
1400         auto line = info.gridMatrix_.find(item.first);
1401         if (line == info.gridMatrix_.end()) {
1402             continue;
1403         }
1404         if (line->second.empty()) {
1405             continue;
1406         }
1407         auto lineStart = line->second.begin()->second;
1408         auto lineEnd = line->second.rbegin()->second;
1409         itemCount += (lineEnd - lineStart + 1);
1410         heightSum += item.second + mainGap;
1411     }
1412     if (itemCount == 0) {
1413         return 0;
1414     }
1415     return heightSum / itemCount;
1416 }
1417 
GetTotalHeight() const1418 float GridPattern::GetTotalHeight() const
1419 {
1420     if (scrollbarInfo_.first.has_value() && scrollbarInfo_.second.has_value()) {
1421         return scrollbarInfo_.second.value();
1422     }
1423     auto host = GetHost();
1424     CHECK_NULL_RETURN(host, 0.0f);
1425     auto geometryNode = host->GetGeometryNode();
1426     CHECK_NULL_RETURN(geometryNode, 0.0f);
1427     auto viewScopeSize = geometryNode->GetPaddingSize();
1428     auto layoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
1429     auto mainGap = GridUtils::GetMainGap(layoutProperty, viewScopeSize, gridLayoutInfo_.axis_);
1430     if (UseIrregularLayout()) {
1431         return gridLayoutInfo_.GetIrregularHeight(mainGap);
1432     }
1433     return gridLayoutInfo_.GetContentHeight(mainGap);
1434 }
1435 
UpdateScrollBarOffset()1436 void GridPattern::UpdateScrollBarOffset()
1437 {
1438     CheckScrollBarOff();
1439     if ((!GetScrollBar() && !GetScrollBarProxy()) || !isConfigScrollable_) {
1440         return;
1441     }
1442     auto host = GetHost();
1443     CHECK_NULL_VOID(host);
1444     auto geometryNode = host->GetGeometryNode();
1445     CHECK_NULL_VOID(geometryNode);
1446     const auto& info = gridLayoutInfo_;
1447     float offset = 0;
1448     float estimatedHeight = 0.f;
1449     if (scrollbarInfo_.first.has_value() && scrollbarInfo_.second.has_value()) {
1450         offset = scrollbarInfo_.first.value();
1451         estimatedHeight = scrollbarInfo_.second.value();
1452     } else {
1453         auto viewScopeSize = geometryNode->GetPaddingSize();
1454         auto layoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
1455         auto mainGap = GridUtils::GetMainGap(layoutProperty, viewScopeSize, info.axis_);
1456         if (UseIrregularLayout()) {
1457             offset = info.GetIrregularOffset(mainGap);
1458             estimatedHeight = info.GetIrregularHeight(mainGap);
1459         } else if (!layoutProperty->GetLayoutOptions().has_value()) {
1460             offset = info.GetContentOffset(mainGap);
1461             estimatedHeight = info.GetContentHeight(mainGap);
1462         } else {
1463             offset = info.GetContentOffset(layoutProperty->GetLayoutOptions().value(), mainGap);
1464             estimatedHeight =
1465                 info.GetContentHeight(layoutProperty->GetLayoutOptions().value(), info.childrenCount_, mainGap);
1466         }
1467     }
1468     if (info.startMainLineIndex_ != 0 && info.startIndex_ == 0) {
1469         for (int32_t lineIndex = info.startMainLineIndex_ - 1; lineIndex >= 0; lineIndex--) {
1470             offset += info.lineHeightMap_.find(lineIndex)->second;
1471         }
1472     }
1473     auto viewSize = geometryNode->GetFrameSize();
1474     auto overScroll = 0.0f;
1475     if (gridLayoutInfo_.reachStart_ && Positive(gridLayoutInfo_.currentOffset_)) {
1476         overScroll = gridLayoutInfo_.currentOffset_;
1477     } else {
1478         overScroll = gridLayoutInfo_.lastMainSize_ - estimatedHeight + offset;
1479         overScroll = Positive(overScroll) ? overScroll : 0.0f;
1480     }
1481     HandleScrollBarOutBoundary(overScroll);
1482     UpdateScrollBarRegion(offset, estimatedHeight, Size(viewSize.Width(), viewSize.Height()), Offset(0.0f, 0.0f));
1483 }
1484 
GetDefaultScrollBarDisplayMode() const1485 DisplayMode GridPattern::GetDefaultScrollBarDisplayMode() const
1486 {
1487     auto defaultDisplayMode = DisplayMode::OFF;
1488     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
1489         defaultDisplayMode = DisplayMode::AUTO;
1490     }
1491     return defaultDisplayMode;
1492 }
1493 
GetOriginalIndex() const1494 int32_t GridPattern::GetOriginalIndex() const
1495 {
1496     return gridLayoutInfo_.GetOriginalIndex();
1497 }
1498 
GetCrossCount() const1499 int32_t GridPattern::GetCrossCount() const
1500 {
1501     return gridLayoutInfo_.crossCount_;
1502 }
1503 
GetChildrenCount() const1504 int32_t GridPattern::GetChildrenCount() const
1505 {
1506     return gridLayoutInfo_.childrenCount_;
1507 }
1508 
ClearDragState()1509 void GridPattern::ClearDragState()
1510 {
1511     gridLayoutInfo_.ClearDragState();
1512     MarkDirtyNodeSelf();
1513 }
1514 
UpdateRectOfDraggedInItem(int32_t insertIndex)1515 void GridPattern::UpdateRectOfDraggedInItem(int32_t insertIndex)
1516 {
1517     auto host = GetHost();
1518     CHECK_NULL_VOID(host);
1519     std::list<RefPtr<FrameNode>> children;
1520     host->GenerateOneDepthAllFrame(children);
1521     for (const auto& item : children) {
1522         auto itemPattern = item->GetPattern<GridItemPattern>();
1523         CHECK_NULL_VOID(itemPattern);
1524         auto itemProperty = itemPattern->GetLayoutProperty<GridItemLayoutProperty>();
1525         CHECK_NULL_VOID(itemProperty);
1526         auto mainIndex = itemProperty->GetMainIndex().value_or(-1);
1527         auto crossIndex = itemProperty->GetCrossIndex().value_or(-1);
1528         if (mainIndex * gridLayoutInfo_.crossCount_ + crossIndex == insertIndex) {
1529             auto size = item->GetRenderContext()->GetPaintRectWithTransform();
1530             size.SetOffset(item->GetTransformRelativeOffset());
1531             gridLayoutInfo_.currentRect_ = size;
1532             break;
1533         }
1534     }
1535 }
1536 
MoveItems(int32_t itemIndex,int32_t insertIndex)1537 void GridPattern::MoveItems(int32_t itemIndex, int32_t insertIndex)
1538 {
1539     if (insertIndex < 0 ||
1540         insertIndex >= ((itemIndex == -1) ? (gridLayoutInfo_.childrenCount_ + 1) : gridLayoutInfo_.childrenCount_)) {
1541         return;
1542     }
1543 
1544     if (itemIndex == -1) {
1545         UpdateRectOfDraggedInItem(insertIndex);
1546     }
1547 
1548     gridLayoutInfo_.SwapItems(itemIndex, insertIndex);
1549 
1550     auto host = GetHost();
1551     CHECK_NULL_VOID(host);
1552     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1553     auto pipeline = PipelineContext::GetCurrentContext();
1554     if (pipeline) {
1555         pipeline->FlushUITasks();
1556     }
1557 }
1558 
IsOutOfBoundary(bool)1559 bool GridPattern::IsOutOfBoundary(bool /*useCurrentDelta*/)
1560 {
1561     const bool scrollable = GetAlwaysEnabled() || (gridLayoutInfo_.startIndex_ > 0) ||
1562                             (gridLayoutInfo_.endIndex_ < gridLayoutInfo_.childrenCount_ - 1) ||
1563                             GreatNotEqual(gridLayoutInfo_.totalHeightOfItemsInView_, gridLayoutInfo_.lastMainSize_);
1564     return scrollable &&
1565            (gridLayoutInfo_.IsOutOfStart() || gridLayoutInfo_.IsOutOfEnd(GetMainGap(), UseIrregularLayout()));
1566 }
1567 
GetEndOffset()1568 float GridPattern::GetEndOffset()
1569 {
1570     auto& info = gridLayoutInfo_;
1571     float contentHeight = info.lastMainSize_ - info.contentEndPadding_;
1572     float mainGap = GetMainGap();
1573     bool regular = !UseIrregularLayout();
1574     float heightInView = info.GetTotalHeightOfItemsInView(mainGap, regular);
1575 
1576     if (GetAlwaysEnabled() && info.HeightSumSmaller(contentHeight, mainGap)) {
1577         // overScroll with contentHeight < viewport
1578         if (!regular) {
1579             return info.GetHeightInRange(0, info.startMainLineIndex_, mainGap);
1580         }
1581         float totalHeight = info.GetTotalLineHeight(mainGap);
1582         return totalHeight - heightInView;
1583     }
1584 
1585     if (regular) {
1586         return contentHeight - heightInView;
1587     }
1588     float disToBot = gridLayoutInfo_.GetDistanceToBottom(contentHeight, heightInView, mainGap);
1589     return gridLayoutInfo_.currentOffset_ - disToBot;
1590 }
1591 
SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect> & scrollEffect)1592 void GridPattern::SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect>& scrollEffect)
1593 {
1594     scrollEffect->SetCurrentPositionCallback([weak = AceType::WeakClaim(this)]() -> double {
1595         auto grid = weak.Upgrade();
1596         CHECK_NULL_RETURN(grid, 0.0);
1597         if (!grid->gridLayoutInfo_.synced_) {
1598             grid->SyncLayoutBeforeSpring();
1599         }
1600         return grid->gridLayoutInfo_.currentOffset_;
1601     });
1602     scrollEffect->SetLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
1603         auto grid = weak.Upgrade();
1604         CHECK_NULL_RETURN(grid, 0.0);
1605         return grid->GetEndOffset();
1606     });
1607     scrollEffect->SetTrailingCallback([]() -> double { return 0.0; });
1608     scrollEffect->SetInitLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
1609         auto grid = weak.Upgrade();
1610         CHECK_NULL_RETURN(grid, 0.0);
1611         return grid->GetEndOffset();
1612     });
1613     scrollEffect->SetInitTrailingCallback([]() -> double { return 0.0; });
1614 }
1615 
SyncLayoutBeforeSpring()1616 void GridPattern::SyncLayoutBeforeSpring()
1617 {
1618     auto& info = gridLayoutInfo_;
1619     if (info.synced_) {
1620         return;
1621     }
1622     if (!UseIrregularLayout()) {
1623         const float delta = info.currentOffset_ - info.prevOffset_;
1624         if (!info.lineHeightMap_.empty() && LessOrEqual(delta, -info.lastMainSize_)) {
1625             // old layout can't handle large overScroll offset. Avoid by skipping this layout.
1626             // Spring animation plays immediately afterwards, so losing this frame's offset is fine
1627             info.currentOffset_ = info.prevOffset_;
1628             info.synced_ = true;
1629             return;
1630         }
1631     }
1632     auto host = GetHost();
1633     CHECK_NULL_VOID(host);
1634 
1635     forceOverScroll_ = true;
1636     host->SetActive();
1637     auto context = host->GetContext();
1638     if (context) {
1639         context->FlushUITaskWithSingleDirtyNode(host);
1640     }
1641     forceOverScroll_ = false;
1642 }
1643 
GetEndOverScrollIrregular(OverScrollOffset & offset,float delta) const1644 void GridPattern::GetEndOverScrollIrregular(OverScrollOffset& offset, float delta) const
1645 {
1646     const auto& info = gridLayoutInfo_;
1647     float disToBot = info.GetDistanceToBottom(
1648         info.lastMainSize_ - info.contentEndPadding_, info.totalHeightOfItemsInView_, GetMainGap());
1649     if (!info.offsetEnd_) {
1650         offset.end = std::min(0.0f, disToBot + static_cast<float>(delta));
1651     } else if (Negative(delta)) {
1652         offset.end = delta;
1653     } else {
1654         offset.end = std::min(static_cast<float>(delta), -disToBot);
1655     }
1656 }
1657 
GetOverScrollOffset(double delta) const1658 OverScrollOffset GridPattern::GetOverScrollOffset(double delta) const
1659 {
1660     OverScrollOffset offset = { 0, 0 };
1661     if (gridLayoutInfo_.startIndex_ == 0 && gridLayoutInfo_.startMainLineIndex_ == 0) {
1662         auto startPos = gridLayoutInfo_.currentOffset_;
1663         auto newStartPos = startPos + delta;
1664         if (startPos > 0 && newStartPos > 0) {
1665             offset.start = delta;
1666         }
1667         if (startPos > 0 && newStartPos <= 0) {
1668             offset.start = -startPos;
1669         }
1670         if (startPos <= 0 && newStartPos > 0) {
1671             offset.start = newStartPos;
1672         }
1673     }
1674     if (UseIrregularLayout()) {
1675         GetEndOverScrollIrregular(offset, static_cast<float>(delta));
1676         return offset;
1677     }
1678     if (gridLayoutInfo_.endIndex_ == gridLayoutInfo_.childrenCount_ - 1) {
1679         float endPos = gridLayoutInfo_.currentOffset_ + gridLayoutInfo_.totalHeightOfItemsInView_;
1680         float mainSize = gridLayoutInfo_.lastMainSize_ - gridLayoutInfo_.contentEndPadding_;
1681         if (GreatNotEqual(
1682                 GetMainContentSize(), gridLayoutInfo_.currentOffset_ + gridLayoutInfo_.totalHeightOfItemsInView_)) {
1683             endPos = gridLayoutInfo_.currentOffset_ + GetMainContentSize();
1684         }
1685         float newEndPos = endPos + delta;
1686         if (endPos < mainSize && newEndPos < mainSize) {
1687             offset.end = delta;
1688         }
1689         if (endPos < mainSize && newEndPos >= mainSize) {
1690             offset.end = mainSize - endPos;
1691         }
1692         if (endPos >= mainSize && newEndPos < mainSize) {
1693             offset.end = newEndPos - mainSize;
1694         }
1695     }
1696     return offset;
1697 }
1698 
DumpAdvanceInfo()1699 void GridPattern::DumpAdvanceInfo()
1700 {
1701     auto property = GetLayoutProperty<GridLayoutProperty>();
1702     CHECK_NULL_VOID(property);
1703     ScrollablePattern::DumpAdvanceInfo();
1704     if (!property->HasLayoutOptions()) {
1705         DumpLog::GetInstance().AddDesc("GridLayoutOptions:null");
1706     } else {
1707         DumpLog::GetInstance().AddDesc("GridLayoutOptions:true");
1708         DumpLog::GetInstance().AddDesc(GetIrregularIndexesString());
1709     }
1710     supportAnimation_ ? DumpLog::GetInstance().AddDesc("supportAnimation:true")
1711                       : DumpLog::GetInstance().AddDesc("supportAnimation:false");
1712     isConfigScrollable_ ? DumpLog::GetInstance().AddDesc("isConfigScrollable:true")
1713                         : DumpLog::GetInstance().AddDesc("isConfigScrollable:false");
1714     gridLayoutInfo_.lastCrossCount_.has_value()
1715         ? DumpLog::GetInstance().AddDesc("lastCrossCount:" + std::to_string(gridLayoutInfo_.lastCrossCount_.value()))
1716         : DumpLog::GetInstance().AddDesc("lastCrossCount:null");
1717     gridLayoutInfo_.reachEnd_ ? DumpLog::GetInstance().AddDesc("reachEnd:true")
1718                               : DumpLog::GetInstance().AddDesc("reachEnd:false");
1719     gridLayoutInfo_.reachStart_ ? DumpLog::GetInstance().AddDesc("reachStart:true")
1720                                 : DumpLog::GetInstance().AddDesc("reachStart:false");
1721     gridLayoutInfo_.offsetEnd_ ? DumpLog::GetInstance().AddDesc("offsetEnd:true")
1722                                : DumpLog::GetInstance().AddDesc("offsetEnd:false");
1723     gridLayoutInfo_.hasBigItem_ ? DumpLog::GetInstance().AddDesc("hasBigItem:true")
1724                                 : DumpLog::GetInstance().AddDesc("hasBigItem:false");
1725     gridLayoutInfo_.synced_ ? DumpLog::GetInstance().AddDesc("synced:true")
1726                             : DumpLog::GetInstance().AddDesc("synced:false");
1727     DumpLog::GetInstance().AddDesc("scrollStop:" + std::to_string(scrollStop_));
1728     DumpLog::GetInstance().AddDesc("prevHeight:" + std::to_string(gridLayoutInfo_.prevHeight_));
1729     DumpLog::GetInstance().AddDesc("currentHeight:" + std::to_string(gridLayoutInfo_.currentHeight_));
1730     DumpLog::GetInstance().AddDesc("endHeight:" + std::to_string(endHeight_));
1731     DumpLog::GetInstance().AddDesc("currentOffset:" + std::to_string(gridLayoutInfo_.currentOffset_));
1732     DumpLog::GetInstance().AddDesc("prevOffset:" + std::to_string(gridLayoutInfo_.prevOffset_));
1733     DumpLog::GetInstance().AddDesc("lastMainSize:" + std::to_string(gridLayoutInfo_.lastMainSize_));
1734     DumpLog::GetInstance().AddDesc(
1735         "totalHeightOfItemsInView:" + std::to_string(gridLayoutInfo_.totalHeightOfItemsInView_));
1736     DumpLog::GetInstance().AddDesc("startIndex:" + std::to_string(gridLayoutInfo_.startIndex_));
1737     DumpLog::GetInstance().AddDesc("endIndex:" + std::to_string(gridLayoutInfo_.endIndex_));
1738     DumpLog::GetInstance().AddDesc("jumpIndex:" + std::to_string(gridLayoutInfo_.jumpIndex_));
1739     DumpLog::GetInstance().AddDesc("crossCount:" + std::to_string(gridLayoutInfo_.crossCount_));
1740     DumpLog::GetInstance().AddDesc("childrenCount:" + std::to_string(gridLayoutInfo_.childrenCount_));
1741     DumpLog::GetInstance().AddDesc("RowsTemplate:", property->GetRowsTemplate()->c_str());
1742     DumpLog::GetInstance().AddDesc("ColumnsTemplate:", property->GetColumnsTemplate()->c_str());
1743     property->GetCachedCount().has_value()
1744         ? DumpLog::GetInstance().AddDesc("CachedCount:" + std::to_string(property->GetCachedCount().value()))
1745         : DumpLog::GetInstance().AddDesc("CachedCount:null");
1746     property->GetMaxCount().has_value()
1747         ? DumpLog::GetInstance().AddDesc("MaxCount:" + std::to_string(property->GetMaxCount().value()))
1748         : DumpLog::GetInstance().AddDesc("MaxCount:null");
1749     property->GetMinCount().has_value()
1750         ? DumpLog::GetInstance().AddDesc("MinCount:" + std::to_string(property->GetMinCount().value()))
1751         : DumpLog::GetInstance().AddDesc("MinCount:null");
1752     property->GetCellLength().has_value()
1753         ? DumpLog::GetInstance().AddDesc("CellLength:" + std::to_string(property->GetCellLength().value()))
1754         : DumpLog::GetInstance().AddDesc("CellLength:null");
1755     property->GetEditable().has_value()
1756         ? DumpLog::GetInstance().AddDesc("Editable:" + std::to_string(property->GetEditable().value()))
1757         : DumpLog::GetInstance().AddDesc("Editable:null");
1758     property->GetScrollEnabled().has_value()
1759         ? DumpLog::GetInstance().AddDesc("ScrollEnabled:" + std::to_string(property->GetScrollEnabled().value()))
1760         : DumpLog::GetInstance().AddDesc("ScrollEnabled:null");
1761     switch (property->GetAlignItems().value_or(GridItemAlignment::DEFAULT)) {
1762         case GridItemAlignment::STRETCH: {
1763             DumpLog::GetInstance().AddDesc("AlignItems:GridItemAlignment.STRETCH");
1764             break;
1765         }
1766         default: {
1767             DumpLog::GetInstance().AddDesc("AlignItems:GridItemAlignment.DEFAULT");
1768             break;
1769         }
1770     }
1771     switch (gridLayoutInfo_.scrollAlign_) {
1772         case ScrollAlign::NONE: {
1773             DumpLog::GetInstance().AddDesc("ScrollAlign:NONE");
1774             break;
1775         }
1776         case ScrollAlign::CENTER: {
1777             DumpLog::GetInstance().AddDesc("ScrollAlign:CENTER");
1778             break;
1779         }
1780         case ScrollAlign::END: {
1781             DumpLog::GetInstance().AddDesc("ScrollAlign:END");
1782             break;
1783         }
1784         case ScrollAlign::START: {
1785             DumpLog::GetInstance().AddDesc("ScrollAlign:START");
1786             break;
1787         }
1788         case ScrollAlign::AUTO: {
1789             DumpLog::GetInstance().AddDesc("ScrollAlign:AUTO");
1790             break;
1791         }
1792         default: {
1793             break;
1794         }
1795     }
1796     if (!gridLayoutInfo_.gridMatrix_.empty()) {
1797         DumpLog::GetInstance().AddDesc("-----------start print gridMatrix------------");
1798         std::string res = std::string("");
1799         for (auto item : gridLayoutInfo_.gridMatrix_) {
1800             res.append(std::to_string(item.first));
1801             res.append(": ");
1802             for (auto index : item.second) {
1803                 res.append("[")
1804                     .append(std::to_string(index.first))
1805                     .append(",")
1806                     .append(std::to_string(index.second))
1807                     .append("] ");
1808             }
1809             DumpLog::GetInstance().AddDesc(res);
1810             res.clear();
1811         }
1812         DumpLog::GetInstance().AddDesc("-----------end print gridMatrix------------");
1813     }
1814     if (!gridLayoutInfo_.lineHeightMap_.empty()) {
1815         DumpLog::GetInstance().AddDesc("-----------start print lineHeightMap------------");
1816         for (auto item : gridLayoutInfo_.lineHeightMap_) {
1817             DumpLog::GetInstance().AddDesc(std::to_string(item.first).append(" :").append(std::to_string(item.second)));
1818         }
1819         DumpLog::GetInstance().AddDesc("-----------end print lineHeightMap------------");
1820     }
1821     if (!gridLayoutInfo_.irregularItemsPosition_.empty()) {
1822         DumpLog::GetInstance().AddDesc("-----------start print irregularItemsPosition_------------");
1823         for (auto item : gridLayoutInfo_.irregularItemsPosition_) {
1824             DumpLog::GetInstance().AddDesc(std::to_string(item.first).append(" :").append(std::to_string(item.second)));
1825         }
1826         DumpLog::GetInstance().AddDesc("-----------end print irregularItemsPosition_------------");
1827     }
1828 }
1829 
GetIrregularIndexesString() const1830 std::string GridPattern::GetIrregularIndexesString() const
1831 {
1832     auto property = GetLayoutProperty<GridLayoutProperty>();
1833     if (!property || !property->HasLayoutOptions()) {
1834         return std::string("");
1835     }
1836     const auto& options = *property->GetLayoutOptions();
1837     if (options.irregularIndexes.empty()) {
1838         return std::string("");
1839     }
1840     std::string irregularIndexes = std::string("IrregularIndexes: [");
1841     int count = 0;
1842     for (const auto& index : options.irregularIndexes) {
1843         if (count > 0) {
1844             irregularIndexes.append(", ");
1845         }
1846         irregularIndexes.append(std::to_string(index));
1847         count++;
1848         if (count == MAX_NUM_SIZE) {
1849             irregularIndexes.append("...");
1850             break;
1851         }
1852     }
1853     irregularIndexes.append("]");
1854     return irregularIndexes;
1855 }
1856 
ProvideRestoreInfo()1857 std::string GridPattern::ProvideRestoreInfo()
1858 {
1859     return std::to_string(gridLayoutInfo_.startIndex_);
1860 }
1861 
OnRestoreInfo(const std::string & restoreInfo)1862 void GridPattern::OnRestoreInfo(const std::string& restoreInfo)
1863 {
1864     gridLayoutInfo_.jumpIndex_ = StringUtils::StringToInt(restoreInfo);
1865     gridLayoutInfo_.scrollAlign_ = ScrollAlign::START;
1866 }
1867 
GetItemRect(int32_t index) const1868 Rect GridPattern::GetItemRect(int32_t index) const
1869 {
1870     if (index < 0 || index < gridLayoutInfo_.startIndex_ || index > gridLayoutInfo_.endIndex_) {
1871         return Rect();
1872     }
1873     auto host = GetHost();
1874     CHECK_NULL_RETURN(host, Rect());
1875     auto item = host->GetChildByIndex(index);
1876     CHECK_NULL_RETURN(item, Rect());
1877     auto itemGeometry = item->GetGeometryNode();
1878     CHECK_NULL_RETURN(itemGeometry, Rect());
1879     return Rect(itemGeometry->GetFrameRect().GetX(), itemGeometry->GetFrameRect().GetY(),
1880         itemGeometry->GetFrameRect().Width(), itemGeometry->GetFrameRect().Height());
1881 }
1882 
ScrollToIndex(int32_t index,bool smooth,ScrollAlign align,std::optional<float> extraOffset)1883 void GridPattern::ScrollToIndex(int32_t index, bool smooth, ScrollAlign align, std::optional<float> extraOffset)
1884 {
1885     SetScrollSource(SCROLL_FROM_JUMP);
1886     StopAnimate();
1887     auto host = GetHost();
1888     CHECK_NULL_VOID(host);
1889     int32_t totalChildCount = host->TotalChildCount();
1890     if (((index >= 0) && (index < totalChildCount)) || (index == LAST_ITEM)) {
1891         if (extraOffset.has_value()) {
1892             gridLayoutInfo_.extraOffset_ = -extraOffset.value();
1893         }
1894         if (smooth) {
1895             SetExtraOffset(extraOffset);
1896             targetIndex_ = index;
1897             scrollAlign_ = align;
1898             host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1899         } else {
1900             UpdateStartIndex(index, align);
1901         }
1902     }
1903     FireAndCleanScrollingListener();
1904 }
1905 
ScrollToEdge(ScrollEdgeType scrollEdgeType,bool smooth)1906 void GridPattern::ScrollToEdge(ScrollEdgeType scrollEdgeType, bool smooth)
1907 {
1908     if (UseIrregularLayout() && scrollEdgeType == ScrollEdgeType::SCROLL_BOTTOM) {
1909         ScrollToIndex(LAST_ITEM, smooth);
1910         // for irregular layout, last item might not be at bottom
1911         gridLayoutInfo_.jumpIndex_ = JUMP_TO_BOTTOM_EDGE;
1912         auto host = GetHost();
1913         CHECK_NULL_VOID(host);
1914         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1915         return;
1916     }
1917     ScrollablePattern::ScrollToEdge(scrollEdgeType, smooth);
1918 }
1919 
1920 // Turn on the scrolling animation
AnimateToTarget(ScrollAlign align,const RefPtr<LayoutAlgorithmWrapper> & algo)1921 void GridPattern::AnimateToTarget(ScrollAlign align, const RefPtr<LayoutAlgorithmWrapper>& algo)
1922 {
1923     if (targetIndex_.has_value()) {
1924         AnimateToTargetImpl(align, algo);
1925         targetIndex_.reset();
1926     }
1927 }
1928 
1929 // scroll to the item where the index is located
AnimateToTargetImpl(ScrollAlign align,const RefPtr<LayoutAlgorithmWrapper> & algo)1930 bool GridPattern::AnimateToTargetImpl(ScrollAlign align, const RefPtr<LayoutAlgorithmWrapper>& algo)
1931 {
1932     const float mainGap = GetMainGap();
1933     float targetPos = 0.0f;
1934     auto host = GetHost();
1935     CHECK_NULL_RETURN(host, false);
1936     auto&& extraOffset = GetExtraOffset();
1937     bool success = true;
1938     if (UseIrregularLayout()) {
1939         auto host = GetHost();
1940         CHECK_NULL_RETURN(host, false);
1941         auto size = GridLayoutUtils::GetItemSize(&gridLayoutInfo_, RawPtr(host), *targetIndex_);
1942         targetPos = gridLayoutInfo_.GetAnimatePosIrregular(*targetIndex_, size.rows, align, mainGap);
1943         if (Negative(targetPos)) {
1944             success = false;
1945         }
1946     } else {
1947         auto gridScrollLayoutAlgorithm = DynamicCast<GridScrollLayoutAlgorithm>(algo->GetLayoutAlgorithm());
1948         scrollGridLayoutInfo_ = gridScrollLayoutAlgorithm->GetScrollGridLayoutInfo();
1949         // Based on the index, align gets the position to scroll to
1950         success = scrollGridLayoutInfo_.GetGridItemAnimatePos(
1951             gridLayoutInfo_, *targetIndex_, align, mainGap, targetPos);
1952     }
1953     if (!success) {
1954         if (NearZero(extraOffset.value_or(0.0f))) {
1955             return false;
1956         }
1957         targetPos = GetTotalOffset();
1958     }
1959 
1960     isSmoothScrolling_ = true;
1961     if (extraOffset.has_value()) {
1962         ACE_SCOPED_TRACE(
1963             "AnimateToTargetImpl, success:%u, targetPos:%f, extraOffset:%f", success, targetPos, *extraOffset);
1964         targetPos += *extraOffset;
1965         ResetExtraOffset();
1966     } else {
1967         ACE_SCOPED_TRACE("AnimateToTargetImpl, targetPos:%f", targetPos);
1968     }
1969     AnimateTo(targetPos, -1, nullptr, true);
1970     return true;
1971 }
1972 
GetVisibleSelectedItems()1973 std::vector<RefPtr<FrameNode>> GridPattern::GetVisibleSelectedItems()
1974 {
1975     std::vector<RefPtr<FrameNode>> children;
1976     auto host = GetHost();
1977     CHECK_NULL_RETURN(host, children);
1978     for (int32_t index = gridLayoutInfo_.startIndex_; index <= gridLayoutInfo_.endIndex_; ++index) {
1979         auto item = host->GetChildByIndex(index);
1980         if (!AceType::InstanceOf<FrameNode>(item)) {
1981             continue;
1982         }
1983         auto itemFrameNode = AceType::DynamicCast<FrameNode>(item);
1984         auto itemPattern = itemFrameNode->GetPattern<GridItemPattern>();
1985         if (!itemPattern) {
1986             continue;
1987         }
1988         if (!itemPattern->IsSelected()) {
1989             continue;
1990         }
1991         children.emplace_back(itemFrameNode);
1992     }
1993     return children;
1994 }
1995 
StopAnimate()1996 void GridPattern::StopAnimate()
1997 {
1998     ScrollablePattern::StopAnimate();
1999     isSmoothScrolling_ = false;
2000 }
2001 
IsPredictOutOfRange(int32_t index) const2002 bool GridPattern::IsPredictOutOfRange(int32_t index) const
2003 {
2004     CHECK_NULL_RETURN(gridLayoutInfo_.reachEnd_, false);
2005     auto host = GetHost();
2006     CHECK_NULL_RETURN(host, true);
2007     auto gridLayoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
2008     CHECK_NULL_RETURN(gridLayoutProperty, true);
2009     auto cacheCount = gridLayoutProperty->GetCachedCountValue(gridLayoutInfo_.defCachedCount_) *
2010         gridLayoutInfo_.crossCount_;
2011     return index < gridLayoutInfo_.startIndex_ - cacheCount || index > gridLayoutInfo_.endIndex_ + cacheCount;
2012 }
2013 
UseIrregularLayout() const2014 inline bool GridPattern::UseIrregularLayout() const
2015 {
2016     return irregular_ || (SystemProperties::GetGridIrregularLayoutEnabled() &&
2017                              GetLayoutProperty<GridLayoutProperty>()->HasLayoutOptions());
2018 }
2019 
IsReverse() const2020 bool GridPattern::IsReverse() const
2021 {
2022     auto host = GetHost();
2023     CHECK_NULL_RETURN(host, false);
2024     auto gridLayoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
2025     CHECK_NULL_RETURN(gridLayoutProperty, false);
2026     return gridLayoutProperty->IsReverse();
2027 }
2028 } // namespace OHOS::Ace::NG
2029