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