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