• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/grid/grid_pattern.h"
17 
18 #include "base/utils/system_properties.h"
19 #include "base/log/dump_log.h"
20 #include "base/perfmonitor/perf_constants.h"
21 #include "base/perfmonitor/perf_monitor.h"
22 #include "core/components_ng/base/observer_handler.h"
23 #include "core/components_ng/pattern/grid/grid_adaptive/grid_adaptive_layout_algorithm.h"
24 #include "core/components_ng/pattern/grid/grid_layout/grid_layout_algorithm.h"
25 #include "core/components_ng/pattern/grid/grid_paint_method.h"
26 #include "core/components_ng/pattern/grid/grid_scroll/grid_scroll_with_options_layout_algorithm.h"
27 #include "core/components_ng/pattern/grid/grid_utils.h"
28 #include "core/components_ng/pattern/grid/irregular/grid_irregular_layout_algorithm.h"
29 #include "core/components_ng/pattern/grid/irregular/grid_layout_utils.h"
30 #include "core/components_ng/pattern/scrollable/scrollable_pattern.h"
31 #include "core/components_ng/syntax/repeat_virtual_scroll_2_node.h"
32 #include "interfaces/inner_api/ui_session/ui_session_manager.h"
33 #include "core/components_ng/manager/scroll_adjust/scroll_adjust_manager.h"
34 
35 namespace OHOS::Ace::NG {
36 
37 namespace {
38 const Color ITEM_FILL_COLOR = Color::TRANSPARENT;
39 
40 const int32_t MAX_NUM_SIZE = 4;
41 } // namespace
42 
CreateLayoutAlgorithm()43 RefPtr<LayoutAlgorithm> GridPattern::CreateLayoutAlgorithm()
44 {
45     auto gridLayoutProperty = GetLayoutProperty<GridLayoutProperty>();
46     CHECK_NULL_RETURN(gridLayoutProperty, nullptr);
47     std::vector<std::string> cols;
48     StringUtils::StringSplitter(gridLayoutProperty->GetColumnsTemplate().value_or(""), ' ', cols);
49     std::vector<std::string> rows;
50     StringUtils::StringSplitter(gridLayoutProperty->GetRowsTemplate().value_or(""), ' ', rows);
51 
52     // When rowsTemplate and columnsTemplate is both not setting, use adaptive layout algorithm.
53     if (rows.empty() && cols.empty()) {
54         return MakeRefPtr<GridAdaptiveLayoutAlgorithm>(info_);
55     }
56 
57     auto crossCount = cols.empty() ? Infinity<int32_t>() : static_cast<int32_t>(cols.size());
58     auto mainCount = rows.empty() ? Infinity<int32_t>() : static_cast<int32_t>(rows.size());
59     if (!gridLayoutProperty->IsVertical()) {
60         std::swap(crossCount, mainCount);
61     }
62     info_.crossCount_ = crossCount;
63     if (targetIndex_.has_value()) {
64         info_.targetIndex_ = targetIndex_;
65     }
66     // When rowsTemplate and columnsTemplate is both setting, use static layout algorithm.
67     if (!rows.empty() && !cols.empty()) {
68         return MakeRefPtr<GridLayoutAlgorithm>(info_, crossCount, mainCount);
69     }
70 
71     // If only set one of rowTemplate and columnsTemplate, use scrollable layout algorithm.
72     const bool disableSkip = IsOutOfBoundary(true) || (ScrollablePattern::AnimateRunning() && !IsBackToTopRunning());
73     const bool canOverScrollStart = CanOverScrollStart(GetScrollSource()) || preSpring_;
74     const bool canOverScrollEnd = CanOverScrollEnd(GetScrollSource()) || preSpring_;
75     if (UseIrregularLayout()) {
76         auto algo = MakeRefPtr<GridIrregularLayoutAlgorithm>(
77             info_, canOverScrollStart, canOverScrollEnd && (info_.repeatDifference_ == 0));
78         algo->SetEnableSkip(!disableSkip);
79         return algo;
80     }
81     RefPtr<GridScrollLayoutAlgorithm> result;
82     if (!gridLayoutProperty->GetLayoutOptions().has_value()) {
83         result = MakeRefPtr<GridScrollLayoutAlgorithm>(info_, crossCount, mainCount);
84     } else {
85         result = MakeRefPtr<GridScrollWithOptionsLayoutAlgorithm>(info_, crossCount, mainCount);
86     }
87     result->SetCanOverScrollStart(canOverScrollStart);
88     result->SetCanOverScrollEnd(canOverScrollEnd && (info_.repeatDifference_ == 0));
89     result->SetScrollSource(GetScrollSource());
90     if (ScrollablePattern::AnimateRunning()) {
91         result->SetLineSkipping(!disableSkip);
92     }
93     return result;
94 }
95 
BeforeCreateLayoutWrapper()96 void GridPattern::BeforeCreateLayoutWrapper()
97 {
98     auto host = GetHost();
99     CHECK_NULL_VOID(host);
100     info_.repeatDifference_ = 0;
101     info_.firstRepeatCount_ = 0;
102     info_.childrenCount_ = 0;
103     GetRepeatCountInfo(host, info_.repeatDifference_, info_.firstRepeatCount_, info_.childrenCount_);
104 }
105 
CreatePaintProperty()106 RefPtr<PaintProperty> GridPattern::CreatePaintProperty()
107 {
108     auto defaultDisplayMode = GetDefaultScrollBarDisplayMode();
109     auto property = MakeRefPtr<GridPaintProperty>();
110     property->UpdateScrollBarMode(defaultDisplayMode);
111     return property;
112 }
113 
CreateNodePaintMethod()114 RefPtr<NodePaintMethod> GridPattern::CreateNodePaintMethod()
115 {
116     auto paint = MakeRefPtr<GridPaintMethod>(GetAxis() == Axis::HORIZONTAL, IsReverse(), GetScrollBar());
117     CHECK_NULL_RETURN(paint, nullptr);
118     paint->SetScrollBarOverlayModifier(GetScrollBarOverlayModifier());
119     auto scrollEffect = GetScrollEdgeEffect();
120     if (scrollEffect && scrollEffect->IsFadeEffect()) {
121         paint->SetEdgeEffect(scrollEffect);
122     }
123     if (!gridContentModifier_) {
124         gridContentModifier_ = AceType::MakeRefPtr<GridContentModifier>();
125     }
126     paint->SetContentModifier(gridContentModifier_);
127     UpdateFadingEdge(paint);
128     return paint;
129 }
130 
OnModifyDone()131 void GridPattern::OnModifyDone()
132 {
133     Pattern::OnModifyDone();
134     auto gridLayoutProperty = GetLayoutProperty<GridLayoutProperty>();
135     CHECK_NULL_VOID(gridLayoutProperty);
136 
137     if (multiSelectable_ && !isMouseEventInit_) {
138         InitMouseEvent();
139     }
140 
141     if (!multiSelectable_ && isMouseEventInit_) {
142         UninitMouseEvent();
143     }
144 
145     info_.axis_ = gridLayoutProperty->IsVertical() ? Axis::VERTICAL : Axis::HORIZONTAL;
146     isConfigScrollable_ = gridLayoutProperty->IsConfiguredScrollable();
147     if (!isConfigScrollable_) {
148         return;
149     }
150     SetAxis(info_.axis_);
151     if (!GetScrollableEvent()) {
152         AddScrollEvent();
153 #ifdef SUPPORT_DIGITAL_CROWN
154         SetDigitalCrownEvent();
155 #endif
156     }
157 
158     SetEdgeEffect();
159 
160     auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
161     CHECK_NULL_VOID(paintProperty);
162     if (paintProperty->GetScrollBarProperty()) {
163         SetScrollBar(paintProperty->GetScrollBarProperty());
164     }
165 
166     auto host = GetHost();
167     CHECK_NULL_VOID(host);
168     auto focusHub = host->GetFocusHub();
169     if (focusHub) {
170         InitOnKeyEvent(focusHub);
171     }
172     SetAccessibilityAction();
173     Register2DragDropManager();
174     auto overlayNode = host->GetOverlayNode();
175     if (!overlayNode && paintProperty->GetFadingEdge().value_or(false)) {
176         CreateAnalyzerOverlay(host);
177     }
178 }
179 
MultiSelectWithoutKeyboard(const RectF & selectedZone)180 void GridPattern::MultiSelectWithoutKeyboard(const RectF& selectedZone)
181 {
182     auto host = GetHost();
183     CHECK_NULL_VOID(host);
184     std::list<RefPtr<FrameNode>> children;
185     host->GenerateOneDepthVisibleFrame(children);
186     for (const auto& itemFrameNode : children) {
187         auto itemEvent = itemFrameNode->GetOrCreateEventHub<EventHub>();
188         CHECK_NULL_VOID(itemEvent);
189         if (!itemEvent->IsEnabled()) {
190             continue;
191         }
192 
193         auto itemPattern = itemFrameNode->GetPattern<GridItemPattern>();
194         CHECK_NULL_VOID(itemPattern);
195         if (!itemPattern->Selectable()) {
196             continue;
197         }
198         auto itemGeometry = itemFrameNode->GetGeometryNode();
199         CHECK_NULL_VOID(itemGeometry);
200         auto context = itemFrameNode->GetRenderContext();
201         CHECK_NULL_VOID(context);
202 
203         auto itemRect = itemGeometry->GetFrameRect();
204         auto iter = itemToBeSelected_.find(itemFrameNode->GetId());
205         if (iter == itemToBeSelected_.end()) {
206             auto result = itemToBeSelected_.emplace(itemFrameNode->GetId(), ItemSelectedStatus());
207             iter = result.first;
208             iter->second.onSelected = itemPattern->GetOrCreateEventHub<GridItemEventHub>()->GetOnSelect();
209             iter->second.selectChangeEvent =
210                 itemPattern->GetOrCreateEventHub<GridItemEventHub>()->GetSelectChangeEvent();
211         }
212         auto startMainOffset = mouseStartOffset_.GetMainOffset(info_.axis_);
213         if (info_.axis_ == Axis::VERTICAL) {
214             iter->second.rect = itemRect + OffsetF(0, totalOffsetOfMousePressed_ - startMainOffset);
215         } else {
216             iter->second.rect = itemRect + OffsetF(totalOffsetOfMousePressed_ - startMainOffset, 0);
217         }
218 
219         if (!selectedZone.IsIntersectWith(itemRect)) {
220             itemPattern->MarkIsSelected(false);
221             iter->second.selected = false;
222             context->OnMouseSelectUpdate(false, ITEM_FILL_COLOR, ITEM_FILL_COLOR);
223         } else {
224             itemPattern->MarkIsSelected(true);
225             iter->second.selected = true;
226             context->OnMouseSelectUpdate(true, ITEM_FILL_COLOR, ITEM_FILL_COLOR);
227         }
228     }
229 
230     DrawSelectedZone(selectedZone);
231 }
232 
ClearMultiSelect()233 void GridPattern::ClearMultiSelect()
234 {
235     auto host = GetHost();
236     CHECK_NULL_VOID(host);
237     std::list<RefPtr<FrameNode>> children;
238     host->GenerateOneDepthAllFrame(children);
239     for (const auto& item : children) {
240         if (!AceType::InstanceOf<FrameNode>(item)) {
241             continue;
242         }
243 
244         auto itemFrameNode = AceType::DynamicCast<FrameNode>(item);
245         auto itemPattern = itemFrameNode->GetPattern<GridItemPattern>();
246         CHECK_NULL_VOID(itemPattern);
247         auto selectedStatus = itemToBeSelected_.find(itemFrameNode->GetId());
248         if (selectedStatus != itemToBeSelected_.end()) {
249             selectedStatus->second.selected = false;
250         }
251         itemPattern->MarkIsSelected(false);
252         auto renderContext = itemFrameNode->GetRenderContext();
253         CHECK_NULL_VOID(renderContext);
254         renderContext->OnMouseSelectUpdate(false, ITEM_FILL_COLOR, ITEM_FILL_COLOR);
255     }
256 
257     ClearSelectedZone();
258 }
259 
IsItemSelected(float offsetX,float offsetY)260 bool GridPattern::IsItemSelected(float offsetX, float offsetY)
261 {
262     auto host = GetHost();
263     CHECK_NULL_RETURN(host, false);
264     auto node = host->FindChildByPosition(offsetX, offsetY);
265     CHECK_NULL_RETURN(node, false);
266     auto itemPattern = node->GetPattern<GridItemPattern>();
267     CHECK_NULL_RETURN(itemPattern, false);
268     return itemPattern->IsSelected();
269 }
270 
FireOnScrollStart(bool withPerfMonitor)271 void GridPattern::FireOnScrollStart(bool withPerfMonitor)
272 {
273     ScrollablePattern::RecordScrollEvent(Recorder::EventType::SCROLL_START);
274     UIObserverHandler::GetInstance().NotifyScrollEventStateChange(
275         AceType::WeakClaim(this), ScrollEventType::SCROLL_START);
276     SuggestOpIncGroup(true);
277     if (withPerfMonitor) {
278         PerfMonitor::GetPerfMonitor()->StartCommercial(PerfConstants::APP_LIST_FLING, PerfActionType::FIRST_MOVE, "");
279     }
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 pipeline = GetContext();
298     if (pipeline) {
299         pipeline->GetFocusManager()->SetNeedTriggerScroll(std::nullopt);
300     }
301     auto host = GetHost();
302     CHECK_NULL_VOID(host);
303     auto hub = host->GetOrCreateEventHub<GridEventHub>();
304     CHECK_NULL_VOID(hub);
305     auto onScrollStart = hub->GetOnScrollStart();
306     if (onScrollStart) {
307         onScrollStart();
308     }
309     auto onJSFrameNodeScrollStart = hub->GetJSFrameNodeOnScrollStart();
310     if (onJSFrameNodeScrollStart) {
311         onJSFrameNodeScrollStart();
312     }
313 }
314 
FireOnReachStart(const OnReachEvent & onReachStart,const OnReachEvent & onJSFrameNodeReachStart)315 void GridPattern::FireOnReachStart(const OnReachEvent& onReachStart, const OnReachEvent& onJSFrameNodeReachStart)
316 {
317     auto host = GetHost();
318     CHECK_NULL_VOID(host);
319     if (info_.startIndex_ != 0) {
320         return;
321     }
322     if (!isInitialized_) {
323         FireObserverOnReachStart();
324         ReportOnItemGridEvent("onReachStart");
325         CHECK_NULL_VOID(onReachStart || onJSFrameNodeReachStart);
326         if (onReachStart) {
327             onReachStart();
328         }
329         if (onJSFrameNodeReachStart) {
330             onJSFrameNodeReachStart();
331         }
332         AddEventsFiredInfo(ScrollableEventType::ON_REACH_START);
333     }
334     auto finalOffset = info_.currentHeight_ - info_.prevHeight_;
335     if (!NearZero(finalOffset)) {
336         bool scrollUpToStart = GreatOrEqual(info_.prevHeight_, 0.0) && LessOrEqual(info_.currentHeight_, 0.0);
337         bool scrollDownToStart = LessNotEqual(info_.prevHeight_, 0.0) && GreatOrEqual(info_.currentHeight_, 0.0);
338         if (scrollUpToStart || scrollDownToStart) {
339             FireObserverOnReachStart();
340             ReportOnItemGridEvent("onReachStart");
341             CHECK_NULL_VOID(onReachStart || onJSFrameNodeReachStart);
342             ACE_SCOPED_TRACE("OnReachStart, scrollUpToStart:%u, scrollDownToStart:%u, id:%d, tag:Grid",
343                 scrollUpToStart, scrollDownToStart, static_cast<int32_t>(host->GetAccessibilityId()));
344             if (onReachStart) {
345                 onReachStart();
346             }
347             if (onJSFrameNodeReachStart) {
348                 onJSFrameNodeReachStart();
349             }
350             AddEventsFiredInfo(ScrollableEventType::ON_REACH_START);
351         }
352     }
353 }
354 
FireOnReachEnd(const OnReachEvent & onReachEnd,const OnReachEvent & onJSFrameNodeReachEnd)355 void GridPattern::FireOnReachEnd(const OnReachEvent& onReachEnd, const OnReachEvent& onJSFrameNodeReachEnd)
356 {
357     auto host = GetHost();
358     CHECK_NULL_VOID(host);
359     auto childrenCount = info_.childrenCount_ + info_.repeatDifference_;
360     if (info_.endIndex_ != (childrenCount - 1)) {
361         return;
362     }
363     if (!isInitialized_) {
364         FireObserverOnReachEnd();
365     }
366     if (!NearZero(mainSizeChanged_)) {
367         info_.prevHeight_ += mainSizeChanged_;
368     }
369     auto finalOffset = info_.currentHeight_ - info_.prevHeight_;
370     if (!NearZero(finalOffset)) {
371         bool scrollDownToEnd =
372             LessNotEqual(info_.prevHeight_, endHeight_) && GreatOrEqual(info_.currentHeight_, endHeight_);
373         bool scrollUpToEnd =
374             GreatNotEqual(info_.prevHeight_, endHeight_) && LessOrEqual(info_.currentHeight_, endHeight_);
375         if (scrollDownToEnd || scrollUpToEnd) {
376             FireObserverOnReachEnd();
377             ReportOnItemGridEvent("onReachEnd");
378             CHECK_NULL_VOID(onReachEnd || onJSFrameNodeReachEnd);
379             ACE_SCOPED_TRACE("OnReachEnd, scrollUpToEnd:%u, scrollDownToEnd:%u, id:%d, tag:Grid", scrollUpToEnd,
380                 scrollDownToEnd, static_cast<int32_t>(host->GetAccessibilityId()));
381             if (onReachEnd) {
382                 onReachEnd();
383             }
384             if (onJSFrameNodeReachEnd) {
385                 onJSFrameNodeReachEnd();
386             }
387             AddEventsFiredInfo(ScrollableEventType::ON_REACH_END);
388         }
389     }
390 }
391 
FireOnScrollIndex(bool indexChanged,const ScrollIndexFunc & onScrollIndex)392 void GridPattern::FireOnScrollIndex(bool indexChanged, const ScrollIndexFunc& onScrollIndex)
393 {
394     CHECK_NULL_VOID(indexChanged && onScrollIndex);
395     int32_t endIndex = info_.endIndex_;
396     if (SystemProperties::IsWhiteBlockEnabled()) {
397         endIndex = ScrollAdjustmanager::GetInstance().AdjustEndIndex(info_.endIndex_);
398     }
399     onScrollIndex(info_.startIndex_, endIndex);
400 }
401 
GetContentSize() const402 SizeF GridPattern::GetContentSize() const
403 {
404     auto host = GetHost();
405     CHECK_NULL_RETURN(host, SizeF());
406     auto geometryNode = host->GetGeometryNode();
407     CHECK_NULL_RETURN(geometryNode, SizeF());
408     return geometryNode->GetPaddingSize();
409 }
410 
GetMainGap() const411 float GridPattern::GetMainGap() const
412 {
413     float mainGap = 0.0;
414     auto host = GetHost();
415     CHECK_NULL_RETURN(host, 0.0);
416     auto geometryNode = host->GetGeometryNode();
417     CHECK_NULL_RETURN(geometryNode, 0.0);
418     auto viewScopeSize = geometryNode->GetPaddingSize();
419     auto layoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
420     mainGap = GridUtils::GetMainGap(layoutProperty, viewScopeSize, info_.axis_);
421     return mainGap;
422 }
423 
IsFadingBottom() const424 bool GridPattern::IsFadingBottom() const
425 {
426     float mainSize = info_.lastMainSize_ - info_.contentEndPadding_;
427     if (info_.startIndex_ == 0 && (info_.endIndex_ == info_.childrenCount_ - 1) &&
428         LessNotEqual(info_.totalHeightOfItemsInView_, mainSize)) {
429         return Positive(info_.currentOffset_);
430     } else {
431         return !info_.offsetEnd_;
432     }
433 }
434 
UpdateCurrentOffset(float offset,int32_t source)435 bool GridPattern::UpdateCurrentOffset(float offset, int32_t source)
436 {
437     if (!isConfigScrollable_ || !scrollable_) {
438         return true;
439     }
440 
441     auto host = GetHost();
442     CHECK_NULL_RETURN(host, false);
443 
444     // check edgeEffect is not springEffect
445     if (!HandleEdgeEffect(offset, source, GetContentSize())) {
446         if (IsOutOfBoundary(true)) {
447             host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
448         }
449         return false;
450     }
451     SetScrollSource(source);
452     FireAndCleanScrollingListener();
453     if (info_.synced_) {
454         info_.prevOffset_ = info_.currentOffset_;
455         info_.synced_ = false;
456     }
457     // When finger moves down, offset is positive.
458     // When finger moves up, offset is negative.
459     bool irregular = UseIrregularLayout();
460     float mainGap = GetMainGap();
461     auto itemsHeight = info_.GetTotalHeightOfItemsInView(mainGap, irregular);
462     float mainContentSize = GetMainContentSize();
463     if (info_.offsetEnd_) {
464         if (source == SCROLL_FROM_UPDATE) {
465             float overScroll = 0.0f;
466             if (GetTotalHeight() <= mainContentSize) {
467                 overScroll = GetTotalOffset();
468             } else if (irregular) {
469                 overScroll = info_.GetDistanceToBottom(mainContentSize, itemsHeight, mainGap);
470             } else {
471                 overScroll = info_.currentOffset_ - (mainContentSize - itemsHeight);
472             }
473             if (!NearZero(mainContentSize)) {
474                 auto friction = CalculateFriction(std::abs(overScroll) / mainContentSize);
475                 offset *= friction;
476             }
477         }
478         auto userOffset = FireOnWillScroll(-offset);
479         userOffset = FireObserverOnWillScroll(userOffset);
480         info_.currentOffset_ -= userOffset;
481         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
482 
483         if (GreatNotEqual(info_.currentOffset_, mainContentSize - itemsHeight)) {
484             info_.offsetEnd_ = false;
485             info_.reachEnd_ = false;
486         }
487 
488         return true;
489     }
490     if (info_.reachStart_) {
491         if (source == SCROLL_FROM_UPDATE) {
492             if (!NearZero(mainContentSize)) {
493                 auto friction = CalculateFriction(std::abs(info_.currentOffset_) / mainContentSize);
494                 offset *= friction;
495             }
496         }
497         auto userOffset = FireOnWillScroll(-offset);
498         userOffset = FireObserverOnWillScroll(userOffset);
499         info_.currentOffset_ -= userOffset;
500         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
501 
502         if (LessNotEqual(info_.currentOffset_, 0.0)) {
503             info_.reachStart_ = false;
504         }
505         return true;
506     }
507     auto userOffset = FireOnWillScroll(-offset);
508     userOffset = FireObserverOnWillScroll(userOffset);
509     info_.currentOffset_ -= userOffset;
510     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
511     ScrollablePattern::MarkScrollBarProxyDirty();
512     return true;
513 }
514 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)515 bool GridPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
516 {
517     if (config.skipMeasure && config.skipLayout) {
518         return false;
519     }
520     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
521     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
522     auto gridLayoutAlgorithm = DynamicCast<GridLayoutBaseAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
523     CHECK_NULL_RETURN(gridLayoutAlgorithm, false);
524     const auto& gridLayoutInfo = gridLayoutAlgorithm->GetGridLayoutInfo();
525     if (!gridLayoutAlgorithm->MeasureInNextFrame()) {
526         auto eventhub = GetOrCreateEventHub<GridEventHub>();
527         CHECK_NULL_RETURN(eventhub, false);
528         Dimension offset(0, DimensionUnit::VP);
529         Dimension offsetPx(gridLayoutInfo.currentOffset_, DimensionUnit::PX);
530         auto offsetVpValue = offsetPx.ConvertToVp();
531         offset.SetValue(offsetVpValue);
532         scrollbarInfo_ = eventhub->FireOnScrollBarUpdate(gridLayoutInfo.startIndex_, offset);
533         if (!isInitialized_ || startIndex_ != gridLayoutInfo.startIndex_) {
534             eventhub->FireOnScrollToIndex(gridLayoutInfo.startIndex_);
535         }
536     }
537 
538     bool offsetEnd = info_.offsetEnd_;
539     mainSizeChanged_ = info_.lastMainSize_ - gridLayoutInfo.lastMainSize_;
540     info_ = gridLayoutInfo;
541     info_.synced_ = true;
542     AnimateToTarget(scrollAlign_, layoutAlgorithmWrapper);
543 
544     info_.reachStart_ = info_.startIndex_ == 0 && GreatOrEqual(info_.currentOffset_, 0.0f);
545 
546     auto curDelta = info_.currentOffset_ - info_.prevOffset_;
547     info_.currentHeight_ = EstimateHeight();
548     bool sizeDiminished =
549         IsOutOfBoundary(true) && !NearZero(curDelta) && (info_.prevHeight_ - info_.currentHeight_ - curDelta > 0.1f);
550 
551     if (info_.offsetEnd_ && (!offsetEnd || !NearZero(mainSizeChanged_))) {
552         endHeight_ = GetTotalHeight() - GetMainContentSize();
553     }
554     if (!gridLayoutAlgorithm->MeasureInNextFrame()) {
555         bool indexChanged = (startIndex_ != info_.startIndex_) || (endIndex_ != info_.endIndex_);
556         ProcessEvent(indexChanged, info_.currentHeight_ - info_.prevHeight_);
557         info_.prevHeight_ = info_.currentHeight_;
558         startIndex_ = info_.startIndex_;
559         endIndex_ = info_.endIndex_;
560         if (isConfigScrollable_) {
561             focusHandler_.ProcessFocusEvent(keyEvent_, indexChanged);
562         }
563     }
564     if (isSmoothScrolling_ && scrollStop_) {
565         isSmoothScrolling_ = false;
566     }
567 
568     info_.extraOffset_.reset();
569     UpdateScrollBarOffset();
570     ChangeAnimateOverScroll();
571     SetScrollSource(SCROLL_FROM_NONE);
572     if (config.frameSizeChange) {
573         if (GetScrollBar() != nullptr) {
574             GetScrollBar()->ScheduleDisappearDelayTask();
575         }
576     }
577     if (!preSpring_ && !GetCanStayOverScroll()) {
578         CheckRestartSpring(sizeDiminished);
579     }
580     CheckScrollable();
581     MarkSelectedItems();
582     ChangeCanStayOverScroll();
583 
584     if (gridLayoutAlgorithm->MeasureInNextFrame()) {
585         ACE_SCOPED_TRACE("Grid MeasureInNextFrame");
586         MarkDirtyNodeSelf();
587     } else {
588         isInitialized_ = true;
589     }
590     auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
591     CHECK_NULL_RETURN(paintProperty, false);
592     return paintProperty->GetFadingEdge().value_or(false) || paintProperty->HasContentClip();
593 }
594 
CheckScrollable()595 void GridPattern::CheckScrollable()
596 {
597     auto host = GetHost();
598     CHECK_NULL_VOID(host);
599     auto gridLayoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
600     CHECK_NULL_VOID(gridLayoutProperty);
601     auto lastScrollable = scrollable_;
602     if (((info_.endIndex_ - info_.startIndex_ + 1) < info_.childrenCount_) ||
603         (GreatNotEqual(info_.GetTotalHeightOfItemsInView(GetMainGap()), GetMainContentSize()))) {
604         scrollable_ = true;
605     } else {
606         scrollable_ = info_.startMainLineIndex_ != 0 || GetAlwaysEnabled();
607     }
608 
609     SetScrollEnabled(scrollable_);
610 
611     if (!gridLayoutProperty->GetScrollEnabled().value_or(scrollable_)) {
612         SetScrollEnabled(false);
613     }
614 
615     if (lastScrollable && !scrollable_) {
616         StopAnimate();
617     }
618 }
619 
ProcessEvent(bool indexChanged,float finalOffset)620 void GridPattern::ProcessEvent(bool indexChanged, float finalOffset)
621 {
622     auto host = GetHost();
623     CHECK_NULL_VOID(host);
624     auto gridEventHub = host->GetOrCreateEventHub<GridEventHub>();
625     CHECK_NULL_VOID(gridEventHub);
626     auto onScroll = gridEventHub->GetOnScroll();
627     PrintOffsetLog(AceLogTag::ACE_GRID, host->GetId(), finalOffset);
628     if (onScroll) {
629         FireOnScroll(finalOffset, onScroll);
630     }
631     FireObserverOnDidScroll(finalOffset);
632     FireObserverOnScrollerAreaChange(finalOffset);
633     auto onDidScroll = gridEventHub->GetOnDidScroll();
634     if (onDidScroll) {
635         FireOnScroll(finalOffset, onDidScroll);
636     }
637     auto onJSFrameNodeDidScroll = gridEventHub->GetJSFrameNodeOnDidScroll();
638     if (onJSFrameNodeDidScroll) {
639         FireOnScroll(finalOffset, onJSFrameNodeDidScroll);
640     }
641     auto onScrollIndex = gridEventHub->GetOnScrollIndex();
642     auto onJsFrameNodeScrollIndex = gridEventHub->GetJSFrameNodeOnGridScrollIndex();
643     FireOnScrollIndex(indexChanged, onScrollIndex);
644     FireOnScrollIndex(indexChanged, onJsFrameNodeScrollIndex);
645     if (indexChanged) {
646         host->OnAccessibilityEvent(AccessibilityEventType::SCROLLING_EVENT, info_.startIndex_, info_.endIndex_);
647     }
648     auto onReachStart = gridEventHub->GetOnReachStart();
649     auto onJSFrameNodeReachStart = gridEventHub->GetJSFrameNodeOnReachStart();
650     FireOnReachStart(onReachStart, onJSFrameNodeReachStart);
651     auto onReachEnd = gridEventHub->GetOnReachEnd();
652     auto onJSFrameNodeReachEnd = gridEventHub->GetJSFrameNodeOnReachEnd();
653     FireOnReachEnd(onReachEnd, onJSFrameNodeReachEnd);
654     auto onScrollStop = gridEventHub->GetOnScrollStop();
655     auto onJSFrameNodeScrollStop = gridEventHub->GetJSFrameNodeOnScrollStop();
656     OnScrollStop(onScrollStop, onJSFrameNodeScrollStop);
657 }
658 
MarkDirtyNodeSelf()659 void GridPattern::MarkDirtyNodeSelf()
660 {
661     auto host = GetHost();
662     CHECK_NULL_VOID(host);
663     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
664 }
665 
OnScrollEndCallback()666 void GridPattern::OnScrollEndCallback()
667 {
668     isSmoothScrolling_ = false;
669     if (AnimateStoped()) {
670         scrollStop_ = true;
671         MarkDirtyNodeSelf();
672     }
673 }
674 
GetFocusNodeIndex(const RefPtr<FocusHub> & focusNode)675 int32_t GridPattern::GetFocusNodeIndex(const RefPtr<FocusHub>& focusNode)
676 {
677     return focusHandler_.GetFocusNodeIndex(focusNode);
678 }
679 
ScrollToFocusNodeIndex(int32_t index)680 void GridPattern::ScrollToFocusNodeIndex(int32_t index)
681 {
682     StopAnimate();
683     UpdateStartIndex(index);
684     auto pipeline = GetContext();
685     if (pipeline) {
686         pipeline->FlushUITasks();
687     }
688     auto tarFocusNodeWeak = focusHandler_.GetChildFocusNodeByIndex(-1, -1, index);
689     auto tarFocusNode = tarFocusNodeWeak.Upgrade();
690     if (tarFocusNode) {
691         tarFocusNode->RequestFocusImmediately();
692     }
693 }
694 
ScrollToNode(const RefPtr<FrameNode> & focusFrameNode)695 bool GridPattern::ScrollToNode(const RefPtr<FrameNode>& focusFrameNode)
696 {
697     CHECK_NULL_RETURN(focusFrameNode, false);
698     auto focusHub = focusFrameNode->GetFocusHub();
699     CHECK_NULL_RETURN(focusHub, false);
700     auto scrollToIndex = focusHandler_.GetFocusNodeIndex(focusHub);
701     if (scrollToIndex < 0) {
702         return false;
703     }
704     StopAnimate();
705     auto ret = UpdateStartIndex(scrollToIndex);
706     auto* pipeline = GetContext();
707     if (pipeline) {
708         pipeline->FlushUITasks();
709     }
710     return ret;
711 }
712 
GetScrollOffsetAbility()713 ScrollOffsetAbility GridPattern::GetScrollOffsetAbility()
714 {
715     return { [wp = WeakClaim(this)](float moveOffset) -> bool {
716                 auto pattern = wp.Upgrade();
717                 CHECK_NULL_RETURN(pattern, false);
718                 pattern->ScrollBy(-moveOffset);
719                 return true;
720             },
721         GetAxis() };
722 }
723 
GetScrollIndexAbility()724 std::function<bool(int32_t)> GridPattern::GetScrollIndexAbility()
725 {
726     return [wp = WeakClaim(this)](int32_t index) -> bool {
727         auto pattern = wp.Upgrade();
728         CHECK_NULL_RETURN(pattern, false);
729         if (index == FocusHub::SCROLL_TO_HEAD) {
730             pattern->ScrollToEdge(ScrollEdgeType::SCROLL_TOP, false);
731         } else if (index == FocusHub::SCROLL_TO_TAIL) {
732             pattern->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false);
733         } else {
734             pattern->UpdateStartIndex(index);
735         }
736         return true;
737     };
738 }
739 
ScrollBy(float offset)740 void GridPattern::ScrollBy(float offset)
741 {
742     StopAnimate();
743     SetIsOverScroll(false);
744     UpdateCurrentOffset(-offset, SCROLL_FROM_JUMP);
745     // AccessibilityEventType::SCROLL_END
746 }
747 
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const748 void GridPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
749 {
750     ScrollablePattern::ToJsonValue(json, filter);
751     /* no fixed attr below, just return */
752     if (filter.IsFastFilter()) {
753         return;
754     }
755     json->PutExtAttr("multiSelectable", multiSelectable_ ? "true" : "false", filter);
756     json->PutExtAttr("supportAnimation", supportAnimation_ ? "true" : "false", filter);
757 }
758 
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)759 void GridPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
760 {
761     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
762         auto pattern = wp.Upgrade();
763         if (pattern) {
764             return pattern->OnKeyEvent(event);
765         }
766         return false;
767     };
768     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
769 }
770 
OnKeyEvent(const KeyEvent & event)771 bool GridPattern::OnKeyEvent(const KeyEvent& event)
772 {
773     if (event.action != KeyAction::DOWN) {
774         return false;
775     }
776     if ((event.code == KeyCode::KEY_PAGE_DOWN) || (event.code == KeyCode::KEY_PAGE_UP)) {
777         ScrollPage(event.code == KeyCode::KEY_PAGE_UP);
778     }
779 
780     if (FocusHub::IsFocusStepKey(event.code)) {
781         if (focusHandler_.ScrollToLastFocusIndex(event.code)) {
782             keyEvent_ = event;
783             return true;
784         }
785     }
786     return false;
787 }
788 
ScrollPage(bool reverse,bool smooth,AccessibilityScrollType scrollType)789 void GridPattern::ScrollPage(bool reverse, bool smooth, AccessibilityScrollType scrollType)
790 {
791     float distance = reverse ? GetMainContentSize() : -GetMainContentSize();
792     if (scrollType == AccessibilityScrollType::SCROLL_HALF) {
793         distance = distance / 2.f;
794     }
795     if (smooth) {
796         float position = -info_.currentHeight_ + distance;
797         ScrollablePattern::AnimateTo(-position, -1, nullptr, true, false, false);
798         return;
799     } else {
800         if (!isConfigScrollable_) {
801             return;
802         }
803         StopAnimate();
804         UpdateCurrentOffset(distance, SCROLL_FROM_JUMP);
805     }
806     // AccessibilityEventType::SCROLL_END
807 }
808 
UpdateStartIndex(int32_t index)809 bool GridPattern::UpdateStartIndex(int32_t index)
810 {
811     if (!isConfigScrollable_) {
812         return false;
813     }
814     auto host = GetHost();
815     CHECK_NULL_RETURN(host, false);
816     info_.jumpIndex_ = index;
817     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
818     // AccessibilityEventType::SCROLL_END
819     SetScrollSource(SCROLL_FROM_JUMP);
820     return true;
821 }
822 
UpdateStartIndex(int32_t index,ScrollAlign align)823 bool GridPattern::UpdateStartIndex(int32_t index, ScrollAlign align)
824 {
825     info_.scrollAlign_ = align;
826     return UpdateStartIndex(index);
827 }
828 
OnAnimateStop()829 void GridPattern::OnAnimateStop()
830 {
831     if (!GetIsDragging() || GetScrollAbort()) {
832         scrollStop_ = true;
833         MarkDirtyNodeSelf();
834     }
835 }
836 
AnimateTo(float position,float duration,const RefPtr<Curve> & curve,bool smooth,bool canOverScroll,bool useTotalOffset)837 void GridPattern::AnimateTo(
838     float position, float duration, const RefPtr<Curve>& curve, bool smooth, bool canOverScroll, bool useTotalOffset)
839 {
840     if (!isConfigScrollable_) {
841         return;
842     }
843     ScrollablePattern::AnimateTo(position, duration, curve, smooth, canOverScroll);
844 }
845 
ScrollTo(float position)846 void GridPattern::ScrollTo(float position)
847 {
848     if (!isConfigScrollable_) {
849         return;
850     }
851     TAG_LOGI(AceLogTag::ACE_GRID, "ScrollTo:%{public}f", position);
852     StopAnimate();
853     SetAnimateCanOverScroll(GetCanStayOverScroll());
854     UpdateCurrentOffset(GetTotalOffset() - position, SCROLL_FROM_JUMP);
855     SetIsOverScroll(GetCanStayOverScroll());
856     // AccessibilityEventType::SCROLL_END
857 }
858 
EstimateHeight() const859 float GridPattern::EstimateHeight() const
860 {
861     if (!isConfigScrollable_) {
862         return 0.0f;
863     }
864     // During the scrolling animation, the exact current position is used. Other times use the estimated location
865     if (isSmoothScrolling_) {
866         const auto* infoPtr = UseIrregularLayout() ? &info_ : infoCopy_.get();
867         CHECK_NULL_RETURN(infoPtr, 0.0f);
868         return infoPtr->GetTotalHeightFromZeroIndex(info_.startMainLineIndex_, GetMainGap()) - info_.currentOffset_;
869     }
870     auto host = GetHost();
871     CHECK_NULL_RETURN(host, 0.0);
872     auto geometryNode = host->GetGeometryNode();
873     CHECK_NULL_RETURN(geometryNode, 0.0);
874     const auto& info = info_;
875     auto viewScopeSize = geometryNode->GetPaddingSize();
876     auto layoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
877     auto mainGap = GridUtils::GetMainGap(layoutProperty, viewScopeSize, info.axis_);
878     if (UseIrregularLayout()) {
879         return info.GetIrregularOffset(mainGap);
880     }
881     if (!layoutProperty->GetLayoutOptions().has_value()) {
882         return info.GetContentOffset(mainGap);
883     }
884 
885     return info.GetContentOffset(layoutProperty->GetLayoutOptions().value(), mainGap);
886 }
887 
GetAverageHeight() const888 float GridPattern::GetAverageHeight() const
889 {
890     auto host = GetHost();
891     CHECK_NULL_RETURN(host, 0.0);
892     auto geometryNode = host->GetGeometryNode();
893     CHECK_NULL_RETURN(geometryNode, 0.0);
894     const auto& info = info_;
895     auto viewScopeSize = geometryNode->GetPaddingSize();
896     auto layoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
897 
898     float heightSum = 0;
899     int32_t itemCount = 0;
900     auto mainGap = GridUtils::GetMainGap(layoutProperty, viewScopeSize, info.axis_);
901     for (const auto& item : info.lineHeightMap_) {
902         auto line = info.gridMatrix_.find(item.first);
903         if (line == info.gridMatrix_.end()) {
904             continue;
905         }
906         if (line->second.empty()) {
907             continue;
908         }
909         auto lineStart = line->second.begin()->second;
910         auto lineEnd = line->second.rbegin()->second;
911         itemCount += (lineEnd - lineStart + 1);
912         heightSum += item.second + mainGap;
913     }
914     if (itemCount == 0) {
915         return 0;
916     }
917     return heightSum / itemCount;
918 }
919 
GetTotalHeight() const920 float GridPattern::GetTotalHeight() const
921 {
922     auto host = GetHost();
923     CHECK_NULL_RETURN(host, 0.0f);
924     auto geometryNode = host->GetGeometryNode();
925     CHECK_NULL_RETURN(geometryNode, 0.0f);
926     auto viewScopeSize = geometryNode->GetPaddingSize();
927     auto props = host->GetLayoutProperty<GridLayoutProperty>();
928     auto mainGap = GridUtils::GetMainGap(props, viewScopeSize, info_.axis_);
929     if (UseIrregularLayout()) {
930         return info_.GetIrregularHeight(mainGap);
931     }
932     if (props->HasLayoutOptions()) {
933         if (info_.IsAllItemsMeasured()) {
934             return info_.GetTotalLineHeight(mainGap);
935         }
936         return info_.GetContentHeight(*props->GetLayoutOptions(), info_.childrenCount_, mainGap);
937     }
938     return info_.GetContentHeight(mainGap);
939 }
940 
UpdateScrollBarOffset()941 void GridPattern::UpdateScrollBarOffset()
942 {
943     CheckScrollBarOff();
944     CHECK_NULL_VOID((GetScrollBar() || GetScrollBarProxy()) && isConfigScrollable_);
945     auto host = GetHost();
946     CHECK_NULL_VOID(host);
947     auto geometryNode = host->GetGeometryNode();
948     CHECK_NULL_VOID(geometryNode);
949     const auto& info = info_;
950     float offset = 0;
951     float estimatedHeight = 0.f;
952     if (scrollbarInfo_.first.has_value() && scrollbarInfo_.second.has_value()) {
953         offset = scrollbarInfo_.first.value();
954         estimatedHeight = scrollbarInfo_.second.value();
955     } else {
956         auto viewScopeSize = geometryNode->GetPaddingSize();
957         auto layoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
958         auto mainGap = GridUtils::GetMainGap(layoutProperty, viewScopeSize, info.axis_);
959         if (UseIrregularLayout()) {
960             offset = info.GetIrregularOffset(mainGap);
961             estimatedHeight = info.GetIrregularHeight(mainGap);
962         } else if (!layoutProperty->GetLayoutOptions().has_value()) {
963             offset = info.GetContentOffset(mainGap);
964             estimatedHeight = info.GetContentHeight(mainGap);
965         } else {
966             auto childrenCount = info_.childrenCount_ + info_.repeatDifference_;
967             offset = info.GetContentOffset(layoutProperty->GetLayoutOptions().value(), mainGap);
968             estimatedHeight = info.GetContentHeight(layoutProperty->GetLayoutOptions().value(), childrenCount, mainGap);
969         }
970     }
971     if (info.startMainLineIndex_ != 0 && info.startIndex_ == 0) {
972         for (int32_t lineIndex = info.startMainLineIndex_ - 1; lineIndex >= 0; lineIndex--) {
973             offset += info.lineHeightMap_.find(lineIndex)->second;
974         }
975     }
976     auto viewSize = geometryNode->GetFrameSize();
977     auto overScroll = 0.0f;
978     if (info_.reachStart_ && Positive(info_.currentOffset_)) {
979         overScroll = info_.currentOffset_;
980     } else {
981         overScroll = info_.lastMainSize_ - estimatedHeight + offset;
982         overScroll = Positive(overScroll) ? overScroll : 0.0f;
983     }
984     if (info_.offsetEnd_ && NearZero(overScroll) && info_.repeatDifference_ == 0) {
985         offset = estimatedHeight - info_.lastMainSize_;
986     }
987     HandleScrollBarOutBoundary(overScroll);
988     UpdateScrollBarRegion(offset, estimatedHeight, Size(viewSize.Width(), viewSize.Height()), Offset(0.0f, 0.0f));
989 }
990 
GetDefaultScrollBarDisplayMode() const991 DisplayMode GridPattern::GetDefaultScrollBarDisplayMode() const
992 {
993     auto defaultDisplayMode = DisplayMode::OFF;
994     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
995         defaultDisplayMode = DisplayMode::AUTO;
996     }
997     return defaultDisplayMode;
998 }
999 
GetOriginalIndex() const1000 int32_t GridPattern::GetOriginalIndex() const
1001 {
1002     return info_.GetOriginalIndex();
1003 }
1004 
GetCrossCount() const1005 int32_t GridPattern::GetCrossCount() const
1006 {
1007     return info_.crossCount_;
1008 }
1009 
GetChildrenCount() const1010 int32_t GridPattern::GetChildrenCount() const
1011 {
1012     return info_.GetChildrenCount();
1013 }
1014 
ClearDragState()1015 void GridPattern::ClearDragState()
1016 {
1017     info_.ClearDragState();
1018     MarkDirtyNodeSelf();
1019 }
1020 
UpdateRectOfDraggedInItem(int32_t insertIndex)1021 void GridPattern::UpdateRectOfDraggedInItem(int32_t insertIndex)
1022 {
1023     auto host = GetHost();
1024     CHECK_NULL_VOID(host);
1025     std::list<RefPtr<FrameNode>> children;
1026     host->GenerateOneDepthAllFrame(children);
1027     for (const auto& item : children) {
1028         auto itemPattern = item->GetPattern<GridItemPattern>();
1029         CHECK_NULL_VOID(itemPattern);
1030         auto itemProperty = itemPattern->GetLayoutProperty<GridItemLayoutProperty>();
1031         CHECK_NULL_VOID(itemProperty);
1032         auto mainIndex = itemProperty->GetMainIndex().value_or(-1);
1033         auto crossIndex = itemProperty->GetCrossIndex().value_or(-1);
1034         if (mainIndex * info_.crossCount_ + crossIndex == insertIndex) {
1035             auto size = item->GetRenderContext()->GetPaintRectWithTransform();
1036             size.SetOffset(item->GetTransformRelativeOffset());
1037             info_.currentRect_ = size;
1038             break;
1039         }
1040     }
1041 }
1042 
MoveItems(int32_t itemIndex,int32_t insertIndex)1043 void GridPattern::MoveItems(int32_t itemIndex, int32_t insertIndex)
1044 {
1045     if (insertIndex < 0 || insertIndex >= ((itemIndex == -1) ? (info_.childrenCount_ + 1) : info_.childrenCount_)) {
1046         return;
1047     }
1048 
1049     if (itemIndex == -1) {
1050         UpdateRectOfDraggedInItem(insertIndex);
1051     }
1052 
1053     info_.SwapItems(itemIndex, insertIndex);
1054 
1055     auto host = GetHost();
1056     CHECK_NULL_VOID(host);
1057     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1058     auto pipeline = GetContext();
1059     if (pipeline) {
1060         pipeline->FlushUITasks();
1061     }
1062 }
1063 
IsOutOfBoundary(bool)1064 bool GridPattern::IsOutOfBoundary(bool /*useCurrentDelta*/)
1065 {
1066     const bool scrollable = GetAlwaysEnabled() || (info_.startIndex_ > 0) ||
1067                             (info_.endIndex_ < info_.childrenCount_ - 1) ||
1068                             GreatNotEqual(info_.totalHeightOfItemsInView_, info_.lastMainSize_);
1069     return scrollable && (info_.IsOutOfStart() || info_.IsOutOfEnd(GetMainGap(), UseIrregularLayout()));
1070 }
1071 
GetEndOffset()1072 float GridPattern::GetEndOffset()
1073 {
1074     auto& info = info_;
1075     float contentHeight = info.lastMainSize_ - info.contentEndPadding_;
1076     const float mainGap = GetMainGap();
1077     const bool irregular = UseIrregularLayout();
1078     float heightInView = info.GetTotalHeightOfItemsInView(mainGap, irregular);
1079 
1080     const float totalHeight = GetTotalHeight();
1081     if (GetAlwaysEnabled() && LessNotEqual(totalHeight, contentHeight)) {
1082         // overScroll with contentHeight < viewport
1083         if (irregular) {
1084             return info.GetHeightInRange(0, info.startMainLineIndex_, mainGap);
1085         }
1086         return totalHeight - heightInView;
1087     }
1088 
1089     if (!irregular) {
1090         return contentHeight - heightInView;
1091     }
1092     float disToBot = info_.GetDistanceToBottom(contentHeight, heightInView, mainGap);
1093     return info_.currentOffset_ - disToBot;
1094 }
1095 
SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect> & scrollEffect)1096 void GridPattern::SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect>& scrollEffect)
1097 {
1098     scrollEffect->SetCurrentPositionCallback([weak = AceType::WeakClaim(this)]() -> double {
1099         auto grid = weak.Upgrade();
1100         CHECK_NULL_RETURN(grid, 0.0);
1101         if (!grid->info_.synced_) {
1102             grid->SyncLayoutBeforeSpring();
1103         }
1104         return grid->info_.currentOffset_;
1105     });
1106     scrollEffect->SetLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
1107         auto grid = weak.Upgrade();
1108         CHECK_NULL_RETURN(grid, 0.0);
1109         return grid->GetEndOffset();
1110     });
1111     scrollEffect->SetTrailingCallback([]() -> double { return 0.0; });
1112     scrollEffect->SetInitLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
1113         auto grid = weak.Upgrade();
1114         CHECK_NULL_RETURN(grid, 0.0);
1115         return grid->GetEndOffset();
1116     });
1117     scrollEffect->SetInitTrailingCallback([]() -> double { return 0.0; });
1118 }
1119 
SyncLayoutBeforeSpring()1120 void GridPattern::SyncLayoutBeforeSpring()
1121 {
1122     auto& info = info_;
1123     if (info.synced_) {
1124         return;
1125     }
1126     if (!UseIrregularLayout()) {
1127         const float delta = info.currentOffset_ - info.prevOffset_;
1128         if (!info.lineHeightMap_.empty() && LessOrEqual(delta, -info_.lastMainSize_)) {
1129             // old layout can't handle large overScroll offset. Avoid by skipping this layout.
1130             // Spring animation plays immediately afterwards, so losing this frame's offset is fine
1131             info.currentOffset_ = info.prevOffset_;
1132             info.synced_ = true;
1133             return;
1134         }
1135     }
1136     auto host = GetHost();
1137     CHECK_NULL_VOID(host);
1138 
1139     preSpring_ = true;
1140     host->SetActive();
1141     auto* context = host->GetContext();
1142     if (context) {
1143         host->SetEscapeDelayForIgnore(true);
1144         context->FlushUITaskWithSingleDirtyNode(host);
1145     }
1146     preSpring_ = false;
1147 }
1148 
GetEndOverScrollIrregular(OverScrollOffset & offset,float delta) const1149 void GridPattern::GetEndOverScrollIrregular(OverScrollOffset& offset, float delta) const
1150 {
1151     const float mainGap = GetMainGap();
1152     const float viewport = info_.lastMainSize_ - info_.contentEndPadding_;
1153     float heightInView = info_.totalHeightOfItemsInView_;
1154     if (info_.HeightSumSmaller(viewport, mainGap)) {
1155         // content < viewport, use viewport height to calculate overScroll
1156         heightInView = viewport - info_.GetHeightInRange(0, info_.startMainLineIndex_, mainGap);
1157     }
1158     float disToBot = info_.GetDistanceToBottom(viewport, heightInView, mainGap);
1159     if (!info_.IsOutOfEnd(mainGap, true)) {
1160         offset.end = std::min(0.0f, disToBot + delta);
1161     } else if (Negative(delta)) {
1162         offset.end = delta;
1163     } else {
1164         offset.end = std::min(delta, -disToBot);
1165     }
1166 }
1167 
GetOverScrollOffset(double delta) const1168 OverScrollOffset GridPattern::GetOverScrollOffset(double delta) const
1169 {
1170     OverScrollOffset offset = { 0, 0 };
1171     if (info_.startIndex_ == 0 && info_.startMainLineIndex_ == 0) {
1172         auto startPos = info_.currentOffset_;
1173         auto newStartPos = startPos + delta;
1174         if (GreatNotEqual(startPos, 0) && GreatNotEqual(newStartPos, 0)) {
1175             offset.start = delta;
1176         }
1177         if (GreatNotEqual(startPos, 0) && LessOrEqual(newStartPos, 0)) {
1178             offset.start = -startPos;
1179         }
1180         if (LessOrEqual(startPos, 0) && GreatNotEqual(newStartPos, 0)) {
1181             offset.start = newStartPos;
1182         }
1183     }
1184     if (UseIrregularLayout()) {
1185         if (info_.repeatDifference_ == 0) {
1186             GetEndOverScrollIrregular(offset, static_cast<float>(delta));
1187         }
1188         return offset;
1189     }
1190     if (info_.endIndex_ == (info_.childrenCount_ + info_.repeatDifference_ - 1)) {
1191         float endPos = info_.currentOffset_ + info_.totalHeightOfItemsInView_;
1192         float mainSize = info_.lastMainSize_ - info_.contentEndPadding_;
1193         if (GreatNotEqual(GetMainContentSize(), info_.currentOffset_ + info_.totalHeightOfItemsInView_)) {
1194             endPos = info_.currentOffset_ + GetMainContentSize();
1195         }
1196         float newEndPos = endPos + delta;
1197         if (LessNotEqual(endPos, mainSize) && LessNotEqual(newEndPos, mainSize)) {
1198             offset.end = delta;
1199         }
1200         if (LessNotEqual(endPos, mainSize) && GreatOrEqual(newEndPos, mainSize)) {
1201             offset.end = mainSize - endPos;
1202         }
1203         if (GreatOrEqual(endPos, mainSize) && LessNotEqual(newEndPos, mainSize)) {
1204             offset.end = newEndPos - mainSize;
1205         }
1206     }
1207     return offset;
1208 }
1209 
DumpAdvanceInfo()1210 void GridPattern::DumpAdvanceInfo()
1211 {
1212     auto property = GetLayoutProperty<GridLayoutProperty>();
1213     CHECK_NULL_VOID(property);
1214     ScrollablePattern::DumpAdvanceInfo();
1215     if (!property->HasLayoutOptions()) {
1216         DumpLog::GetInstance().AddDesc("GridLayoutOptions:null");
1217     } else {
1218         DumpLog::GetInstance().AddDesc("GridLayoutOptions:true");
1219         DumpLog::GetInstance().AddDesc(GetIrregularIndexesString());
1220     }
1221     supportAnimation_ ? DumpLog::GetInstance().AddDesc("supportAnimation:true")
1222                       : DumpLog::GetInstance().AddDesc("supportAnimation:false");
1223     isConfigScrollable_ ? DumpLog::GetInstance().AddDesc("isConfigScrollable:true")
1224                         : DumpLog::GetInstance().AddDesc("isConfigScrollable:false");
1225     info_.lastCrossCount_.has_value()
1226         ? DumpLog::GetInstance().AddDesc("lastCrossCount:" + std::to_string(info_.lastCrossCount_.value()))
1227         : DumpLog::GetInstance().AddDesc("lastCrossCount:null");
1228     info_.reachEnd_ ? DumpLog::GetInstance().AddDesc("reachEnd:true")
1229                     : DumpLog::GetInstance().AddDesc("reachEnd:false");
1230     info_.reachStart_ ? DumpLog::GetInstance().AddDesc("reachStart:true")
1231                       : DumpLog::GetInstance().AddDesc("reachStart:false");
1232     info_.offsetEnd_ ? DumpLog::GetInstance().AddDesc("offsetEnd:true")
1233                      : DumpLog::GetInstance().AddDesc("offsetEnd:false");
1234     info_.hasBigItem_ ? DumpLog::GetInstance().AddDesc("hasBigItem:true")
1235                       : DumpLog::GetInstance().AddDesc("hasBigItem:false");
1236     info_.synced_ ? DumpLog::GetInstance().AddDesc("synced:true") : DumpLog::GetInstance().AddDesc("synced:false");
1237     DumpLog::GetInstance().AddDesc("scrollStop:" + std::to_string(scrollStop_));
1238     DumpLog::GetInstance().AddDesc("prevHeight:" + std::to_string(info_.prevHeight_));
1239     DumpLog::GetInstance().AddDesc("currentHeight:" + std::to_string(info_.currentHeight_));
1240     DumpLog::GetInstance().AddDesc("endHeight:" + std::to_string(endHeight_));
1241     DumpLog::GetInstance().AddDesc("currentOffset:" + std::to_string(info_.currentOffset_));
1242     DumpLog::GetInstance().AddDesc("prevOffset:" + std::to_string(info_.prevOffset_));
1243     DumpLog::GetInstance().AddDesc("lastMainSize:" + std::to_string(info_.lastMainSize_));
1244     DumpLog::GetInstance().AddDesc("totalHeightOfItemsInView:" + std::to_string(info_.totalHeightOfItemsInView_));
1245     DumpLog::GetInstance().AddDesc("startIndex:" + std::to_string(info_.startIndex_));
1246     DumpLog::GetInstance().AddDesc("endIndex:" + std::to_string(info_.endIndex_));
1247     DumpLog::GetInstance().AddDesc("jumpIndex:" + std::to_string(info_.jumpIndex_));
1248     DumpLog::GetInstance().AddDesc("crossCount:" + std::to_string(info_.crossCount_));
1249     DumpLog::GetInstance().AddDesc("childrenCount:" + std::to_string(info_.childrenCount_));
1250     DumpLog::GetInstance().AddDesc("RowsTemplate:", property->GetRowsTemplate()->c_str());
1251     DumpLog::GetInstance().AddDesc("ColumnsTemplate:", property->GetColumnsTemplate()->c_str());
1252     property->GetRowsGap().has_value()
1253         ? DumpLog::GetInstance().AddDesc("RowsGap:" + std::to_string(property->GetRowsGap().value().Value()))
1254         : DumpLog::GetInstance().AddDesc("RowsGap:null");
1255     property->GetColumnsGap().has_value()
1256         ? DumpLog::GetInstance().AddDesc("ColumnsGap:" + std::to_string(property->GetColumnsGap().value().Value()))
1257         : DumpLog::GetInstance().AddDesc("ColumnsGap:null");
1258     property->GetCachedCount().has_value()
1259         ? DumpLog::GetInstance().AddDesc("CachedCount:" + std::to_string(property->GetCachedCount().value()))
1260         : DumpLog::GetInstance().AddDesc("CachedCount:null");
1261     property->GetMaxCount().has_value()
1262         ? DumpLog::GetInstance().AddDesc("MaxCount:" + std::to_string(property->GetMaxCount().value()))
1263         : DumpLog::GetInstance().AddDesc("MaxCount:null");
1264     property->GetMinCount().has_value()
1265         ? DumpLog::GetInstance().AddDesc("MinCount:" + std::to_string(property->GetMinCount().value()))
1266         : DumpLog::GetInstance().AddDesc("MinCount:null");
1267     property->GetCellLength().has_value()
1268         ? DumpLog::GetInstance().AddDesc("CellLength:" + std::to_string(property->GetCellLength().value()))
1269         : DumpLog::GetInstance().AddDesc("CellLength:null");
1270     property->GetEditable().has_value()
1271         ? DumpLog::GetInstance().AddDesc("Editable:" + std::to_string(property->GetEditable().value()))
1272         : DumpLog::GetInstance().AddDesc("Editable:null");
1273     property->GetScrollEnabled().has_value()
1274         ? DumpLog::GetInstance().AddDesc("ScrollEnabled:" + std::to_string(property->GetScrollEnabled().value()))
1275         : DumpLog::GetInstance().AddDesc("ScrollEnabled:null");
1276     switch (property->GetAlignItems().value_or(GridItemAlignment::DEFAULT)) {
1277         case GridItemAlignment::STRETCH: {
1278             DumpLog::GetInstance().AddDesc("AlignItems:GridItemAlignment.STRETCH");
1279             break;
1280         }
1281         default: {
1282             DumpLog::GetInstance().AddDesc("AlignItems:GridItemAlignment.DEFAULT");
1283             break;
1284         }
1285     }
1286     switch (info_.scrollAlign_) {
1287         case ScrollAlign::NONE: {
1288             DumpLog::GetInstance().AddDesc("ScrollAlign:NONE");
1289             break;
1290         }
1291         case ScrollAlign::CENTER: {
1292             DumpLog::GetInstance().AddDesc("ScrollAlign:CENTER");
1293             break;
1294         }
1295         case ScrollAlign::END: {
1296             DumpLog::GetInstance().AddDesc("ScrollAlign:END");
1297             break;
1298         }
1299         case ScrollAlign::START: {
1300             DumpLog::GetInstance().AddDesc("ScrollAlign:START");
1301             break;
1302         }
1303         case ScrollAlign::AUTO: {
1304             DumpLog::GetInstance().AddDesc("ScrollAlign:AUTO");
1305             break;
1306         }
1307         default: {
1308             break;
1309         }
1310     }
1311     if (!info_.gridMatrix_.empty()) {
1312         DumpLog::GetInstance().AddDesc("-----------start print gridMatrix------------");
1313         std::string res = std::string("");
1314         for (auto item : info_.gridMatrix_) {
1315             res.append(std::to_string(item.first));
1316             res.append(": ");
1317             for (auto index : item.second) {
1318                 res.append("[")
1319                     .append(std::to_string(index.first))
1320                     .append(",")
1321                     .append(std::to_string(index.second))
1322                     .append("] ");
1323             }
1324             DumpLog::GetInstance().AddDesc(res);
1325             res.clear();
1326         }
1327         DumpLog::GetInstance().AddDesc("-----------end print gridMatrix------------");
1328     }
1329     if (!info_.lineHeightMap_.empty()) {
1330         DumpLog::GetInstance().AddDesc("-----------start print lineHeightMap------------");
1331         for (auto item : info_.lineHeightMap_) {
1332             DumpLog::GetInstance().AddDesc(std::to_string(item.first).append(" :").append(std::to_string(item.second)));
1333         }
1334         DumpLog::GetInstance().AddDesc("-----------end print lineHeightMap------------");
1335     }
1336     if (!info_.irregularItemsPosition_.empty()) {
1337         DumpLog::GetInstance().AddDesc("-----------start print irregularItemsPosition_------------");
1338         for (auto item : info_.irregularItemsPosition_) {
1339             DumpLog::GetInstance().AddDesc(std::to_string(item.first).append(" :").append(std::to_string(item.second)));
1340         }
1341         DumpLog::GetInstance().AddDesc("-----------end print irregularItemsPosition_------------");
1342     }
1343 }
1344 
GetEventDumpInfo()1345 void GridPattern::GetEventDumpInfo()
1346 {
1347     ScrollablePattern::GetEventDumpInfo();
1348     auto host = GetHost();
1349     CHECK_NULL_VOID(host);
1350     auto hub = host->GetOrCreateEventHub<GridEventHub>();
1351     CHECK_NULL_VOID(hub);
1352     auto onScrollIndex = hub->GetOnScrollIndex();
1353     onScrollIndex ? DumpLog::GetInstance().AddDesc("hasOnScrollIndex: true")
1354                   : DumpLog::GetInstance().AddDesc("hasOnScrollIndex: false");
1355     auto onJSFrameNodeScrollIndex = hub->GetJSFrameNodeOnGridScrollIndex();
1356     onJSFrameNodeScrollIndex ? DumpLog::GetInstance().AddDesc("nodeOnScrollIndex: true")
1357                              : DumpLog::GetInstance().AddDesc("nodeOnScrollIndex: false");
1358 }
1359 
GetEventDumpInfo(std::unique_ptr<JsonValue> & json)1360 void GridPattern::GetEventDumpInfo(std::unique_ptr<JsonValue>& json)
1361 {
1362     ScrollablePattern::GetEventDumpInfo(json);
1363     auto host = GetHost();
1364     CHECK_NULL_VOID(host);
1365     auto hub = host->GetOrCreateEventHub<GridEventHub>();
1366     CHECK_NULL_VOID(hub);
1367     auto onScrollIndex = hub->GetOnScrollIndex();
1368     json->Put("hasOnScrollIndex", onScrollIndex ? "true" : "false");
1369     auto onJSFrameNodeScrollIndex = hub->GetJSFrameNodeOnGridScrollIndex();
1370     json->Put("nodeOnScrollIndex", onJSFrameNodeScrollIndex ? "true" : "false");
1371 }
1372 
GetIrregularIndexesString() const1373 std::string GridPattern::GetIrregularIndexesString() const
1374 {
1375     auto property = GetLayoutProperty<GridLayoutProperty>();
1376     if (!property || !property->HasLayoutOptions()) {
1377         return std::string("");
1378     }
1379     const auto& options = *property->GetLayoutOptions();
1380     if (options.irregularIndexes.empty()) {
1381         return std::string("");
1382     }
1383     std::string irregularIndexes = std::string("IrregularIndexes: [");
1384     int count = 0;
1385     for (const auto& index : options.irregularIndexes) {
1386         if (count > 0) {
1387             irregularIndexes.append(", ");
1388         }
1389         irregularIndexes.append(std::to_string(index));
1390         count++;
1391         if (count == MAX_NUM_SIZE) {
1392             irregularIndexes.append("...");
1393             break;
1394         }
1395     }
1396     irregularIndexes.append("]");
1397     return irregularIndexes;
1398 }
1399 
ProvideRestoreInfo()1400 std::string GridPattern::ProvideRestoreInfo()
1401 {
1402     return std::to_string(info_.startIndex_);
1403 }
1404 
OnRestoreInfo(const std::string & restoreInfo)1405 void GridPattern::OnRestoreInfo(const std::string& restoreInfo)
1406 {
1407     info_.jumpIndex_ = StringUtils::StringToInt(restoreInfo);
1408     info_.scrollAlign_ = ScrollAlign::START;
1409 }
1410 
GetItemRect(int32_t index) const1411 Rect GridPattern::GetItemRect(int32_t index) const
1412 {
1413     if (index < 0 || index < info_.startIndex_ || index > info_.endIndex_) {
1414         return Rect();
1415     }
1416     auto host = GetHost();
1417     CHECK_NULL_RETURN(host, Rect());
1418     auto item = host->GetChildByIndex(index);
1419     CHECK_NULL_RETURN(item, Rect());
1420     auto itemGeometry = item->GetGeometryNode();
1421     CHECK_NULL_RETURN(itemGeometry, Rect());
1422     return Rect(itemGeometry->GetFrameRect().GetX(), itemGeometry->GetFrameRect().GetY(),
1423         itemGeometry->GetFrameRect().Width(), itemGeometry->GetFrameRect().Height());
1424 }
1425 
GetItemIndex(double x,double y) const1426 int32_t GridPattern::GetItemIndex(double x, double y) const
1427 {
1428     for (int32_t index = info_.startIndex_; index <= info_.endIndex_; ++index) {
1429         Rect rect = GetItemRect(index);
1430         if (rect.IsInRegion({ x, y })) {
1431             return index;
1432         }
1433     }
1434     return -1;
1435 }
1436 
ScrollToIndex(int32_t index,bool smooth,ScrollAlign align,std::optional<float> extraOffset)1437 void GridPattern::ScrollToIndex(int32_t index, bool smooth, ScrollAlign align, std::optional<float> extraOffset)
1438 {
1439     SetScrollSource(SCROLL_FROM_JUMP);
1440     StopAnimate();
1441     auto host = GetHost();
1442     CHECK_NULL_VOID(host);
1443     int32_t totalChildCount = host->TotalChildCount();
1444     if (((index >= 0) && (index < totalChildCount)) || (index == LAST_ITEM)) {
1445         if (extraOffset.has_value()) {
1446             info_.extraOffset_ = -extraOffset.value();
1447         }
1448         if (smooth) {
1449             SetExtraOffset(extraOffset);
1450             targetIndex_ = index;
1451             scrollAlign_ = align;
1452             host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1453         } else {
1454             UpdateStartIndex(index, align);
1455         }
1456     }
1457     FireAndCleanScrollingListener();
1458 }
1459 
ScrollToEdge(ScrollEdgeType scrollEdgeType,bool smooth)1460 void GridPattern::ScrollToEdge(ScrollEdgeType scrollEdgeType, bool smooth)
1461 {
1462     if (UseIrregularLayout() && scrollEdgeType == ScrollEdgeType::SCROLL_BOTTOM) {
1463         ScrollToIndex(LAST_ITEM, smooth);
1464         // for irregular layout, last item might not be at bottom
1465         info_.jumpIndex_ = JUMP_TO_BOTTOM_EDGE;
1466         auto host = GetHost();
1467         CHECK_NULL_VOID(host);
1468         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1469         return;
1470     }
1471     ScrollablePattern::ScrollToEdge(scrollEdgeType, smooth);
1472 }
1473 
1474 // Turn on the scrolling animation
AnimateToTarget(ScrollAlign align,const RefPtr<LayoutAlgorithmWrapper> & algo)1475 void GridPattern::AnimateToTarget(ScrollAlign align, const RefPtr<LayoutAlgorithmWrapper>& algo)
1476 {
1477     if (targetIndex_.has_value()) {
1478         AnimateToTargetImpl(align, algo);
1479         targetIndex_.reset();
1480     }
1481 }
1482 
1483 // scroll to the item where the index is located
AnimateToTargetImpl(ScrollAlign align,const RefPtr<LayoutAlgorithmWrapper> & algo)1484 bool GridPattern::AnimateToTargetImpl(ScrollAlign align, const RefPtr<LayoutAlgorithmWrapper>& algo)
1485 {
1486     const float mainGap = GetMainGap();
1487     float targetPos = 0.0f;
1488     auto host = GetHost();
1489     CHECK_NULL_RETURN(host, false);
1490     auto&& extraOffset = GetExtraOffset();
1491     bool success = true;
1492     if (UseIrregularLayout()) {
1493         auto host = GetHost();
1494         CHECK_NULL_RETURN(host, false);
1495         auto size = GridLayoutUtils::GetItemSize(&info_, RawPtr(host), *targetIndex_);
1496         targetPos = info_.GetAnimatePosIrregular(*targetIndex_, size.rows, align, mainGap);
1497         if (Negative(targetPos)) {
1498             success = false;
1499         }
1500     } else {
1501         auto gridScrollLayoutAlgorithm = DynamicCast<GridScrollLayoutAlgorithm>(algo->GetLayoutAlgorithm());
1502         infoCopy_ = gridScrollLayoutAlgorithm->MoveInfoCopy();
1503         // Based on the index, align gets the position to scroll to
1504         success = infoCopy_ && infoCopy_->GetGridItemAnimatePos(info_, *targetIndex_, align, mainGap, targetPos);
1505     }
1506     if (!success) {
1507         if (NearZero(extraOffset.value_or(0.0f))) {
1508             return false;
1509         }
1510         targetPos = GetTotalOffset();
1511     }
1512 
1513     isSmoothScrolling_ = true;
1514     if (extraOffset.has_value()) {
1515         ACE_SCOPED_TRACE(
1516             "AnimateToTargetImpl, success:%u, targetPos:%f, extraOffset:%f", success, targetPos, *extraOffset);
1517         targetPos += *extraOffset;
1518         ResetExtraOffset();
1519     } else {
1520         ACE_SCOPED_TRACE("AnimateToTargetImpl, targetPos:%f", targetPos);
1521     }
1522     if (NearEqual(targetPos, GetTotalOffset())) {
1523         isSmoothScrolling_ = false;
1524         return false;
1525     }
1526     AnimateTo(targetPos, -1, nullptr, true);
1527     return true;
1528 }
1529 
GetVisibleSelectedItems()1530 std::vector<RefPtr<FrameNode>> GridPattern::GetVisibleSelectedItems()
1531 {
1532     std::vector<RefPtr<FrameNode>> children;
1533     auto host = GetHost();
1534     CHECK_NULL_RETURN(host, children);
1535     for (int32_t index = info_.startIndex_; index <= info_.endIndex_; ++index) {
1536         auto item = host->GetChildByIndex(index);
1537         if (!AceType::InstanceOf<FrameNode>(item)) {
1538             continue;
1539         }
1540         auto itemFrameNode = AceType::DynamicCast<FrameNode>(item);
1541         auto itemPattern = itemFrameNode->GetPattern<GridItemPattern>();
1542         if (!itemPattern) {
1543             continue;
1544         }
1545         if (!itemPattern->IsSelected()) {
1546             continue;
1547         }
1548         children.emplace_back(itemFrameNode);
1549     }
1550     return children;
1551 }
1552 
StopAnimate()1553 void GridPattern::StopAnimate()
1554 {
1555     ScrollablePattern::StopAnimate();
1556     isSmoothScrolling_ = false;
1557 }
1558 
IsPredictOutOfCacheRange(int32_t index) const1559 bool GridPattern::IsPredictOutOfCacheRange(int32_t index) const
1560 {
1561     CHECK_NULL_RETURN(index < info_.GetChildrenCount(), true);
1562     auto host = GetHost();
1563     CHECK_NULL_RETURN(host, true);
1564     auto gridLayoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
1565     CHECK_NULL_RETURN(gridLayoutProperty, true);
1566     auto cacheCount = gridLayoutProperty->GetCachedCountValue(info_.defCachedCount_) * info_.crossCount_;
1567     return index < info_.startIndex_ - cacheCount || index > info_.endIndex_ + cacheCount ||
1568            (index >= info_.startIndex_ && index <= info_.endIndex_);
1569 }
1570 
UseIrregularLayout() const1571 inline bool GridPattern::UseIrregularLayout() const
1572 {
1573     return irregular_ || (SystemProperties::GetGridIrregularLayoutEnabled() &&
1574                              GetLayoutProperty<GridLayoutProperty>()->HasLayoutOptions());
1575 }
1576 
IsReverse() const1577 bool GridPattern::IsReverse() const
1578 {
1579     auto host = GetHost();
1580     CHECK_NULL_RETURN(host, false);
1581     auto gridLayoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
1582     CHECK_NULL_RETURN(gridLayoutProperty, false);
1583     return gridLayoutProperty->IsReverse();
1584 }
1585 
BuildScrollAlignInfo(std::unique_ptr<JsonValue> & json)1586 void GridPattern::BuildScrollAlignInfo(std::unique_ptr<JsonValue>& json)
1587 {
1588     switch (info_.scrollAlign_) {
1589         case ScrollAlign::NONE: {
1590             json->Put("ScrollAlign", "NONE");
1591             break;
1592         }
1593         case ScrollAlign::CENTER: {
1594             json->Put("ScrollAlign", "CENTER");
1595             break;
1596         }
1597         case ScrollAlign::END: {
1598             json->Put("ScrollAlign", "END");
1599             break;
1600         }
1601         case ScrollAlign::START: {
1602             json->Put("ScrollAlign", "START");
1603             break;
1604         }
1605         case ScrollAlign::AUTO: {
1606             json->Put("ScrollAlign", "AUTO");
1607             break;
1608         }
1609         default: {
1610             break;
1611         }
1612     }
1613 }
1614 
BuildGridLayoutInfo(std::unique_ptr<JsonValue> & json)1615 void GridPattern::BuildGridLayoutInfo(std::unique_ptr<JsonValue>& json)
1616 {
1617     if (!info_.gridMatrix_.empty()) {
1618         std::unique_ptr<JsonValue> children = JsonUtil::Create(true);
1619         std::string res = std::string("");
1620         for (auto item : info_.gridMatrix_) {
1621             for (auto index : item.second) {
1622                 res.append("[")
1623                     .append(std::to_string(index.first))
1624                     .append(",")
1625                     .append(std::to_string(index.second))
1626                     .append("] ");
1627             }
1628             children->Put(std::to_string(item.first).c_str(), res.c_str());
1629             res.clear();
1630         }
1631         children->Put("gridMatrix", children);
1632     }
1633     if (!info_.lineHeightMap_.empty()) {
1634         std::unique_ptr<JsonValue> children = JsonUtil::Create(true);
1635         for (auto item : info_.lineHeightMap_) {
1636             children->Put(std::to_string(item.first).c_str(), std::to_string(item.second).c_str());
1637         }
1638         children->Put("lineHeightMap", children);
1639     }
1640     if (!info_.irregularItemsPosition_.empty()) {
1641         std::unique_ptr<JsonValue> children = JsonUtil::Create(true);
1642         for (auto item : info_.irregularItemsPosition_) {
1643             children->Put(std::to_string(item.first).c_str(), std::to_string(item.second).c_str());
1644         }
1645         children->Put("irregularItemsPosition_", children);
1646     }
1647 }
1648 
DumpAdvanceInfo(std::unique_ptr<JsonValue> & json)1649 void GridPattern::DumpAdvanceInfo(std::unique_ptr<JsonValue>& json)
1650 {
1651     auto property = GetLayoutProperty<GridLayoutProperty>();
1652     CHECK_NULL_VOID(property);
1653     ScrollablePattern::DumpAdvanceInfo(json);
1654     json->Put("GridLayoutOptions", property->HasLayoutOptions() ? GetIrregularIndexesString().c_str() : "null");
1655     json->Put("supportAnimation", supportAnimation_);
1656     json->Put("isConfigScrollable", isConfigScrollable_);
1657     json->Put("lastCrossCount",
1658         info_.lastCrossCount_.has_value() ? std::to_string(info_.lastCrossCount_.value()).c_str() : "null");
1659     json->Put("reachEnd", info_.reachEnd_);
1660     json->Put("reachStart", info_.reachStart_);
1661     json->Put("offsetEnd", info_.offsetEnd_);
1662     json->Put("hasBigItem", info_.hasBigItem_);
1663     json->Put("synced", info_.synced_);
1664     json->Put("scrollStop", std::to_string(scrollStop_).c_str());
1665     json->Put("prevHeight", info_.prevHeight_);
1666     json->Put("currentHeight", info_.currentHeight_);
1667     json->Put("endHeight", endHeight_);
1668     json->Put("currentOffset", std::to_string(info_.currentOffset_).c_str());
1669     json->Put("prevOffset", std::to_string(info_.prevOffset_).c_str());
1670     json->Put("lastMainSize", std::to_string(info_.lastMainSize_).c_str());
1671     json->Put("totalHeightOfItemsInView", std::to_string(info_.totalHeightOfItemsInView_).c_str());
1672     json->Put("startIndex", info_.startIndex_);
1673     json->Put("endIndex", info_.endIndex_);
1674     json->Put("jumpIndex", info_.jumpIndex_);
1675     json->Put("crossCount", info_.crossCount_);
1676     json->Put("childrenCount", info_.childrenCount_);
1677     json->Put("RowsTemplate", property->GetRowsTemplate()->c_str());
1678     json->Put("ColumnsTemplate", property->GetColumnsTemplate()->c_str());
1679     json->Put("CachedCount",
1680         property->GetCachedCount().has_value() ? std::to_string(property->GetCachedCount().value()).c_str() : "null");
1681     json->Put("ShowCache", std::to_string(property->GetShowCachedItemsValue(false)).c_str());
1682     json->Put("MaxCount",
1683         property->GetMaxCount().has_value() ? std::to_string(property->GetMaxCount().value()).c_str() : "null");
1684     json->Put("MinCount",
1685         property->GetMinCount().has_value() ? std::to_string(property->GetMinCount().value()).c_str() : "null");
1686     json->Put("CellLength",
1687         property->GetCellLength().has_value() ? std::to_string(property->GetCellLength().value()).c_str() : "null");
1688     json->Put("Editable",
1689         property->GetEditable().has_value() ? std::to_string(property->GetEditable().value()).c_str() : "null");
1690     json->Put("ScrollEnabled", property->GetScrollEnabled().has_value()
1691                                    ? std::to_string(property->GetScrollEnabled().value()).c_str()
1692                                    : "null");
1693 
1694     json->Put("AlignItems", property->GetAlignItems() ? "GridItemAlignment.STRETCH" : "GridItemAlignment.DEFAULT");
1695     BuildScrollAlignInfo(json);
1696     BuildGridLayoutInfo(json);
1697 }
1698 
GetChildrenExpandedSize()1699 SizeF GridPattern::GetChildrenExpandedSize()
1700 {
1701     auto host = GetHost();
1702     CHECK_NULL_RETURN(host, SizeF());
1703     auto layoutProperty = host->GetLayoutProperty<GridLayoutProperty>();
1704     CHECK_NULL_RETURN(layoutProperty, SizeF());
1705     auto padding = layoutProperty->CreatePaddingAndBorder();
1706     auto geometryNode = host->GetGeometryNode();
1707     CHECK_NULL_RETURN(geometryNode, SizeF());
1708     auto viewSize = geometryNode->GetFrameSize();
1709     MinusPaddingToSize(padding, viewSize);
1710 
1711     auto axis = GetAxis();
1712     float estimatedHeight = 0.f;
1713     const auto& info = info_;
1714     auto viewScopeSize = geometryNode->GetPaddingSize();
1715     auto mainGap = GridUtils::GetMainGap(layoutProperty, viewScopeSize, info.axis_);
1716     if (UseIrregularLayout()) {
1717         estimatedHeight = info.GetIrregularHeight(mainGap);
1718     } else if (!layoutProperty->GetLayoutOptions().has_value()) {
1719         estimatedHeight = info.GetContentHeight(mainGap);
1720     } else {
1721         estimatedHeight =
1722             info.GetContentHeight(layoutProperty->GetLayoutOptions().value(), info.childrenCount_, mainGap);
1723     }
1724 
1725     if (axis == Axis::VERTICAL) {
1726         return SizeF(viewSize.Width(), estimatedHeight);
1727     } else if (axis == Axis::HORIZONTAL) {
1728         return SizeF(estimatedHeight, viewSize.Height());
1729     }
1730     return SizeF();
1731 }
1732 
GetScopeFocusAlgorithm()1733 ScopeFocusAlgorithm GridPattern::GetScopeFocusAlgorithm()
1734 {
1735     auto property = GetLayoutProperty<GridLayoutProperty>();
1736     if (!property) {
1737         return ScopeFocusAlgorithm();
1738     }
1739     return ScopeFocusAlgorithm(property->IsVertical(), true, ScopeType::OTHERS,
1740         [wp = WeakClaim(this)](
1741             FocusStep step, const WeakPtr<FocusHub>& currFocusNode, WeakPtr<FocusHub>& nextFocusNode) -> bool {
1742             auto grid = wp.Upgrade();
1743             CHECK_NULL_RETURN(grid, false);
1744             if (grid->UseIrregularLayout() && (step == FocusStep::TAB || step == FocusStep::SHIFT_TAB)) {
1745                 nextFocusNode = grid->focusHandler_.GetNextFocusSimplified(step, currFocusNode.Upgrade());
1746             } else {
1747                 nextFocusNode = grid->focusHandler_.GetNextFocusNode(step, currFocusNode);
1748             }
1749             return nextFocusNode.Upgrade() != currFocusNode.Upgrade();
1750         });
1751 }
1752 
HandleOnItemFocus(int32_t index)1753 void GridPattern::HandleOnItemFocus(int32_t index)
1754 {
1755     focusHandler_.SetFocusIndex(index);
1756     auto host = GetHost();
1757     CHECK_NULL_VOID(host);
1758     auto focusHub = host->GetFocusHub();
1759     CHECK_NULL_VOID(host);
1760     if (focusHub->GetFocusDependence() != FocusDependence::AUTO) {
1761         focusHub->SetFocusDependence(FocusDependence::AUTO);
1762     }
1763 }
1764 
ReportOnItemGridEvent(const std::string & event)1765 void GridPattern::ReportOnItemGridEvent(const std::string& event)
1766 {
1767     auto host = GetHost();
1768     CHECK_NULL_VOID(host);
1769     std::string value = std::string("Grid.") + event;
1770     UiSessionManager::GetInstance()->ReportComponentChangeEvent("event", value);
1771     TAG_LOGI(AceLogTag::ACE_GRID, "nodeId:[%{public}d] Grid reportComponentChangeEvent %{public}s", host->GetId(),
1772         event.c_str());
1773 }
1774 
OnInjectionEvent(const std::string & command)1775 int32_t GridPattern::OnInjectionEvent(const std::string& command)
1776 {
1777     TAG_LOGI(AceLogTag::ACE_GRID, "OnInjectionEvent command: %{public}s", command.c_str());
1778 
1779     std::string ret = ScrollablePattern::ParseCommand(command);
1780     if (ret == "scrollForward") {
1781         ScrollPage(true);
1782     } else if (ret == "scrollBackward") {
1783         ScrollPage(false);
1784     } else {
1785         return RET_FAILED;
1786     }
1787     return RET_SUCCESS;
1788 }
1789 
OnColorModeChange(uint32_t colorMode)1790 void GridPattern::OnColorModeChange(uint32_t colorMode)
1791 {
1792     Pattern::OnColorModeChange(colorMode);
1793     CHECK_NULL_VOID(SystemProperties::ConfigChangePerform());
1794     auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
1795     CHECK_NULL_VOID(paintProperty);
1796     if (paintProperty->GetScrollBarProperty()) {
1797         SetScrollBar(paintProperty->GetScrollBarProperty());
1798     }
1799     auto host = GetHost();
1800     CHECK_NULL_VOID(host);
1801     host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1802 }
1803 } // namespace OHOS::Ace::NG
1804