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