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