1 /*
2  * Copyright (c) 2022-2023 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/list/list_item_pattern.h"
17 
18 #include "base/geometry/axis.h"
19 #include "base/geometry/ng/size_t.h"
20 #include "base/log/dump_log.h"
21 #include "base/memory/ace_type.h"
22 #include "base/utils/utils.h"
23 #include "core/components_ng/pattern/list/list_item_group_layout_property.h"
24 #include "core/components/common/properties/color.h"
25 #include "core/components_ng/base/inspector_filter.h"
26 #include "core/components_ng/pattern/list/list_item_layout_algorithm.h"
27 #include "core/components_ng/pattern/list/list_item_layout_property.h"
28 #include "core/components_ng/pattern/list/list_pattern.h"
29 #include "core/components_ng/property/property.h"
30 #include "core/components_v2/inspector/inspector_constants.h"
31 #include "core/common/container.h"
32 
33 namespace OHOS::Ace::NG {
34 namespace {
35 constexpr float SWIPER_TH = 0.25f;
36 constexpr float NEW_SWIPER_TH = 0.5f;
37 constexpr float SWIPER_SPEED_TH = 1500.f;
38 constexpr float SWIPE_RATIO = 0.6f;
39 constexpr float NEW_SWIPE_RATIO = 1.848f;
40 constexpr float SWIPE_SPRING_MASS = 1.f;
41 constexpr float SWIPE_SPRING_STIFFNESS = 228.f;
42 constexpr float SWIPE_SPRING_DAMPING = 30.f;
43 constexpr int32_t DELETE_ANIMATION_DURATION = 400;
44 constexpr Color ITEM_FILL_COLOR = Color(0x1A0A59f7);
45 } // namespace
46 
OnAttachToFrameNode()47 void ListItemPattern::OnAttachToFrameNode()
48 {
49     auto host = GetHost();
50     CHECK_NULL_VOID(host);
51     if (listItemStyle_ == V2::ListItemStyle::CARD) {
52         SetListItemDefaultAttributes(host);
53     }
54 }
55 
OnColorConfigurationUpdate()56 void ListItemPattern::OnColorConfigurationUpdate()
57 {
58     if (listItemStyle_ != V2::ListItemStyle::CARD) {
59         return;
60     }
61     auto listItemNode = GetHost();
62     CHECK_NULL_VOID(listItemNode);
63     auto renderContext = listItemNode->GetRenderContext();
64     CHECK_NULL_VOID(renderContext);
65     auto pipeline = listItemNode->GetContext();
66     CHECK_NULL_VOID(pipeline);
67     auto listItemTheme = pipeline->GetTheme<ListItemTheme>();
68     CHECK_NULL_VOID(listItemTheme);
69 
70     renderContext->UpdateBackgroundColor(listItemTheme->GetItemDefaultColor());
71 }
72 
SetListItemDefaultAttributes(const RefPtr<FrameNode> & listItemNode)73 void ListItemPattern::SetListItemDefaultAttributes(const RefPtr<FrameNode>& listItemNode)
74 {
75     auto renderContext = listItemNode->GetRenderContext();
76     CHECK_NULL_VOID(renderContext);
77     auto layoutProperty = listItemNode->GetLayoutProperty<ListItemLayoutProperty>();
78     CHECK_NULL_VOID(layoutProperty);
79     auto pipeline = GetContext();
80     CHECK_NULL_VOID(pipeline);
81     auto listItemTheme = pipeline->GetTheme<ListItemTheme>();
82     CHECK_NULL_VOID(listItemTheme);
83 
84     renderContext->UpdateBackgroundColor(listItemTheme->GetItemDefaultColor());
85 
86     PaddingProperty itemPadding;
87     itemPadding.left = CalcLength(listItemTheme->GetItemDefaultLeftPadding());
88     itemPadding.right = CalcLength(listItemTheme->GetItemDefaultRightPadding());
89     layoutProperty->UpdatePadding(itemPadding);
90 
91     layoutProperty->UpdateUserDefinedIdealSize(
92         CalcSize(CalcLength(1.0, DimensionUnit::PERCENT), CalcLength(listItemTheme->GetItemDefaultHeight())));
93     renderContext->UpdateBorderRadius(listItemTheme->GetItemDefaultBorderRadius());
94 }
95 
CreateLayoutAlgorithm()96 RefPtr<LayoutAlgorithm> ListItemPattern::CreateLayoutAlgorithm()
97 {
98     auto host = GetHost();
99     CHECK_NULL_RETURN(host, nullptr);
100     auto listItemEventHub = host->GetEventHub<ListItemEventHub>();
101     CHECK_NULL_RETURN(listItemEventHub, nullptr);
102     if (!HasStartNode() && !HasEndNode() && !listItemEventHub->GetStartOnDelete() &&
103         !listItemEventHub->GetEndOnDelete()) {
104         return MakeRefPtr<BoxLayoutAlgorithm>();
105     }
106     auto layoutAlgorithm = MakeRefPtr<ListItemLayoutAlgorithm>(startNodeIndex_, endNodeIndex_, childNodeIndex_);
107     layoutAlgorithm->SetAxis(axis_);
108     layoutAlgorithm->SetStartNodeSize(startNodeSize_);
109     layoutAlgorithm->SetEndNodeSize(endNodeSize_);
110     layoutAlgorithm->SetCurOffset(curOffset_);
111     layoutAlgorithm->SetHasStartDeleteArea(hasStartDeleteArea_);
112     layoutAlgorithm->SetHasEndDeleteArea(hasEndDeleteArea_);
113     return layoutAlgorithm;
114 }
115 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)116 bool ListItemPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
117 {
118     if (config.skipMeasure && config.skipLayout) {
119         return false;
120     }
121     isLayouted_ = true;
122     if (!HasStartNode() && !HasEndNode()) {
123         return false;
124     }
125     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
126     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
127     auto layoutAlgorithm = DynamicCast<ListItemLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
128     CHECK_NULL_RETURN(layoutAlgorithm, false);
129     startNodeSize_ = layoutAlgorithm->GetStartNodeSize();
130     endNodeSize_ = layoutAlgorithm->GetEndNodeSize();
131     if (axis_ != GetAxis()) {
132         ChangeAxis(GetAxis());
133     }
134     return false;
135 }
136 
SetStartNode(const RefPtr<NG::UINode> & startNode)137 void ListItemPattern::SetStartNode(const RefPtr<NG::UINode>& startNode)
138 {
139     auto host = GetHost();
140     CHECK_NULL_VOID(host);
141     if (startNode) {
142         if (!HasStartNode()) {
143             host->AddChild(startNode);
144             startNodeIndex_ = host->GetChildIndexById(startNode->GetId());
145             if (childNodeIndex_ >= startNodeIndex_) {
146                 childNodeIndex_++;
147             }
148             if (endNodeIndex_ >= startNodeIndex_) {
149                 endNodeIndex_++;
150             }
151         } else {
152             host->ReplaceChild(host->GetChildAtIndex(startNodeIndex_), startNode);
153             host->MarkDirtyNode(PROPERTY_UPDATE_BY_CHILD_REQUEST);
154         }
155     } else if (HasStartNode()) {
156         if (NonNegative(curOffset_)) {
157             curOffset_ = 0.0f;
158             isDragging_ = false;
159         }
160         host->RemoveChildAtIndex(startNodeIndex_);
161         host->MarkDirtyNode(PROPERTY_UPDATE_BY_CHILD_REQUEST);
162         if (endNodeIndex_ > startNodeIndex_) {
163             endNodeIndex_--;
164         }
165         if (childNodeIndex_ > startNodeIndex_) {
166             childNodeIndex_--;
167         }
168         startNodeIndex_ = -1;
169     }
170 }
171 
SetEndNode(const RefPtr<NG::UINode> & endNode)172 void ListItemPattern::SetEndNode(const RefPtr<NG::UINode>& endNode)
173 {
174     auto host = GetHost();
175     CHECK_NULL_VOID(host);
176     if (endNode) {
177         if (!HasEndNode()) {
178             host->AddChild(endNode);
179             endNodeIndex_ = host->GetChildIndexById(endNode->GetId());
180             if (childNodeIndex_ >= endNodeIndex_) {
181                 childNodeIndex_++;
182             }
183             if (startNodeIndex_ >= endNodeIndex_) {
184                 startNodeIndex_++;
185             }
186         } else {
187             host->ReplaceChild(host->GetChildAtIndex(endNodeIndex_), endNode);
188             host->MarkDirtyNode(PROPERTY_UPDATE_BY_CHILD_REQUEST);
189         }
190     } else if (HasEndNode()) {
191         if (NonPositive(curOffset_)) {
192             curOffset_ = 0.0f;
193             isDragging_ = false;
194         }
195         host->RemoveChildAtIndex(endNodeIndex_);
196         host->MarkDirtyNode(PROPERTY_UPDATE_BY_CHILD_REQUEST);
197         if (startNodeIndex_ > endNodeIndex_) {
198             startNodeIndex_--;
199         }
200         if (childNodeIndex_ > endNodeIndex_) {
201             childNodeIndex_--;
202         }
203         endNodeIndex_ = -1;
204     }
205 }
206 
OnDidPop()207 void ListItemPattern::OnDidPop()
208 {
209     if (endNodeIndex_ >= 0 && endNodeIndex_ < childNodeIndex_) {
210         auto host = GetHost();
211         CHECK_NULL_VOID(host);
212         auto childNode = host->GetChildAtIndex(childNodeIndex_);
213         CHECK_NULL_VOID(childNode);
214         auto endNode = host->GetChildAtIndex(endNodeIndex_);
215         CHECK_NULL_VOID(endNode);
216         endNode->MovePosition(-1);
217         endNodeIndex_ = host->GetChildIndexById(endNode->GetId());
218         childNodeIndex_--;
219     }
220 }
221 
GetContentSize() const222 SizeF ListItemPattern::GetContentSize() const
223 {
224     auto host = GetHost();
225     CHECK_NULL_RETURN(host, {});
226     auto geometryNode = host->GetGeometryNode();
227     CHECK_NULL_RETURN(geometryNode, {});
228     return geometryNode->GetPaddingSize();
229 }
230 
GetListFrameNode() const231 RefPtr<FrameNode> ListItemPattern::GetListFrameNode() const
232 {
233     auto host = GetHost();
234     CHECK_NULL_RETURN(host, nullptr);
235     auto parent = host->GetParent();
236     RefPtr<FrameNode> frameNode = AceType::DynamicCast<FrameNode>(parent);
237     while (parent && (!frameNode || (parent->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG))) {
238         parent = parent->GetParent();
239         frameNode = AceType::DynamicCast<FrameNode>(parent);
240     }
241     return frameNode;
242 }
243 
GetParentFrameNode() const244 RefPtr<FrameNode> ListItemPattern::GetParentFrameNode() const
245 {
246     auto host = GetHost();
247     CHECK_NULL_RETURN(host, nullptr);
248     auto parent = host->GetParent();
249     RefPtr<FrameNode> frameNode = AceType::DynamicCast<FrameNode>(parent);
250     while (parent && !frameNode) {
251         parent = parent->GetParent();
252         frameNode = AceType::DynamicCast<FrameNode>(parent);
253     }
254     return frameNode;
255 }
256 
GetAxis() const257 Axis ListItemPattern::GetAxis() const
258 {
259     auto frameNode = GetListFrameNode();
260     CHECK_NULL_RETURN(frameNode, Axis::VERTICAL);
261     auto layoutProperty = frameNode->GetLayoutProperty<ListLayoutProperty>();
262     CHECK_NULL_RETURN(layoutProperty, Axis::VERTICAL);
263     return layoutProperty->GetListDirection().value_or(Axis::VERTICAL);
264 }
265 
SetSwiperItemForList()266 void ListItemPattern::SetSwiperItemForList()
267 {
268     auto frameNode = GetListFrameNode();
269     CHECK_NULL_VOID(frameNode);
270     auto listPattern = frameNode->GetPattern<ListPattern>();
271     CHECK_NULL_VOID(listPattern);
272     listPattern->SetSwiperItem(AceType::WeakClaim(this));
273 }
274 
SetOffsetChangeCallBack(OnOffsetChangeFunc && offsetChangeCallback)275 void ListItemPattern::SetOffsetChangeCallBack(OnOffsetChangeFunc&& offsetChangeCallback)
276 {
277     auto host = GetHost();
278     CHECK_NULL_VOID(host);
279     auto listItemEventHub = host->GetEventHub<ListItemEventHub>();
280     CHECK_NULL_VOID(listItemEventHub);
281     listItemEventHub->SetOnOffsetChangeOffset(std::move(offsetChangeCallback));
282 }
283 
CloseSwipeAction(OnFinishFunc && onFinishCallback)284 void ListItemPattern::CloseSwipeAction(OnFinishFunc&& onFinishCallback)
285 {
286     onFinishEvent_ = onFinishCallback;
287     ResetSwipeStatus(true);
288 }
289 
OnModifyDone()290 void ListItemPattern::OnModifyDone()
291 {
292     auto host = GetHost();
293     CHECK_NULL_VOID(host);
294     auto listItemEventHub = host->GetEventHub<ListItemEventHub>();
295     CHECK_NULL_VOID(listItemEventHub);
296     Pattern::OnModifyDone();
297     InitListItemCardStyleForList();
298     if (!listItemEventHub->HasStateStyle(UI_STATE_SELECTED)) {
299         auto context = host->GetRenderContext();
300         CHECK_NULL_VOID(context);
301         context->BlendBgColor(GetBlendGgColor());
302     }
303     if (HasStartNode() || HasEndNode() || listItemEventHub->GetStartOnDelete() || listItemEventHub->GetEndOnDelete()) {
304         auto axis = GetAxis();
305         bool axisChanged = axis_ != axis;
306         axis_ = axis;
307         InitSwiperAction(axisChanged);
308         return;
309     }
310     auto gestureHub = listItemEventHub->GetOrCreateGestureEventHub();
311     CHECK_NULL_VOID(gestureHub);
312     gestureHub->RemovePanEvent(panEvent_);
313     panEvent_.Reset();
314     springController_.Reset();
315     SetAccessibilityAction();
316 }
317 
GetEdgeEffect()318 V2::SwipeEdgeEffect ListItemPattern::GetEdgeEffect()
319 {
320     auto layoutProperty = GetLayoutProperty<ListItemLayoutProperty>();
321     CHECK_NULL_RETURN(layoutProperty, V2::SwipeEdgeEffect::Spring);
322     return layoutProperty->GetEdgeEffect().value_or(V2::SwipeEdgeEffect::Spring);
323 }
324 
MarkDirtyNode()325 void ListItemPattern::MarkDirtyNode()
326 {
327     auto host = GetHost();
328     CHECK_NULL_VOID(host);
329     if (LessOrEqual(curOffset_, startNodeSize_) && LessOrEqual(-curOffset_, endNodeSize_)) {
330         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
331         return;
332     }
333     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
334 }
335 
ChangeAxis(Axis axis)336 void ListItemPattern::ChangeAxis(Axis axis)
337 {
338     auto host = GetHost();
339     CHECK_NULL_VOID(host);
340     auto listItemEventHub = host->GetEventHub<ListItemEventHub>();
341     CHECK_NULL_VOID(listItemEventHub);
342     axis_ = axis;
343     if (HasStartNode() || HasEndNode() || listItemEventHub->GetStartOnDelete() || listItemEventHub->GetEndOnDelete()) {
344         InitSwiperAction(true);
345     }
346 }
347 
InitSwiperAction(bool axisChanged)348 void ListItemPattern::InitSwiperAction(bool axisChanged)
349 {
350     bool isPanInit = false;
351     if (!panEvent_) {
352         auto weak = AceType::WeakClaim(this);
353         auto actionStartTask = [weak](const GestureEvent& info) {
354             auto pattern = weak.Upgrade();
355             CHECK_NULL_VOID(pattern);
356             auto frameNode = pattern->GetListFrameNode();
357             CHECK_NULL_VOID(frameNode);
358             auto listPattern = frameNode->GetPattern<ListPattern>();
359             CHECK_NULL_VOID(listPattern);
360             if (!listPattern->CanReplaceSwiperItem()) {
361                 return;
362             }
363             pattern->HandleDragStart(info);
364         };
365 
366         auto actionUpdateTask = [weak](const GestureEvent& info) {
367             auto pattern = weak.Upgrade();
368             CHECK_NULL_VOID(pattern);
369             auto frameNode = pattern->GetListFrameNode();
370             CHECK_NULL_VOID(frameNode);
371             auto listPattern = frameNode->GetPattern<ListPattern>();
372             CHECK_NULL_VOID(listPattern);
373             if (!listPattern->IsCurrentSwiperItem(weak) || !pattern->isDragging_) {
374                 return;
375             }
376             pattern->HandleDragUpdate(info);
377         };
378 
379         auto actionEndTask = [weak](const GestureEvent& info) {
380             auto pattern = weak.Upgrade();
381             CHECK_NULL_VOID(pattern);
382             auto frameNode = pattern->GetListFrameNode();
383             CHECK_NULL_VOID(frameNode);
384             auto listPattern = frameNode->GetPattern<ListPattern>();
385             CHECK_NULL_VOID(listPattern);
386             if (!listPattern->IsCurrentSwiperItem(weak) || !pattern->isDragging_) {
387                 return;
388             }
389             pattern->HandleDragEnd(info);
390         };
391 
392         auto actionCancelTask = [weak]() {
393             auto pattern = weak.Upgrade();
394             CHECK_NULL_VOID(pattern);
395             auto frameNode = pattern->GetListFrameNode();
396             CHECK_NULL_VOID(frameNode);
397             auto listPattern = frameNode->GetPattern<ListPattern>();
398             CHECK_NULL_VOID(listPattern);
399             if (!listPattern->IsCurrentSwiperItem(weak) || !pattern->isDragging_) {
400                 return;
401             }
402             GestureEvent info;
403             pattern->HandleDragEnd(info);
404         };
405         panEvent_ = MakeRefPtr<PanEvent>(std::move(actionStartTask), std::move(actionUpdateTask),
406             std::move(actionEndTask), std::move(actionCancelTask));
407         isPanInit = true;
408     }
409     if (isPanInit || axisChanged) {
410         auto host = GetHost();
411         CHECK_NULL_VOID(host);
412         auto hub = host->GetEventHub<EventHub>();
413         CHECK_NULL_VOID(hub);
414         auto gestureHub = hub->GetOrCreateGestureEventHub();
415         CHECK_NULL_VOID(gestureHub);
416         PanDirection panDirection = {
417             .type = axis_ == Axis::HORIZONTAL ? PanDirection::VERTICAL : PanDirection::HORIZONTAL,
418         };
419         gestureHub->AddPanEvent(panEvent_, panDirection, 1, DEFAULT_PAN_DISTANCE);
420 
421         startNodeSize_ = 0.0f;
422         endNodeSize_ = 0.0f;
423         float oldOffset = curOffset_;
424         curOffset_ = 0.0f;
425         FireSwipeActionOffsetChange(oldOffset, curOffset_);
426     }
427     if (!springController_) {
428         springController_ = CREATE_ANIMATOR(PipelineBase::GetCurrentContext());
429     } else if (axisChanged) {
430         springController_->Stop();
431     }
432 }
433 
HandleDragStart(const GestureEvent & info)434 void ListItemPattern::HandleDragStart(const GestureEvent& info)
435 {
436     if (info.GetInputEventType() == InputEventType::AXIS && info.GetSourceTool() == SourceTool::MOUSE) {
437         return;
438     }
439     if (springController_ && !springController_->IsStopped()) {
440         // clear stop listener before stop
441         springController_->ClearStopListeners();
442         springController_->Stop();
443     }
444     isDragging_ = true;
445     SetSwiperItemForList();
446 }
447 
CalculateFriction(float gamma)448 float ListItemPattern::CalculateFriction(float gamma)
449 {
450     float ratio = SWIPE_RATIO;
451     if (GreatOrEqual(gamma, 1.0)) {
452         gamma = 1.0f;
453     }
454     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
455         ratio = NEW_SWIPE_RATIO;
456         return exp(-ratio * gamma);
457     }
458     float result = ratio * std::pow(1.0 - gamma, SQUARE);
459     if (!std::isnan(result) && LessNotEqual(result, 1.0f)) {
460         return result;
461     }
462     return 1.0f;
463 }
464 
GetFriction()465 float ListItemPattern::GetFriction()
466 {
467     if (GreatNotEqual(curOffset_, 0.0f)) {
468         float width = startNodeSize_;
469         float itemWidth = GetContentSize().CrossSize(axis_);
470         if (width < curOffset_) {
471             return CalculateFriction((curOffset_ - width) / (itemWidth - width));
472         }
473     } else if (LessNotEqual(curOffset_, 0.0f)) {
474         float width = endNodeSize_;
475         float itemWidth = GetContentSize().CrossSize(axis_);
476         if (width < -curOffset_) {
477             return CalculateFriction((-curOffset_ - width) / (itemWidth - width));
478         }
479     }
480     return 1.0f;
481 }
482 
ChangeDeleteAreaStage()483 void ListItemPattern::ChangeDeleteAreaStage()
484 {
485     auto host = GetHost();
486     CHECK_NULL_VOID(host);
487     auto listItemEventHub = host->GetEventHub<ListItemEventHub>();
488     CHECK_NULL_VOID(listItemEventHub);
489     auto enterStartDeleteArea = listItemEventHub->GetOnEnterStartDeleteArea();
490     auto enterEndDeleteArea = listItemEventHub->GetOnEnterEndDeleteArea();
491     auto exitStartDeleteArea = listItemEventHub->GetOnExitStartDeleteArea();
492     auto exitEndDeleteArea = listItemEventHub->GetOnExitEndDeleteArea();
493     if (Positive(curOffset_) && hasStartDeleteArea_) {
494         if (GreatOrEqual(curOffset_, startNodeSize_ + startDeleteAreaDistance_)) {
495             if (!inStartDeleteArea_) {
496                 inStartDeleteArea_ = true;
497                 if (enterStartDeleteArea) {
498                     enterStartDeleteArea();
499                 }
500             }
501         } else {
502             if (inStartDeleteArea_) {
503                 inStartDeleteArea_ = false;
504                 if (exitStartDeleteArea) {
505                     exitStartDeleteArea();
506                 }
507             }
508         }
509     }
510     if (Negative(curOffset_) && hasEndDeleteArea_) {
511         if (GreatNotEqual(-curOffset_, endNodeSize_ + endDeleteAreaDistance_)) {
512             if (!inEndDeleteArea_) {
513                 inEndDeleteArea_ = true;
514                 if (enterEndDeleteArea) {
515                     enterEndDeleteArea();
516                 }
517             }
518         } else {
519             if (inEndDeleteArea_) {
520                 inEndDeleteArea_ = false;
521                 if (exitEndDeleteArea) {
522                     exitEndDeleteArea();
523                 }
524             }
525         }
526     }
527 }
528 
UpdatePostion(float delta)529 void ListItemPattern::UpdatePostion(float delta)
530 {
531     auto host = GetHost();
532     CHECK_NULL_VOID(host);
533     auto listItemEventHub = host->GetEventHub<ListItemEventHub>();
534     CHECK_NULL_VOID(listItemEventHub);
535     auto offset = curOffset_;
536     IsRTLAndVertical() ? curOffset_ -= delta : curOffset_ += delta;
537     ChangeDeleteAreaStage();
538     auto edgeEffect = GetEdgeEffect();
539     if (edgeEffect == V2::SwipeEdgeEffect::None) {
540         if (hasStartDeleteArea_) {
541             if (Positive(startNodeSize_) && GreatNotEqual(curOffset_, startNodeSize_ + startDeleteAreaDistance_)) {
542                 curOffset_ = startNodeSize_ + startDeleteAreaDistance_;
543             } else if (startNodeSize_ == 0 && GreatNotEqual(curOffset_, startDeleteAreaDistance_)) {
544                 curOffset_ = startDeleteAreaDistance_;
545             }
546         }
547         if (hasEndDeleteArea_) {
548             if (Positive(endNodeSize_) && GreatNotEqual(-curOffset_, endNodeSize_ + endDeleteAreaDistance_)) {
549                 curOffset_ = -endNodeSize_ - endDeleteAreaDistance_;
550             } else if (endNodeSize_ == 0 && GreatNotEqual(-curOffset_, endDeleteAreaDistance_)) {
551                 curOffset_ = -endDeleteAreaDistance_;
552             }
553         }
554         if (Positive(startNodeSize_) && GreatNotEqual(curOffset_, startNodeSize_) && !hasStartDeleteArea_) {
555             curOffset_ = startNodeSize_;
556         } else if (Positive(endNodeSize_) && GreatNotEqual(-curOffset_, endNodeSize_) && !hasEndDeleteArea_) {
557             curOffset_ = -endNodeSize_;
558         }
559         if ((Negative(curOffset_) && !HasEndNode() && !listItemEventHub->GetEndOnDelete()) ||
560             (Positive(curOffset_) && !HasStartNode() && !listItemEventHub->GetStartOnDelete())) {
561             curOffset_ = 0.0f;
562         }
563     }
564     if (!NearEqual(offset, curOffset_)) {
565         MarkDirtyNode();
566         FireSwipeActionOffsetChange(offset, curOffset_);
567     }
568 }
569 
IsRTLAndVertical() const570 bool ListItemPattern::IsRTLAndVertical() const
571 {
572     auto layoutProperty = GetLayoutProperty<ListItemLayoutProperty>();
573     CHECK_NULL_RETURN(layoutProperty, false);
574     auto layoutDirection = layoutProperty->GetNonAutoLayoutDirection();
575     if (layoutDirection == TextDirection::RTL && axis_ == Axis::VERTICAL) {
576         return true;
577     } else {
578         return false;
579     }
580 }
581 
HandleDragUpdate(const GestureEvent & info)582 void ListItemPattern::HandleDragUpdate(const GestureEvent& info)
583 {
584     if (info.GetInputEventType() == InputEventType::AXIS && info.GetSourceTool() == SourceTool::MOUSE) {
585         return;
586     }
587     auto layoutProperty = GetLayoutProperty<ListItemLayoutProperty>();
588     CHECK_NULL_VOID(layoutProperty);
589     hasStartDeleteArea_ = false;
590     hasEndDeleteArea_ = false;
591     float itemWidth = GetContentSize().CrossSize(axis_);
592     float maxDeleteArea = 0.0f;
593 
594     if (GreatNotEqual(curOffset_, 0.0)) {
595         maxDeleteArea = itemWidth - startNodeSize_;
596         startDeleteAreaDistance_ = static_cast<float>(
597             layoutProperty->GetStartDeleteAreaDistance().value_or(Dimension(0, DimensionUnit::VP)).ConvertToPx());
598         if (GreatNotEqual(startDeleteAreaDistance_, 0.0) && LessNotEqual(startDeleteAreaDistance_, maxDeleteArea)) {
599             hasStartDeleteArea_ = true;
600         }
601     } else if (LessNotEqual(curOffset_, 0.0)) {
602         maxDeleteArea = itemWidth - endNodeSize_;
603         endDeleteAreaDistance_ = static_cast<float>(
604             layoutProperty->GetEndDeleteAreaDistance().value_or(Dimension(0, DimensionUnit::VP)).ConvertToPx());
605         if (GreatNotEqual(endDeleteAreaDistance_, 0.0) && LessNotEqual(endDeleteAreaDistance_, maxDeleteArea)) {
606             hasEndDeleteArea_ = true;
607         }
608     }
609     float delta = info.GetMainDelta();
610     delta *= GetFriction();
611     UpdatePostion(delta);
612 }
613 
StartSpringMotion(float start,float end,float velocity,bool trigOnFinishEvent)614 void ListItemPattern::StartSpringMotion(float start, float end, float velocity, bool trigOnFinishEvent)
615 {
616     if (!springController_) {
617         return;
618     }
619     const RefPtr<SpringProperty> DEFAULT_OVER_SPRING_PROPERTY =
620         AceType::MakeRefPtr<SpringProperty>(SWIPE_SPRING_MASS, SWIPE_SPRING_STIFFNESS, SWIPE_SPRING_DAMPING);
621     if (!springMotion_) {
622         springMotion_ = AceType::MakeRefPtr<SpringMotion>(start, end, velocity, DEFAULT_OVER_SPRING_PROPERTY);
623     } else {
624         springMotion_->Reset(start, end, velocity, DEFAULT_OVER_SPRING_PROPERTY);
625         springMotion_->ClearListeners();
626     }
627     // 10000.0f: use a large value to mask the determination of speed threshold
628     springMotion_->SetVelocityAccuracy(10000.0f);
629     springMotion_->AddListener([weakScroll = AceType::WeakClaim(this), start, end](double position) {
630         auto listItem = weakScroll.Upgrade();
631         CHECK_NULL_VOID(listItem);
632         if (listItem->GetEdgeEffect() == V2::SwipeEdgeEffect::None &&
633             ((GreatNotEqual(end, start) && GreatOrEqual(position, end)) ||
634             (LessNotEqual(end, start) && LessOrEqual(position, end)))) {
635             listItem->springController_->ClearStopListeners();
636             listItem->springController_->Stop();
637             position = end;
638         }
639         if (NearEqual(position, listItem->curOffset_, 1.0) && !listItem->springMotionTraceFlag_) {
640             listItem->springMotionTraceFlag_ = true;
641             AceAsyncTraceBegin(0, TRAILING_ANIMATION);
642         }
643         float delta = listItem->IsRTLAndVertical() ? listItem->curOffset_ - position : position - listItem->curOffset_;
644         listItem->UpdatePostion(delta);
645     });
646     springController_->ClearStopListeners();
647     springController_->PlayMotion(springMotion_);
648     springController_->AddStopListener([weak = AceType::WeakClaim(this), trigOnFinishEvent]() {
649         auto listItem = weak.Upgrade();
650         CHECK_NULL_VOID(listItem);
651         if (NearZero(listItem->curOffset_)) {
652             listItem->FireSwipeActionStateChange(ListItemSwipeIndex::ITEM_CHILD);
653             listItem->ResetNodeSize();
654             listItem->FireSwipeActionOffsetChange(SWIPE_SPRING_MASS, listItem->curOffset_);
655         }
656         if (listItem->springMotionTraceFlag_) {
657             listItem->springMotionTraceFlag_ = false;
658             AceAsyncTraceEnd(0, TRAILING_ANIMATION);
659         }
660         listItem->MarkDirtyNode();
661         if (trigOnFinishEvent) {
662             listItem->FireOnFinshEvent();
663         }
664     });
665 }
666 
DoDeleteAnimation(bool isStartDelete)667 void ListItemPattern::DoDeleteAnimation(bool isStartDelete)
668 {
669     auto host = GetHost();
670     CHECK_NULL_VOID(host);
671     auto renderContext = host->GetRenderContext();
672     CHECK_NULL_VOID(renderContext);
673     auto context = GetContext();
674     CHECK_NULL_VOID(context);
675     float itemWidth = GetContentSize().CrossSize(axis_);
676 
677     AnimationOption option = AnimationOption();
678     option.SetDuration(DELETE_ANIMATION_DURATION);
679     option.SetCurve(Curves::FRICTION);
680     option.SetFillMode(FillMode::FORWARDS);
681     context->OpenImplicitAnimation(option, option.GetCurve(), nullptr);
682     float oldOffset = curOffset_;
683     curOffset_ = isStartDelete ? itemWidth : -itemWidth;
684     FireSwipeActionOffsetChange(oldOffset, curOffset_);
685     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
686     context->FlushUITasks();
687     context->CloseImplicitAnimation();
688     FireSwipeActionStateChange(ListItemSwipeIndex::SWIPER_ACTION);
689 }
690 
FireSwipeActionOffsetChange(float oldOffset,float newOffset)691 void ListItemPattern::FireSwipeActionOffsetChange(float oldOffset, float newOffset)
692 {
693     if (NearEqual(oldOffset, newOffset)) {
694         return;
695     }
696     auto host = GetHost();
697     CHECK_NULL_VOID(host);
698     auto listItemEventHub = host->GetEventHub<ListItemEventHub>();
699     CHECK_NULL_VOID(listItemEventHub);
700     listItemEventHub->FireOffsetChangeEvent(Dimension(newOffset).ConvertToVp());
701 }
702 
FireSwipeActionStateChange(ListItemSwipeIndex newSwiperIndex)703 void ListItemPattern::FireSwipeActionStateChange(ListItemSwipeIndex newSwiperIndex)
704 {
705     auto host = GetHost();
706     CHECK_NULL_VOID(host);
707     TAG_LOGI(AceLogTag::ACE_LIST, "ListItem:%{public}d swiperIndex origin:%{public}d, new:%{public}d, "
708         "curPos:%{public}f", host->GetId(), swiperIndex_, newSwiperIndex, curOffset_);
709     if (newSwiperIndex == swiperIndex_) {
710         return;
711     }
712     auto listItemEventHub = host->GetEventHub<ListItemEventHub>();
713     CHECK_NULL_VOID(listItemEventHub);
714 
715     bool trigStart = GreatNotEqual(curOffset_, 0.0f);
716     switch (newSwiperIndex) {
717         case ListItemSwipeIndex::SWIPER_START:
718         case ListItemSwipeIndex::SWIPER_END:
719             swipeActionState_ = SwipeActionState::EXPANDED;
720             break;
721         case ListItemSwipeIndex::SWIPER_ACTION:
722             swipeActionState_ = SwipeActionState::ACTIONING;
723             break;
724         case ListItemSwipeIndex::ITEM_CHILD:
725         default:
726             if (swiperIndex_ == ListItemSwipeIndex::SWIPER_START) {
727                 trigStart = true;
728             } else if (swiperIndex_ == ListItemSwipeIndex::SWIPER_END) {
729                 trigStart = false;
730             }
731             swipeActionState_ = SwipeActionState::COLLAPSED;
732     }
733     swiperIndex_ = newSwiperIndex;
734     listItemEventHub->FireStateChangeEvent(swipeActionState_, trigStart);
735     UpdateClickJudgeCallback();
736 }
737 
UpdateClickJudgeCallback()738 void ListItemPattern::UpdateClickJudgeCallback()
739 {
740     auto frameNode = GetListFrameNode();
741     CHECK_NULL_VOID(frameNode);
742     auto listPattern = frameNode->GetPattern<ListPattern>();
743     CHECK_NULL_VOID(listPattern);
744     auto scrollableEvent = listPattern->GetScrollableEvent();
745     CHECK_NULL_VOID(scrollableEvent);
746     if (swipeActionState_ == SwipeActionState::COLLAPSED) {
747         TAG_LOGI(AceLogTag::ACE_LIST, "List:%{public}d RemoveClickJudgeCallback", frameNode->GetId());
748         scrollableEvent->SetClickJudgeCallback(nullptr);
749     } else if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TWELVE)) {
750         auto clickJudgeCallback = [weak = WeakClaim(this)](const PointF& localPoint) -> bool {
751             auto item = weak.Upgrade();
752             CHECK_NULL_RETURN(item, false);
753             if (item->swipeActionState_ == SwipeActionState::COLLAPSED) {
754                 return false;
755             }
756             return item->ClickJudge(localPoint);
757         };
758         TAG_LOGI(AceLogTag::ACE_LIST, "List:%{public}d AddClickJudgeCallback", frameNode->GetId());
759         scrollableEvent->SetClickJudgeCallback(clickJudgeCallback);
760     }
761 }
762 
HandleDragEnd(const GestureEvent & info)763 void ListItemPattern::HandleDragEnd(const GestureEvent& info)
764 {
765     if (info.GetInputEventType() == InputEventType::AXIS && info.GetSourceTool() == SourceTool::MOUSE) {
766         return;
767     }
768 
769     auto listPattern = GetListFrameNode()->GetPattern<ListPattern>();
770     CHECK_NULL_VOID(listPattern);
771     listPattern->SetSwiperItemEnd(AceType::WeakClaim(this));
772 
773     auto host = GetHost();
774     CHECK_NULL_VOID(host);
775     auto listItemEventHub = host->GetEventHub<ListItemEventHub>();
776     CHECK_NULL_VOID(listItemEventHub);
777     auto startOnDelete = listItemEventHub->GetStartOnDelete();
778     auto endOnDelete = listItemEventHub->GetEndOnDelete();
779     float end = 0.0f;
780     float friction = GetFriction();
781     float threshold = Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TWELVE) ? NEW_SWIPER_TH : SWIPER_TH;
782     float velocity = IsRTLAndVertical() ? -info.GetMainVelocity() : info.GetMainVelocity();
783     bool reachRightSpeed = velocity > SWIPER_SPEED_TH;
784     bool reachLeftSpeed = -velocity > SWIPER_SPEED_TH;
785     isDragging_ = false;
786 
787     TAG_LOGI(AceLogTag::ACE_LIST, "ListItem:%{public}d HandleDragEnd, velocity:%{public}f, curPos:%{public}f",
788         host->GetId(), velocity, curOffset_);
789     if (swiperIndex_ != ListItemSwipeIndex::SWIPER_ACTION && hasStartDeleteArea_ && !HasStartNode() && startOnDelete &&
790         GreatOrEqual(curOffset_, startDeleteAreaDistance_) && swiperIndex_ != ListItemSwipeIndex::SWIPER_END) {
791         DoDeleteAnimation(true);
792         startOnDelete();
793         return;
794     } else if (hasStartDeleteArea_ && !HasStartNode()) {
795         FireSwipeActionStateChange(ListItemSwipeIndex::ITEM_CHILD);
796     }
797 
798     if (swiperIndex_ != ListItemSwipeIndex::SWIPER_ACTION && hasEndDeleteArea_ && !HasEndNode() && endOnDelete &&
799         GreatOrEqual(-curOffset_, endDeleteAreaDistance_) && swiperIndex_ != ListItemSwipeIndex::SWIPER_START) {
800         DoDeleteAnimation(false);
801         endOnDelete();
802         return;
803     } else if (hasEndDeleteArea_ && !HasEndNode()) {
804         FireSwipeActionStateChange(ListItemSwipeIndex::ITEM_CHILD);
805     }
806 
807     if (GreatNotEqual(curOffset_, 0.0) && HasStartNode()) {
808         float width = startNodeSize_;
809         if (swiperIndex_ == ListItemSwipeIndex::ITEM_CHILD && reachLeftSpeed) {
810             StartSpringMotion(curOffset_, 0, velocity * friction);
811             FireSwipeActionStateChange(ListItemSwipeIndex::ITEM_CHILD);
812             return;
813         }
814         if (swiperIndex_ != ListItemSwipeIndex::SWIPER_ACTION && swiperIndex_ != ListItemSwipeIndex::SWIPER_END &&
815             GreatOrEqual(curOffset_, width + startDeleteAreaDistance_) && hasStartDeleteArea_ && startOnDelete) {
816             DoDeleteAnimation(true);
817             startOnDelete();
818             return;
819         }
820         if (swiperIndex_ == ListItemSwipeIndex::ITEM_CHILD && width > 0 &&
821             (curOffset_ > width * threshold || reachRightSpeed)) {
822             FireSwipeActionStateChange(ListItemSwipeIndex::SWIPER_START);
823         } else if (swiperIndex_ == ListItemSwipeIndex::SWIPER_START && (curOffset_ < width *
824                   (1 - threshold) || (reachLeftSpeed && curOffset_ < width))) {
825             FireSwipeActionStateChange(ListItemSwipeIndex::ITEM_CHILD);
826         } else if (swiperIndex_ == ListItemSwipeIndex::SWIPER_END ||
827                    swiperIndex_ == ListItemSwipeIndex::SWIPER_ACTION) {
828             FireSwipeActionStateChange(ListItemSwipeIndex::ITEM_CHILD);
829         }
830         end = width * static_cast<int32_t>(swiperIndex_);
831     } else if (LessNotEqual(curOffset_, 0.0) && HasEndNode()) {
832         float width = endNodeSize_;
833         if (swiperIndex_ == ListItemSwipeIndex::ITEM_CHILD && reachRightSpeed) {
834             StartSpringMotion(curOffset_, 0, velocity * friction);
835             FireSwipeActionStateChange(ListItemSwipeIndex::ITEM_CHILD);
836             return;
837         }
838         if (swiperIndex_ != ListItemSwipeIndex::SWIPER_ACTION && swiperIndex_ != ListItemSwipeIndex::SWIPER_START &&
839             GreatOrEqual(-curOffset_, width + endDeleteAreaDistance_) && hasEndDeleteArea_ && endOnDelete) {
840             DoDeleteAnimation(false);
841             endOnDelete();
842             return;
843         }
844         if (swiperIndex_ == ListItemSwipeIndex::ITEM_CHILD && width > 0 &&
845             (width * threshold < -curOffset_ || reachLeftSpeed)) {
846             FireSwipeActionStateChange(ListItemSwipeIndex::SWIPER_END);
847         } else if (swiperIndex_ == ListItemSwipeIndex::SWIPER_END && (-curOffset_ < width *
848                   (1 - threshold) || (reachRightSpeed && -curOffset_ < width))) {
849             FireSwipeActionStateChange(ListItemSwipeIndex::ITEM_CHILD);
850         } else if (swiperIndex_ == ListItemSwipeIndex::SWIPER_START ||
851                    swiperIndex_ == ListItemSwipeIndex::SWIPER_ACTION) {
852             FireSwipeActionStateChange(ListItemSwipeIndex::ITEM_CHILD);
853         }
854         end = width * static_cast<int32_t>(swiperIndex_);
855     }
856     StartSpringMotion(curOffset_, end, velocity * friction);
857 }
858 
ResetSwipeStatus(bool calledByUser)859 void ListItemPattern::ResetSwipeStatus(bool calledByUser)
860 {
861     if (swiperIndex_ == ListItemSwipeIndex::ITEM_CHILD) {
862         return;
863     }
864     FireSwipeActionStateChange(ListItemSwipeIndex::ITEM_CHILD);
865     float velocity = 0.0f;
866     if (springMotion_) {
867         velocity = springMotion_->GetCurrentVelocity();
868     }
869     if (springController_ && !springController_->IsStopped()) {
870         // clear stop listener before stop
871         springController_->ClearStopListeners();
872         springController_->Stop();
873     }
874     StartSpringMotion(curOffset_, 0.0f, velocity, calledByUser);
875 }
876 
MarkIsSelected(bool isSelected)877 void ListItemPattern::MarkIsSelected(bool isSelected)
878 {
879     if (isSelected_ != isSelected) {
880         isSelected_ = isSelected;
881         auto eventHub = GetEventHub<ListItemEventHub>();
882         eventHub->FireSelectChangeEvent(isSelected);
883         auto host = GetHost();
884         CHECK_NULL_VOID(host);
885         if (isSelected) {
886             eventHub->UpdateCurrentUIState(UI_STATE_SELECTED);
887             host->OnAccessibilityEvent(AccessibilityEventType::SELECTED);
888         } else {
889             eventHub->ResetCurrentUIState(UI_STATE_SELECTED);
890             host->OnAccessibilityEvent(AccessibilityEventType::CHANGE);
891         }
892         if (!eventHub->HasStateStyle(UI_STATE_SELECTED)) {
893             auto context = host->GetRenderContext();
894             CHECK_NULL_VOID(context);
895             context->BlendBgColor(GetBlendGgColor());
896         }
897     }
898 }
899 
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const900 void ListItemPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
901 {
902     json->PutFixedAttr("selectable", selectable_, filter, FIXED_ATTR_SELECTABLE);
903     json->PutExtAttr("selected", isSelected_, filter);
904 }
905 
SetAccessibilityAction()906 void ListItemPattern::SetAccessibilityAction()
907 {
908     auto host = GetHost();
909     CHECK_NULL_VOID(host);
910     auto listItemAccessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
911     CHECK_NULL_VOID(listItemAccessibilityProperty);
912     listItemAccessibilityProperty->SetActionSelect([weakPtr = WeakClaim(this)]() {
913         const auto& pattern = weakPtr.Upgrade();
914         CHECK_NULL_VOID(pattern);
915         if (!pattern->Selectable()) {
916             return;
917         }
918         auto host = pattern->GetHost();
919         CHECK_NULL_VOID(host);
920         auto context = host->GetRenderContext();
921         CHECK_NULL_VOID(context);
922         context->OnMouseSelectUpdate(false, ITEM_FILL_COLOR, ITEM_FILL_COLOR);
923         pattern->MarkIsSelected(true);
924         context->OnMouseSelectUpdate(true, ITEM_FILL_COLOR, ITEM_FILL_COLOR);
925     });
926 
927     listItemAccessibilityProperty->SetActionClearSelection([weakPtr = WeakClaim(this)]() {
928         const auto& pattern = weakPtr.Upgrade();
929         CHECK_NULL_VOID(pattern);
930         if (!pattern->Selectable()) {
931             return;
932         }
933         auto host = pattern->GetHost();
934         CHECK_NULL_VOID(host);
935         auto context = host->GetRenderContext();
936         CHECK_NULL_VOID(context);
937         pattern->MarkIsSelected(false);
938         context->OnMouseSelectUpdate(false, ITEM_FILL_COLOR, ITEM_FILL_COLOR);
939     });
940 }
941 
InitListItemCardStyleForList()942 void ListItemPattern::InitListItemCardStyleForList()
943 {
944     if (listItemStyle_ == V2::ListItemStyle::CARD) {
945         UpdateListItemAlignToCenter();
946         InitHoverEvent();
947         InitPressEvent();
948         InitDisableEvent();
949     }
950 }
951 
UpdateListItemAlignToCenter()952 void ListItemPattern::UpdateListItemAlignToCenter()
953 {
954     auto frameNode = GetListFrameNode();
955     CHECK_NULL_VOID(frameNode);
956     auto layoutProperty = frameNode->GetLayoutProperty<ListLayoutProperty>();
957     CHECK_NULL_VOID(layoutProperty);
958     if (!layoutProperty->HasListItemAlign()) {
959         layoutProperty->UpdateListItemAlign(V2::ListItemAlign::CENTER);
960     }
961 }
962 
GetBlendGgColor()963 Color ListItemPattern::GetBlendGgColor()
964 {
965     Color color = Color::TRANSPARENT;
966     auto pipeline = PipelineContext::GetCurrentContext();
967     CHECK_NULL_RETURN(pipeline, color);
968     auto theme = pipeline->GetTheme<ListItemTheme>();
969     CHECK_NULL_RETURN(theme, color);
970     if (isSelected_) {
971         auto eventHub = GetEventHub<ListItemEventHub>();
972         CHECK_NULL_RETURN(eventHub, color);
973         if (!eventHub->HasStateStyle(UI_STATE_SELECTED)) {
974             color = theme->GetItemSelectedColor();
975         }
976     }
977     if (isPressed_) {
978         color = color.BlendColor(theme->GetItemPressColor());
979     } else if (isHover_) {
980         color = color.BlendColor(theme->GetItemHoverColor());
981     }
982     return color;
983 }
984 
InitHoverEvent()985 void ListItemPattern::InitHoverEvent()
986 {
987     if (hoverEvent_) {
988         return;
989     }
990     auto host = GetHost();
991     CHECK_NULL_VOID(host);
992     auto eventHub = host->GetEventHub<EventHub>();
993     CHECK_NULL_VOID(eventHub);
994     auto inputHub = eventHub->GetOrCreateInputEventHub();
995     CHECK_NULL_VOID(inputHub);
996     auto hoverTask = [weak = WeakClaim(this)](bool isHover) {
997         auto pattern = weak.Upgrade();
998         if (pattern) {
999             pattern->HandleHoverEvent(isHover, pattern->GetHost());
1000         }
1001     };
1002     hoverEvent_ = MakeRefPtr<InputEvent>(std::move(hoverTask));
1003     inputHub->AddOnHoverEvent(hoverEvent_);
1004 }
1005 
HandleHoverEvent(bool isHover,const RefPtr<NG::FrameNode> & itemNode)1006 void ListItemPattern::HandleHoverEvent(bool isHover, const RefPtr<NG::FrameNode>& itemNode)
1007 {
1008     auto renderContext = itemNode->GetRenderContext();
1009     CHECK_NULL_VOID(renderContext);
1010     auto pipeline = GetContext();
1011     CHECK_NULL_VOID(pipeline);
1012     auto theme = pipeline->GetTheme<ListItemTheme>();
1013     CHECK_NULL_VOID(theme);
1014 
1015     isHover_ = isHover;
1016     auto hoverColor = GetBlendGgColor();
1017     AnimationUtils::BlendBgColorAnimation(
1018         renderContext, hoverColor, theme->GetHoverAnimationDuration(), Curves::FRICTION);
1019 }
1020 
InitPressEvent()1021 void ListItemPattern::InitPressEvent()
1022 {
1023     if (touchListener_) {
1024         return;
1025     }
1026     auto host = GetHost();
1027     CHECK_NULL_VOID(host);
1028     auto gesture = host->GetOrCreateGestureEventHub();
1029     CHECK_NULL_VOID(gesture);
1030     auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
1031         auto pattern = weak.Upgrade();
1032         CHECK_NULL_VOID(pattern);
1033         auto touchType = info.GetTouches().front().GetTouchType();
1034         if (touchType == TouchType::DOWN || touchType == TouchType::UP || touchType == TouchType::CANCEL) {
1035             pattern->HandlePressEvent(touchType == TouchType::DOWN, pattern->GetHost());
1036         }
1037     };
1038     auto touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
1039     gesture->AddTouchEvent(touchListener_);
1040 }
1041 
HandlePressEvent(bool isPressed,const RefPtr<NG::FrameNode> & itemNode)1042 void ListItemPattern::HandlePressEvent(bool isPressed, const RefPtr<NG::FrameNode>& itemNode)
1043 {
1044     auto renderContext = itemNode->GetRenderContext();
1045     CHECK_NULL_VOID(renderContext);
1046     auto pipeline = GetContext();
1047     CHECK_NULL_VOID(pipeline);
1048     auto theme = pipeline->GetTheme<ListItemTheme>();
1049     CHECK_NULL_VOID(theme);
1050     auto duration = isHover_ ? theme->GetHoverToPressAnimationDuration() : theme->GetHoverAnimationDuration();
1051     isPressed_ = isPressed;
1052     Color color = GetBlendGgColor();
1053     AnimationUtils::BlendBgColorAnimation(renderContext, color, duration, Curves::SHARP);
1054 }
1055 
InitDisableEvent()1056 void ListItemPattern::InitDisableEvent()
1057 {
1058     auto host = GetHost();
1059     CHECK_NULL_VOID(host);
1060     auto eventHub = host->GetEventHub<ListItemEventHub>();
1061     CHECK_NULL_VOID(eventHub);
1062     auto renderContext = host->GetRenderContext();
1063     CHECK_NULL_VOID(renderContext);
1064     auto pipeline = PipelineBase::GetCurrentContext();
1065     CHECK_NULL_VOID(pipeline);
1066     auto theme = pipeline->GetTheme<ListItemTheme>();
1067     CHECK_NULL_VOID(theme);
1068     auto userDefineOpacity = renderContext->GetOpacityValue(1.0);
1069 
1070     if (!eventHub->IsDeveloperEnabled()) {
1071         if (selectable_) {
1072             selectable_ = false;
1073         }
1074         enableOpacity_ = renderContext->GetOpacityValue(1.0);
1075         renderContext->UpdateOpacity(theme->GetItemDisabledAlpha());
1076     } else {
1077         if (enableOpacity_.has_value()) {
1078             renderContext->UpdateOpacity(enableOpacity_.value());
1079         } else {
1080             renderContext->UpdateOpacity(userDefineOpacity);
1081         }
1082     }
1083 }
1084 
GetLayouted() const1085 bool ListItemPattern::GetLayouted() const
1086 {
1087     return isLayouted_;
1088 }
1089 
DumpAdvanceInfo()1090 void ListItemPattern::DumpAdvanceInfo()
1091 {
1092     DumpLog::GetInstance().AddDesc("indexInList:" + std::to_string(indexInList_));
1093     DumpLog::GetInstance().AddDesc("indexInListItemGroup:" + std::to_string(indexInListItemGroup_));
1094     DumpLog::GetInstance().AddDesc("swiperAction.startNodeIndex:" + std::to_string(startNodeIndex_));
1095     DumpLog::GetInstance().AddDesc("swiperAction.endNodeIndex:" + std::to_string(endNodeIndex_));
1096     DumpLog::GetInstance().AddDesc("swiperAction.childNodeIndex:" + std::to_string(childNodeIndex_));
1097     DumpLog::GetInstance().AddDesc("curOffset:" + std::to_string(curOffset_));
1098     DumpLog::GetInstance().AddDesc("startNodeSize:" + std::to_string(startNodeSize_));
1099     DumpLog::GetInstance().AddDesc("endNodeSize:" + std::to_string(endNodeSize_));
1100     DumpLog::GetInstance().AddDesc("startDeleteAreaDistance:" + std::to_string(startDeleteAreaDistance_));
1101     DumpLog::GetInstance().AddDesc("endDeleteAreaDistance:" + std::to_string(endDeleteAreaDistance_));
1102     switch (swipeActionState_) {
1103         case SwipeActionState::COLLAPSED:
1104             DumpLog::GetInstance().AddDesc("SwipeActionState::COLLAPSED");
1105             break;
1106         case SwipeActionState::EXPANDED:
1107             DumpLog::GetInstance().AddDesc("SwipeActionState::EXPANDED");
1108             break;
1109         case SwipeActionState::ACTIONING:
1110             DumpLog::GetInstance().AddDesc("SwipeActionState::ACTIONING");
1111             break;
1112     }
1113     hasStartDeleteArea_ ? DumpLog::GetInstance().AddDesc("hasStartDeleteArea:true")
1114                      : DumpLog::GetInstance().AddDesc("hasStartDeleteArea:false");
1115     hasEndDeleteArea_ ? DumpLog::GetInstance().AddDesc("hasEndDeleteArea:true")
1116                      : DumpLog::GetInstance().AddDesc("hasEndDeleteArea:false");
1117     inStartDeleteArea_ ? DumpLog::GetInstance().AddDesc("inStartDeleteArea:true")
1118                      : DumpLog::GetInstance().AddDesc("inStartDeleteArea:false");
1119     inEndDeleteArea_ ? DumpLog::GetInstance().AddDesc("inEndDeleteArea:true")
1120                      : DumpLog::GetInstance().AddDesc("inEndDeleteArea:false");
1121     selectable_ ? DumpLog::GetInstance().AddDesc("selectable:true")
1122                 : DumpLog::GetInstance().AddDesc("selectable:false");
1123     isSelected_ ? DumpLog::GetInstance().AddDesc("isSelected:true")
1124                 : DumpLog::GetInstance().AddDesc("isSelected:false");
1125     isHover_ ? DumpLog::GetInstance().AddDesc("isHover:true")
1126                 : DumpLog::GetInstance().AddDesc("isHover:false");
1127     isPressed_ ? DumpLog::GetInstance().AddDesc("isPressed:true")
1128                 : DumpLog::GetInstance().AddDesc("isPressed:false");
1129     isLayouted_ ? DumpLog::GetInstance().AddDesc("isLayouted:true")
1130                 : DumpLog::GetInstance().AddDesc("isLayouted:false");
1131     if (enableOpacity_.has_value()) {
1132         enableOpacity_.value() ? DumpLog::GetInstance().AddDesc("enableOpacity:true")
1133                             : DumpLog::GetInstance().AddDesc("enableOpacity:false");
1134     } else {
1135         DumpLog::GetInstance().AddDesc("enableOpacity:null");
1136     }
1137 }
1138 
GetEstimateHeight(float estimateHeight,Axis axis) const1139 float ListItemPattern::GetEstimateHeight(float estimateHeight, Axis axis) const
1140 {
1141     auto layoutProperty = GetLayoutProperty<ListItemLayoutProperty>();
1142     CHECK_NULL_RETURN(layoutProperty, 0.0f);
1143     auto visible = layoutProperty->GetVisibility().value_or(VisibleType::VISIBLE);
1144     if (visible == VisibleType::GONE) {
1145         return 0.0f;
1146     }
1147     if (!isLayouted_) {
1148         return estimateHeight;
1149     }
1150     auto host = GetHost();
1151     CHECK_NULL_RETURN(host, estimateHeight);
1152     auto geometryNode = host->GetGeometryNode();
1153     CHECK_NULL_RETURN(geometryNode, estimateHeight);
1154     return GetMainAxisSize(geometryNode->GetMarginFrameSize(), axis);
1155 }
1156 
ClickJudgeVertical(const SizeF & size,double xOffset,double yOffset)1157 bool ListItemPattern::ClickJudgeVertical(const SizeF& size, double xOffset, double yOffset)
1158 {
1159     if (yOffset < 0 || yOffset > size.Height()) {
1160         return true;
1161     }
1162     if (!IsRTLAndVertical()) {
1163         if (startNodeSize_ && xOffset > 0 && xOffset < startNodeSize_) {
1164             return false;
1165         } else if (endNodeSize_ && xOffset > size.Width() - endNodeSize_ && xOffset < size.Width()) {
1166             return false;
1167         }
1168     } else {
1169         if (endNodeSize_ && xOffset > 0 && xOffset < endNodeSize_) {
1170             return false;
1171         } else if (startNodeSize_ && xOffset > size.Width() - startNodeSize_ && xOffset < size.Width()) {
1172             return false;
1173         }
1174     }
1175     return true;
1176 }
1177 
ClickJudge(const PointF & localPoint)1178 bool ListItemPattern::ClickJudge(const PointF& localPoint)
1179 {
1180     auto host = GetHost();
1181     CHECK_NULL_RETURN(host, false);
1182     auto geometryNode = host->GetGeometryNode();
1183     CHECK_NULL_RETURN(geometryNode, false);
1184     auto offset = geometryNode->GetFrameOffset();
1185     if (indexInListItemGroup_ != -1) {
1186         auto parentFrameNode = GetParentFrameNode();
1187         CHECK_NULL_RETURN(parentFrameNode, false);
1188         auto parentGeometryNode = parentFrameNode->GetGeometryNode();
1189         CHECK_NULL_RETURN(parentGeometryNode, false);
1190         auto parentOffset = parentGeometryNode->GetFrameOffset();
1191         offset = offset + parentOffset;
1192     }
1193     auto size = geometryNode->GetFrameSize();
1194     auto xOffset = localPoint.GetX() - offset.GetX();
1195     auto yOffset = localPoint.GetY() - offset.GetY();
1196     if (GetAxis() == Axis::VERTICAL) {
1197         return ClickJudgeVertical(size, xOffset, yOffset);
1198     } else {
1199         if (xOffset > 0 && xOffset < size.Width()) {
1200             if (startNodeSize_ && yOffset > 0 && yOffset < startNodeSize_) {
1201                 return false;
1202             } else if (endNodeSize_ && yOffset > size.Height() - endNodeSize_ && yOffset < size.Height()) {
1203                 return false;
1204             }
1205         }
1206     }
1207     return true;
1208 }
1209 
OnDetachFromMainTree()1210 void ListItemPattern::OnDetachFromMainTree()
1211 {
1212     auto frameNode = GetListFrameNode();
1213     CHECK_NULL_VOID(frameNode);
1214     auto listPattern = frameNode->GetPattern<ListPattern>();
1215     CHECK_NULL_VOID(listPattern);
1216     listPattern->SetSwiperItemEnd(AceType::WeakClaim(this));
1217     swipeActionState_ = SwipeActionState::COLLAPSED;
1218 }
1219 
RenderCustomChild(int64_t deadline)1220 bool ListItemPattern::RenderCustomChild(int64_t deadline)
1221 {
1222     if (shallowBuilder_ && !shallowBuilder_->IsExecuteDeepRenderDone()) {
1223         if (GetSysTimestamp() > deadline) {
1224             return false;
1225         }
1226         shallowBuilder_->ExecuteDeepRender();
1227         shallowBuilder_.Reset();
1228     }
1229     return true;
1230 }
1231 } // namespace OHOS::Ace::NG
1232 
1233