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