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