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