1 /*
2 * Copyright (c) 2023-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/waterflow/water_flow_pattern.h"
17
18 #include "base/log/dump_log.h"
19 #include "base/utils/utils.h"
20 #include "core/components/scroll/scroll_controller_base.h"
21 #include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h"
22 #include "core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_algorithm.h"
23 #include "core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_info.h"
24 #include "core/components_ng/pattern/waterflow/layout/top_down/water_flow_segmented_layout.h"
25 #include "core/components_ng/pattern/waterflow/layout/water_flow_layout_info_base.h"
26 #include "core/components_ng/pattern/waterflow/water_flow_paint_method.h"
27 #include "core/components_ng/pattern/waterflow/water_flow_item_pattern.h"
28
29 namespace OHOS::Ace::NG {
GetContentSize() const30 SizeF WaterFlowPattern::GetContentSize() const
31 {
32 auto host = GetHost();
33 CHECK_NULL_RETURN(host, SizeF());
34 auto geometryNode = host->GetGeometryNode();
35 CHECK_NULL_RETURN(geometryNode, SizeF());
36 return geometryNode->GetPaddingSize();
37 }
38
UpdateCurrentOffset(float delta,int32_t source)39 bool WaterFlowPattern::UpdateCurrentOffset(float delta, int32_t source)
40 {
41 auto host = GetHost();
42 CHECK_NULL_RETURN(host, false);
43
44 // check edgeEffect is not springEffect
45 if (!HandleEdgeEffect(delta, source, GetContentSize())) {
46 if (IsOutOfBoundary()) {
47 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
48 }
49 return false;
50 }
51 SetScrollSource(source);
52 FireAndCleanScrollingListener();
53 if (GetScrollEdgeEffect()) {
54 // over scroll in drag update from normal to over scroll.
55 float overScroll = layoutInfo_->CalcOverScroll(GetMainContentSize(), delta);
56 if (source == SCROLL_FROM_UPDATE) {
57 auto friction = ScrollablePattern::CalculateFriction(std::abs(overScroll) / GetMainContentSize());
58 delta *= friction;
59 }
60 } else {
61 if (layoutInfo_->itemStart_ && delta > 0) {
62 return false;
63 }
64 if (layoutInfo_->offsetEnd_ && delta < 0) {
65 return false;
66 }
67 if (layoutInfo_->Mode() == LayoutMode::TOP_DOWN && GreatNotEqual(delta, 0.0f)) {
68 // adjust top overScroll
69 delta = std::min(delta, -layoutInfo_->Offset());
70 }
71 }
72 delta = -FireOnWillScroll(-delta);
73 layoutInfo_->UpdateOffset(delta);
74 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
75 return true;
76 };
77
IsScrollable() const78 bool WaterFlowPattern::IsScrollable() const
79 {
80 return !(IsAtTop() && IsAtBottom() && !GetAlwaysEnabled());
81 }
IsAtTop() const82 bool WaterFlowPattern::IsAtTop() const
83 {
84 return layoutInfo_->itemStart_;
85 };
IsAtBottom() const86 bool WaterFlowPattern::IsAtBottom() const
87 {
88 return layoutInfo_->offsetEnd_;
89 };
IsReverse() const90 bool WaterFlowPattern::IsReverse() const
91 {
92 auto host = GetHost();
93 CHECK_NULL_RETURN(host, false);
94 auto layoutProperty = host->GetLayoutProperty<WaterFlowLayoutProperty>();
95 CHECK_NULL_RETURN(layoutProperty, false);
96 return layoutProperty->IsReverse();
97 }
IsVerticalReverse() const98 bool WaterFlowPattern::IsVerticalReverse() const
99 {
100 auto host = GetHost();
101 CHECK_NULL_RETURN(host, false);
102 auto layoutProperty = host->GetLayoutProperty<WaterFlowLayoutProperty>();
103 CHECK_NULL_RETURN(layoutProperty, false);
104 return layoutProperty->IsVerticalReverse();
105 }
GetOverScrollOffset(double delta) const106 OverScrollOffset WaterFlowPattern::GetOverScrollOffset(double delta) const
107 {
108 return layoutInfo_->GetOverScrolledDelta(static_cast<float>(delta));
109 }
110
UpdateScrollBarOffset()111 void WaterFlowPattern::UpdateScrollBarOffset()
112 {
113 if (layoutInfo_->Mode() == LayoutMode::SLIDING_WINDOW) {
114 return;
115 }
116 CheckScrollBarOff();
117 if (!GetScrollBar() && !GetScrollBarProxy()) {
118 return;
119 }
120 auto host = GetHost();
121 CHECK_NULL_VOID(host);
122 auto geometryNode = host->GetGeometryNode();
123 auto viewSize = geometryNode->GetFrameSize();
124 auto overScroll = 0.0f;
125 auto info = DynamicCast<WaterFlowLayoutInfo>(layoutInfo_);
126 if (Positive(info->currentOffset_)) {
127 overScroll = info->currentOffset_;
128 } else {
129 overScroll = GetMainContentSize() - (info->GetContentHeight() + info->currentOffset_);
130 overScroll = Positive(overScroll) ? overScroll : 0.0f;
131 }
132 HandleScrollBarOutBoundary(overScroll);
133 UpdateScrollBarRegion(-info->currentOffset_, info->EstimateContentHeight(),
134 Size(viewSize.Width(), viewSize.Height()), Offset(0.0f, 0.0f));
135 };
136
BeforeCreateLayoutWrapper()137 void WaterFlowPattern::BeforeCreateLayoutWrapper()
138 {
139 for (const auto& start : sectionChangeStartPos_) {
140 OnSectionChanged(start);
141 }
142 sectionChangeStartPos_.clear();
143
144 if (sections_ && layoutInfo_->segmentTails_.empty()) {
145 layoutInfo_->InitSegments(sections_->GetSectionInfo(), 0);
146 }
147
148 if (sections_ || SystemProperties::WaterFlowUseSegmentedLayout()) {
149 return;
150 }
151 auto footer = footer_.Upgrade();
152 if (footer && footer->FrameCount() > 0) {
153 layoutInfo_->footerIndex_ = 0;
154 } else {
155 layoutInfo_->footerIndex_ = -1;
156 }
157 }
158
CreateLayoutAlgorithm()159 RefPtr<LayoutAlgorithm> WaterFlowPattern::CreateLayoutAlgorithm()
160 {
161 if (targetIndex_.has_value()) {
162 layoutInfo_->targetIndex_ = targetIndex_;
163 }
164 RefPtr<WaterFlowLayoutBase> algorithm;
165 if (layoutInfo_->Mode() == LayoutMode::SLIDING_WINDOW) {
166 algorithm = MakeRefPtr<WaterFlowLayoutSW>(DynamicCast<WaterFlowLayoutInfoSW>(layoutInfo_));
167 } else if (sections_ || SystemProperties::WaterFlowUseSegmentedLayout()) {
168 algorithm = MakeRefPtr<WaterFlowSegmentedLayout>(DynamicCast<WaterFlowLayoutInfo>(layoutInfo_));
169 } else {
170 algorithm = MakeRefPtr<WaterFlowLayoutAlgorithm>(DynamicCast<WaterFlowLayoutInfo>(layoutInfo_));
171 }
172 algorithm->SetCanOverScroll(CanOverScroll(GetScrollSource()));
173 return algorithm;
174 }
175
CreateNodePaintMethod()176 RefPtr<NodePaintMethod> WaterFlowPattern::CreateNodePaintMethod()
177 {
178 auto paint = MakeRefPtr<WaterFlowPaintMethod>(GetAxis() == Axis::HORIZONTAL, IsReverse(), IsVerticalReverse());
179 if (!contentModifier_) {
180 contentModifier_ = AceType::MakeRefPtr<WaterFlowContentModifier>();
181 }
182 paint->SetContentModifier(contentModifier_);
183
184 paint->SetScrollBar(GetScrollBar());
185 CreateScrollBarOverlayModifier();
186 paint->SetScrollBarOverlayModifier(GetScrollBarOverlayModifier());
187
188 auto scrollEffect = GetScrollEdgeEffect();
189 if (scrollEffect && scrollEffect->IsFadeEffect()) {
190 paint->SetEdgeEffect(scrollEffect);
191 }
192 UpdateFadingEdge(paint);
193 return paint;
194 }
195
OnModifyDone()196 void WaterFlowPattern::OnModifyDone()
197 {
198 Pattern::OnModifyDone();
199 auto layoutProperty = GetLayoutProperty<WaterFlowLayoutProperty>();
200 CHECK_NULL_VOID(layoutProperty);
201 // SetAxis for scroll event
202 SetAxis(layoutProperty->GetAxis());
203 if (!GetScrollableEvent()) {
204 AddScrollEvent();
205 }
206 SetEdgeEffect();
207
208 auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
209 CHECK_NULL_VOID(paintProperty);
210 if (layoutInfo_->Mode() == LayoutMode::SLIDING_WINDOW) {
211 SetScrollBar(DisplayMode::OFF);
212 } else if (paintProperty->GetScrollBarProperty()) {
213 SetScrollBar(paintProperty->GetScrollBarProperty());
214 }
215 SetAccessibilityAction();
216 Register2DragDropManager();
217 auto host = GetHost();
218 CHECK_NULL_VOID(host);
219 auto overlayNode = host->GetOverlayNode();
220 if (!overlayNode && paintProperty->GetFadingEdge().value_or(false)) {
221 CreateAnalyzerOverlay(host);
222 }
223 }
224
TriggerModifyDone()225 void WaterFlowPattern::TriggerModifyDone()
226 {
227 OnModifyDone();
228 }
229
230 namespace {
231 // check if layout is misaligned after a scroll event
CheckMisalignment(const RefPtr<WaterFlowLayoutInfoBase> & info)232 bool CheckMisalignment(const RefPtr<WaterFlowLayoutInfoBase>& info)
233 {
234 if (info->IsMisaligned()) {
235 info->Reset();
236 return true;
237 }
238 return false;
239 }
240 } // namespace
241
TriggerPostLayoutEvents()242 void WaterFlowPattern::TriggerPostLayoutEvents()
243 {
244 auto host = GetHost();
245 CHECK_NULL_VOID(host);
246 auto eventHub = host->GetEventHub<WaterFlowEventHub>();
247 CHECK_NULL_VOID(eventHub);
248 float delta = layoutInfo_->GetDelta(prevOffset_);
249 PrintOffsetLog(AceLogTag::ACE_WATERFLOW, host->GetId(), delta);
250
251 FireObserverOnDidScroll(delta);
252 auto onScroll = eventHub->GetOnScroll();
253 if (onScroll) {
254 FireOnScroll(delta, onScroll);
255 }
256 auto onDidScroll = eventHub->GetOnDidScroll();
257 if (onDidScroll) {
258 FireOnScroll(delta, onDidScroll);
259 }
260 bool indexChanged = itemRange_.first != layoutInfo_->FirstIdx() || itemRange_.second != layoutInfo_->endIndex_;
261 auto onScrollIndex = eventHub->GetOnScrollIndex();
262 FireOnScrollIndex(indexChanged, onScrollIndex);
263 auto onReachStart = eventHub->GetOnReachStart();
264 FireOnReachStart(onReachStart);
265 auto onReachEnd = eventHub->GetOnReachEnd();
266 FireOnReachEnd(onReachEnd);
267 OnScrollStop(eventHub->GetOnScrollStop());
268 }
269
FireOnReachStart(const OnReachEvent & onReachStart)270 void WaterFlowPattern::FireOnReachStart(const OnReachEvent& onReachStart)
271 {
272 auto host = GetHost();
273 CHECK_NULL_VOID(host && layoutInfo_->ReachStart(prevOffset_, !isInitialized_));
274 FireObserverOnReachStart();
275 CHECK_NULL_VOID(onReachStart);
276 ACE_SCOPED_TRACE("OnReachStart, id:%d, tag:WaterFlow", static_cast<int32_t>(host->GetAccessibilityId()));
277 onReachStart();
278 AddEventsFiredInfo(ScrollableEventType::ON_REACH_START);
279 }
280
FireOnReachEnd(const OnReachEvent & onReachEnd)281 void WaterFlowPattern::FireOnReachEnd(const OnReachEvent& onReachEnd)
282 {
283 auto host = GetHost();
284 CHECK_NULL_VOID(host);
285 if (layoutInfo_->ReachEnd(prevOffset_, false)) {
286 FireObserverOnReachEnd();
287 CHECK_NULL_VOID(onReachEnd);
288 ACE_SCOPED_TRACE("OnReachEnd, id:%d, tag:WaterFlow", static_cast<int32_t>(host->GetAccessibilityId()));
289 onReachEnd();
290 AddEventsFiredInfo(ScrollableEventType::ON_REACH_END);
291 } else if (!isInitialized_ && layoutInfo_->ReachEnd(prevOffset_, true)) {
292 FireObserverOnReachEnd();
293 }
294 }
295
FireOnScrollIndex(bool indexChanged,const ScrollIndexFunc & onScrollIndex)296 void WaterFlowPattern::FireOnScrollIndex(bool indexChanged, const ScrollIndexFunc& onScrollIndex)
297 {
298 CHECK_NULL_VOID(indexChanged);
299 itemRange_ = { layoutInfo_->FirstIdx(), layoutInfo_->endIndex_ };
300 CHECK_NULL_VOID(onScrollIndex);
301 onScrollIndex(layoutInfo_->FirstIdx(), layoutInfo_->endIndex_);
302 }
303
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)304 bool WaterFlowPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
305 {
306 if (config.skipMeasure && config.skipLayout) {
307 return false;
308 }
309 prevOffset_ += layoutInfo_->CalibrateOffset(); // adjust prevOffset_ to keep in sync with calibrated TotalOffset
310 TriggerPostLayoutEvents();
311
312 if (targetIndex_.has_value()) {
313 ScrollToTargetIndex(targetIndex_.value());
314 targetIndex_.reset();
315 }
316 layoutInfo_->UpdateStartIndex();
317 prevOffset_ = layoutInfo_->Offset();
318 layoutInfo_->jumpIndex_ = EMPTY_JUMP_INDEX;
319 layoutInfo_->targetIndex_.reset();
320 layoutInfo_->extraOffset_.reset();
321 UpdateScrollBarOffset();
322 CheckScrollable();
323
324 isInitialized_ = true;
325
326 if (layoutInfo_->startIndex_ == 0 && CheckMisalignment(layoutInfo_)) {
327 MarkDirtyNodeSelf();
328 }
329
330 if (layoutInfo_->isDataValid_) {
331 GetHost()->ChildrenUpdatedFrom(-1);
332 }
333 layoutInfo_->isDataValid_ = true;
334
335 return NeedRender();
336 }
337
ScrollToTargetIndex(int32_t index)338 bool WaterFlowPattern::ScrollToTargetIndex(int32_t index)
339 {
340 if (index == LAST_ITEM) {
341 auto host = GetHost();
342 CHECK_NULL_RETURN(host, false);
343 auto totalItemCount = host->TotalChildCount();
344 index = totalItemCount - 1;
345 }
346 auto crossIndex = layoutInfo_->GetCrossIndex(index);
347 if (crossIndex == -1) {
348 return false;
349 }
350 float targetPosition = layoutInfo_->CalcTargetPosition(index, crossIndex);
351 auto extraOffset = GetExtraOffset();
352 if (extraOffset.has_value()) {
353 targetPosition += extraOffset.value();
354 ResetExtraOffset();
355 }
356 ScrollablePattern::AnimateTo(targetPosition, -1, nullptr, true);
357 return true;
358 }
359
CheckScrollable()360 void WaterFlowPattern::CheckScrollable()
361 {
362 auto layoutProperty = GetLayoutProperty<WaterFlowLayoutProperty>();
363 CHECK_NULL_VOID(layoutProperty);
364 SetScrollEnabled(IsScrollable());
365 if (!layoutProperty->GetScrollEnabled().value_or(IsScrollable())) {
366 SetScrollEnabled(false);
367 }
368 }
369
UpdateStartIndex(int32_t index)370 bool WaterFlowPattern::UpdateStartIndex(int32_t index)
371 {
372 auto host = GetHost();
373 CHECK_NULL_RETURN(host, false);
374 auto childCount = host->GetTotalChildCount();
375 layoutInfo_->jumpIndex_ = (index == LAST_ITEM ? childCount - 1 : index);
376 // if target index is footer, fix align because it will jump after fillViewport.
377 if (layoutInfo_->footerIndex_ == 0 && layoutInfo_->jumpIndex_ == childCount - 1) {
378 SetScrollAlign(ScrollAlign::END);
379 }
380 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
381 return true;
382 }
383
GetRows() const384 int32_t WaterFlowPattern::GetRows() const
385 {
386 auto layoutProperty = GetLayoutProperty<WaterFlowLayoutProperty>();
387 CHECK_NULL_RETURN(layoutProperty, 0);
388
389 return layoutProperty->GetAxis() == Axis::VERTICAL ? layoutInfo_->GetMainCount() : layoutInfo_->GetCrossCount();
390 }
391
GetColumns() const392 int32_t WaterFlowPattern::GetColumns() const
393 {
394 auto layoutProperty = GetLayoutProperty<WaterFlowLayoutProperty>();
395 CHECK_NULL_RETURN(layoutProperty, 0);
396
397 return layoutProperty->GetAxis() == Axis::VERTICAL ? layoutInfo_->GetCrossCount() : layoutInfo_->GetMainCount();
398 }
399
ScrollPage(bool reverse,bool smooth,AccessibilityScrollType scrollType)400 void WaterFlowPattern::ScrollPage(bool reverse, bool smooth, AccessibilityScrollType scrollType)
401 {
402 CHECK_NULL_VOID(IsScrollable());
403
404 auto layoutProperty = GetLayoutProperty<WaterFlowLayoutProperty>();
405 CHECK_NULL_VOID(layoutProperty);
406 auto axis = layoutProperty->GetAxis();
407
408 auto host = GetHost();
409 CHECK_NULL_VOID(host);
410 auto geometryNode = host->GetGeometryNode();
411 CHECK_NULL_VOID(geometryNode);
412 auto mainContentSize = geometryNode->GetPaddingSize().MainSize(axis);
413 float distance = reverse ? mainContentSize : -mainContentSize;
414 if (layoutProperty->IsReverse()) {
415 distance = -distance;
416 }
417 if (scrollType == AccessibilityScrollType::SCROLL_HALF) {
418 distance = distance / 2.f;
419 }
420 if (smooth) {
421 float position = layoutInfo_->Offset() + distance;
422 ScrollablePattern::AnimateTo(-position, -1, nullptr, true, false, false);
423 } else {
424 UpdateCurrentOffset(distance, SCROLL_FROM_JUMP);
425 }
426 // AccessibilityEventType::SCROLL_END
427 }
428
ProvideRestoreInfo()429 std::string WaterFlowPattern::ProvideRestoreInfo()
430 {
431 auto jsonObj = JsonUtil::Create(true);
432 jsonObj->Put("beginIndex", GetBeginIndex());
433 Dimension dimension(GetStoredOffset());
434 jsonObj->Put("offset", dimension.ConvertToVp());
435 return jsonObj->ToString();
436 }
437
OnRestoreInfo(const std::string & restoreInfo)438 void WaterFlowPattern::OnRestoreInfo(const std::string& restoreInfo)
439 {
440 auto info = JsonUtil::ParseJsonString(restoreInfo);
441 if (!info->IsValid() || !info->IsObject()) {
442 return;
443 }
444 UpdateStartIndex(info->GetInt("beginIndex"));
445 Dimension dimension(info->GetDouble("offset"), DimensionUnit::VP);
446 SetRestoreOffset(dimension.ConvertToPx());
447 SetScrollAlign(ScrollAlign::START);
448 }
449
GetItemRect(int32_t index) const450 Rect WaterFlowPattern::GetItemRect(int32_t index) const
451 {
452 if (index < 0 || index < layoutInfo_->startIndex_ || index > layoutInfo_->endIndex_) {
453 return Rect();
454 }
455 index += layoutInfo_->footerIndex_ + 1;
456 auto host = GetHost();
457 CHECK_NULL_RETURN(host, Rect());
458 auto item = host->GetChildByIndex(index);
459 CHECK_NULL_RETURN(item, Rect());
460 auto itemGeometry = item->GetGeometryNode();
461 CHECK_NULL_RETURN(itemGeometry, Rect());
462 return Rect(itemGeometry->GetFrameRect().GetX(), itemGeometry->GetFrameRect().GetY(),
463 itemGeometry->GetFrameRect().Width(), itemGeometry->GetFrameRect().Height());
464 }
465
GetItemIndex(double x,double y) const466 int32_t WaterFlowPattern::GetItemIndex(double x, double y) const
467 {
468 for (int32_t index = layoutInfo_->FirstIdx(); index <= layoutInfo_->endIndex_; ++index) {
469 Rect rect = GetItemRect(index);
470 if (rect.IsInRegion({x, y})) {
471 return index;
472 }
473 }
474 return -1;
475 }
476
GetSections() const477 RefPtr<WaterFlowSections> WaterFlowPattern::GetSections() const
478 {
479 return sections_;
480 }
481
GetOrCreateWaterFlowSections()482 RefPtr<WaterFlowSections> WaterFlowPattern::GetOrCreateWaterFlowSections()
483 {
484 if (sections_) {
485 return sections_;
486 }
487 sections_ = AceType::MakeRefPtr<WaterFlowSections>();
488 auto sectionChangeCallback = [weakPattern = WeakClaim(this)](int32_t start, int32_t count) {
489 auto pattern = weakPattern.Upgrade();
490 CHECK_NULL_VOID(pattern);
491 pattern->NotifyDataChange(start, count);
492 };
493 auto callback = [weakPattern = WeakClaim(this)](int32_t start) {
494 auto pattern = weakPattern.Upgrade();
495 CHECK_NULL_VOID(pattern);
496 pattern->AddSectionChangeStartPos(start);
497 };
498 sections_->SetOnDataChange(callback);
499 sections_->SetNotifyDataChange(sectionChangeCallback);
500 return sections_;
501 }
502
OnSectionChanged(int32_t start)503 void WaterFlowPattern::OnSectionChanged(int32_t start)
504 {
505 if (layoutInfo_->Mode() == LayoutMode::SLIDING_WINDOW && keepContentPosition_) {
506 layoutInfo_->InitSegmentsForKeepPositionMode(
507 sections_->GetSectionInfo(), sections_->GetPrevSectionInfo(), start);
508 } else {
509 layoutInfo_->InitSegments(sections_->GetSectionInfo(), start);
510 }
511 }
512
ResetSections()513 void WaterFlowPattern::ResetSections()
514 {
515 if (!sections_) {
516 return;
517 }
518 sections_.Reset();
519 layoutInfo_->Reset();
520 MarkDirtyNodeSelf();
521 }
522
ScrollToIndex(int32_t index,bool smooth,ScrollAlign align,std::optional<float> extraOffset)523 void WaterFlowPattern::ScrollToIndex(int32_t index, bool smooth, ScrollAlign align, std::optional<float> extraOffset)
524 {
525 SetScrollSource(SCROLL_FROM_JUMP);
526 SetScrollAlign(align);
527 StopAnimate();
528 auto footer = footer_.Upgrade();
529 const int32_t itemCnt = footer ? GetChildrenCount() - footer->FrameCount() : GetChildrenCount();
530 if (index > EMPTY_JUMP_INDEX && index < itemCnt) {
531 if (smooth) {
532 SetExtraOffset(extraOffset);
533 if (!ScrollToTargetIndex(index)) {
534 targetIndex_ = index;
535 auto host = GetHost();
536 CHECK_NULL_VOID(host);
537 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
538 }
539 } else {
540 UpdateStartIndex(index);
541 if (extraOffset.has_value()) {
542 layoutInfo_->extraOffset_ = -extraOffset.value();
543 }
544 }
545 }
546 FireAndCleanScrollingListener();
547 }
548
IsOutOfBoundary(bool useCurrentDelta)549 bool WaterFlowPattern::IsOutOfBoundary(bool useCurrentDelta)
550 {
551 return layoutInfo_->OutOfBounds();
552 }
553
SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect> & scrollEffect)554 void WaterFlowPattern::SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect>& scrollEffect)
555 {
556 scrollEffect->SetCurrentPositionCallback([weak = AceType::WeakClaim(this)]() -> double {
557 auto pattern = weak.Upgrade();
558 CHECK_NULL_RETURN(pattern, 0.0);
559 return pattern->layoutInfo_->CurrentPos();
560 });
561 scrollEffect->SetLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
562 auto pattern = weak.Upgrade();
563 CHECK_NULL_RETURN(pattern, 0.0);
564 return pattern->layoutInfo_->BottomFinalPos(pattern->GetMainContentSize());
565 });
566 scrollEffect->SetTrailingCallback([weak = AceType::WeakClaim(this)]() -> double {
567 auto pattern = weak.Upgrade();
568 CHECK_NULL_RETURN(pattern, 0.0);
569 return pattern->layoutInfo_->TopFinalPos();
570 });
571 scrollEffect->SetInitLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
572 auto pattern = weak.Upgrade();
573 CHECK_NULL_RETURN(pattern, 0.0);
574 return pattern->layoutInfo_->BottomFinalPos(pattern->GetMainContentSize());
575 });
576 scrollEffect->SetInitTrailingCallback([weak = AceType::WeakClaim(this)]() -> double {
577 auto pattern = weak.Upgrade();
578 CHECK_NULL_RETURN(pattern, 0.0);
579 return pattern->layoutInfo_->TopFinalPos();
580 });
581 }
582
MarkDirtyNodeSelf()583 void WaterFlowPattern::MarkDirtyNodeSelf()
584 {
585 auto host = GetHost();
586 CHECK_NULL_VOID(host);
587 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
588 }
589
OnScrollEndCallback()590 void WaterFlowPattern::OnScrollEndCallback()
591 {
592 if (AnimateStoped()) {
593 scrollStop_ = true;
594 }
595 CheckMisalignment(layoutInfo_);
596 MarkDirtyNodeSelf();
597 }
598
OnAnimateStop()599 void WaterFlowPattern::OnAnimateStop()
600 {
601 if (!GetIsDragging() || GetScrollAbort()) {
602 scrollStop_ = true;
603 }
604 CheckMisalignment(layoutInfo_);
605 MarkDirtyNodeSelf();
606 }
607
AnimateTo(float position,float duration,const RefPtr<Curve> & curve,bool smooth,bool canOverScroll,bool useTotalOffset)608 void WaterFlowPattern::AnimateTo(
609 float position, float duration, const RefPtr<Curve>& curve, bool smooth, bool canOverScroll, bool useTotalOffset)
610 {
611 if (layoutInfo_->Mode() == WaterFlowLayoutMode::SLIDING_WINDOW) {
612 return;
613 }
614 ScrollablePattern::AnimateTo(position, duration, curve, smooth, canOverScroll);
615 }
616
ScrollTo(float position)617 void WaterFlowPattern::ScrollTo(float position)
618 {
619 if (layoutInfo_->Mode() == WaterFlowLayoutMode::SLIDING_WINDOW) {
620 return;
621 }
622 ScrollablePattern::ScrollTo(position);
623 }
624
NeedRender()625 bool WaterFlowPattern::NeedRender()
626 {
627 auto host = GetHost();
628 CHECK_NULL_RETURN(host, false);
629 auto geometryNode = host->GetGeometryNode();
630 CHECK_NULL_RETURN(geometryNode, false);
631 auto size = geometryNode->GetPaddingSize();
632
633 auto needRender = lastSize_ != size;
634 lastSize_ = size;
635
636 auto property = host->GetLayoutProperty();
637 CHECK_NULL_RETURN(host, false);
638 needRender = property->GetPaddingProperty() != nullptr || needRender;
639 auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
640 CHECK_NULL_RETURN(paintProperty, needRender);
641 needRender |= paintProperty->GetFadingEdge().value_or(false);
642 needRender |= paintProperty->GetContentClip().has_value();
643 return needRender;
644 }
645
ResetLayoutInfo()646 void WaterFlowPattern::ResetLayoutInfo()
647 {
648 layoutInfo_->Reset();
649 if (sections_) {
650 layoutInfo_->InitSegments(sections_->GetSectionInfo(), 0);
651 }
652 }
653
AddFooter(const RefPtr<NG::UINode> & footer)654 void WaterFlowPattern::AddFooter(const RefPtr<NG::UINode>& footer)
655 {
656 // assume this is always before other children are modified, because it's called during State update.
657 auto host = GetHost();
658 CHECK_NULL_VOID(host);
659 auto prevFooter = footer_.Upgrade();
660 if (!prevFooter) {
661 host->AddChild(footer);
662 layoutInfo_->footerIndex_ = 0;
663 } else {
664 host->ReplaceChild(prevFooter, footer);
665 }
666 footer_ = footer;
667 footer->SetActive(false);
668 }
669
SetLayoutMode(LayoutMode mode)670 void WaterFlowPattern::SetLayoutMode(LayoutMode mode)
671 {
672 if (!layoutInfo_ || mode != layoutInfo_->Mode()) {
673 layoutInfo_ = WaterFlowLayoutInfoBase::Create(mode);
674 MarkDirtyNodeSelf();
675 }
676 }
677
GetChildrenCount() const678 int32_t WaterFlowPattern::GetChildrenCount() const
679 {
680 auto host = GetHost();
681 if (host) {
682 return host->GetTotalChildCount();
683 }
684 return 0;
685 }
686
NotifyDataChange(int32_t index,int32_t count)687 void WaterFlowPattern::NotifyDataChange(int32_t index, int32_t count)
688 {
689 if (layoutInfo_->Mode() == LayoutMode::SLIDING_WINDOW && keepContentPosition_) {
690 if (footer_.Upgrade()) {
691 layoutInfo_->NotifyDataChange(index - 1, count);
692 } else {
693 layoutInfo_->NotifyDataChange(index, count);
694 }
695 }
696 }
697
GetScopeFocusAlgorithm()698 ScopeFocusAlgorithm WaterFlowPattern::GetScopeFocusAlgorithm()
699 {
700 return { layoutInfo_->axis_ == Axis::VERTICAL, true, ScopeType::OTHERS,
701 [wp = WeakClaim(this)](
702 FocusStep step, const WeakPtr<FocusHub>& currFocusNode, WeakPtr<FocusHub>& nextFocusNode) {
703 auto self = wp.Upgrade();
704 if (self) {
705 nextFocusNode = self->GetNextFocusNode(step, currFocusNode);
706 }
707 } };
708 }
709
GetNextFocusNode(FocusStep step,const WeakPtr<FocusHub> & currentFocusNode)710 WeakPtr<FocusHub> WaterFlowPattern::GetNextFocusNode(FocusStep step, const WeakPtr<FocusHub>& currentFocusNode)
711 {
712 auto cur = currentFocusNode.Upgrade();
713 CHECK_NULL_RETURN(cur, nullptr);
714 auto host = GetHost();
715 CHECK_NULL_RETURN(host, nullptr);
716 int32_t curIdx = host->GetChildTrueIndex(cur->GetFrameNode());
717 int32_t diff = 0;
718 switch (step) {
719 case FocusStep::DOWN:
720 case FocusStep::DOWN_END:
721 case FocusStep::RIGHT:
722 case FocusStep::RIGHT_END:
723 case FocusStep::TAB:
724 diff = 1;
725 break;
726 case FocusStep::LEFT:
727 case FocusStep::LEFT_END:
728 case FocusStep::UP:
729 case FocusStep::UP_END:
730 case FocusStep::SHIFT_TAB:
731 diff = -1;
732 break;
733 default:
734 return currentFocusNode;
735 }
736 int32_t idx = curIdx + diff;
737 int32_t footerOffset = layoutInfo_->footerIndex_ + 1; // 1 if footer present, 0 if not
738 while (idx - footerOffset >= 0 && idx < GetChildrenCount()) {
739 int32_t itemIdx = idx - footerOffset;
740 if (itemIdx >= layoutInfo_->endIndex_ || itemIdx <= layoutInfo_->startIndex_) {
741 ScrollToIndex(itemIdx, false, ScrollAlign::AUTO);
742 host->SetActive();
743 auto context = host->GetContext();
744 if (context) {
745 context->FlushUITaskWithSingleDirtyNode(host);
746 }
747 }
748 auto next = host->GetChildByIndex(idx);
749 CHECK_NULL_RETURN(next, nullptr);
750 auto focus = next->GetHostNode()->GetFocusHub();
751 if (focus && focus->IsFocusable()) {
752 return focus;
753 }
754 idx += diff;
755 }
756 return nullptr;
757 }
758
GetScrollIndexAbility()759 std::function<bool(int32_t)> WaterFlowPattern::GetScrollIndexAbility()
760 {
761 return [wp = WeakClaim(this)](int32_t index) -> bool {
762 auto self = wp.Upgrade();
763 CHECK_NULL_RETURN(self, false);
764 if (index == FocusHub::SCROLL_TO_HEAD) {
765 self->ScrollToEdge(ScrollEdgeType::SCROLL_TOP, false);
766 } else if (index == FocusHub::SCROLL_TO_TAIL) {
767 self->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false);
768 } else {
769 self->ScrollToIndex(index, false, ScrollAlign::AUTO);
770 }
771 return true;
772 };
773 }
774
DumpAdvanceInfo()775 void WaterFlowPattern::DumpAdvanceInfo()
776 {
777 auto property = GetLayoutProperty<WaterFlowLayoutProperty>();
778 CHECK_NULL_VOID(property);
779 ScrollablePattern::DumpAdvanceInfo();
780 auto info = DynamicCast<WaterFlowLayoutInfo>(layoutInfo_);
781 std::vector<std::string> scrollAlign = { "START", "CENTER", "END", "AUTO", "NONE" };
782
783 DumpLog::GetInstance().AddDesc("currentOffset:" + std::to_string(info->currentOffset_));
784 DumpLog::GetInstance().AddDesc("prevOffset:" + std::to_string(prevOffset_));
785 DumpLog::GetInstance().AddDesc("lastMainSize:" + std::to_string(info->lastMainSize_));
786 DumpLog::GetInstance().AddDesc("maxHeight:" + std::to_string(info->maxHeight_));
787 DumpLog::GetInstance().AddDesc("startIndex:" + std::to_string(info->startIndex_));
788 DumpLog::GetInstance().AddDesc("endIndex:" + std::to_string(info->endIndex_));
789 DumpLog::GetInstance().AddDesc("jumpIndex:" + std::to_string(info->jumpIndex_));
790 DumpLog::GetInstance().AddDesc("childrenCount:" + std::to_string(info->childrenCount_));
791
792 DumpLog::GetInstance().AddDesc("RowsTemplate:", property->GetRowsTemplate()->c_str());
793 DumpLog::GetInstance().AddDesc("ColumnsTemplate:", property->GetColumnsTemplate()->c_str());
794 DumpLog::GetInstance().AddDesc("CachedCount:" + std::to_string(property->GetCachedCount().value_or(1)));
795 DumpLog::GetInstance().AddDesc("ScrollAlign:" + scrollAlign[static_cast<int32_t>(layoutInfo_->align_)]);
796
797 property->IsReverse() ? DumpLog::GetInstance().AddDesc("isReverse:true")
798 : DumpLog::GetInstance().AddDesc("isReverse:false");
799 info->itemStart_ ? DumpLog::GetInstance().AddDesc("itemStart:true")
800 : DumpLog::GetInstance().AddDesc("itemStart:false");
801 info->itemEnd_ ? DumpLog::GetInstance().AddDesc("itemEnd:true") : DumpLog::GetInstance().AddDesc("itemEnd:false");
802 info->offsetEnd_ ? DumpLog::GetInstance().AddDesc("offsetEnd:true")
803 : DumpLog::GetInstance().AddDesc("offsetEnd:false");
804 footer_.Upgrade() ? DumpLog::GetInstance().AddDesc("footer:true") : DumpLog::GetInstance().AddDesc("footer:false");
805
806 property->GetItemMinSize().has_value()
807 ? DumpLog::GetInstance().AddDesc("ItemMinSize:" + property->GetItemMinSize().value().ToString())
808 : DumpLog::GetInstance().AddDesc("ItemMinSize:null");
809 property->GetItemMaxSize().has_value()
810 ? DumpLog::GetInstance().AddDesc("ItemMaxSize:" + property->GetItemMaxSize().value().ToString())
811 : DumpLog::GetInstance().AddDesc("ItemMaxSize:null");
812
813 if (sections_) {
814 DumpLog::GetInstance().AddDesc("-----------start print sections_------------");
815 std::string res = std::string("");
816 int32_t index = 0;
817 for (const auto& section : sections_->GetSectionInfo()) {
818 res.append("[section:" + std::to_string(index) + "]");
819 res.append("{ itemCount:" + std::to_string(section.itemsCount) + " },")
820 .append("{ crossCount:" + std::to_string(section.crossCount.value_or(1)) + " },")
821 .append("{ columnsGap:" + section.columnsGap.value_or(Dimension(0.0)).ToString() + " },")
822 .append("{ rowsGap:" + section.rowsGap.value_or(Dimension(0.0)).ToString() + " },")
823 .append("{ margin:[" + section.margin.value_or(MarginProperty()).ToString() + " ]}");
824 DumpLog::GetInstance().AddDesc(res);
825 res.clear();
826 index++;
827 }
828 DumpLog::GetInstance().AddDesc("-----------end print sections_------------");
829 }
830 }
831
GetChildrenExpandedSize()832 SizeF WaterFlowPattern::GetChildrenExpandedSize()
833 {
834 auto viewSize = GetViewSizeMinusPadding();
835 auto axis = GetAxis();
836 float estimatedHeight = 0.0f;
837 if (layoutInfo_->Mode() != LayoutMode::SLIDING_WINDOW) {
838 auto info = DynamicCast<WaterFlowLayoutInfo>(layoutInfo_);
839 estimatedHeight = info->EstimateContentHeight();
840 }
841
842 if (axis == Axis::VERTICAL) {
843 return SizeF(viewSize.Width(), estimatedHeight);
844 } else if (axis == Axis::HORIZONTAL) {
845 return SizeF(estimatedHeight, viewSize.Height());
846 }
847 return SizeF();
848 }
849 } // namespace OHOS::Ace::NG