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