• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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/list/render_list_item.h"
17 
18 #include "base/log/dump_log.h"
19 #include "base/utils/system_properties.h"
20 #include "core/common/frontend.h"
21 #include "core/common/vibrator/vibrator_proxy.h"
22 #include "core/components/box/render_box.h"
23 #include "core/components/flex/render_flex.h"
24 #include "core/components/list/render_list.h"
25 #include "core/components/list/render_list_item_group.h"
26 #include "core/components/list/tv_interactive_effect.h"
27 #ifdef WEARABLE_PRODUCT
28 #include "core/components/list/watch_interactive_effect.h"
29 #endif
30 #include "core/components/proxy/render_item_proxy.h"
31 
32 namespace OHOS::Ace {
33 namespace {
34 
35 const double HALF_SIZE = 0.5;
36 const double ITEM_DISTANCE_BASE = 76.0;
37 const double ITEM_DEFAULT_SCALE = 1.0;
38 const double ITEM_ZERO = 0.0;
39 const double ITEM_SCALE_BASE = 1.12;
40 const double ITEM_OPACITY_BASE = 1.0;
41 const double ITEM_RATIO = -0.34; // 0.78 - 1.12  // 0.66 - 1.0
42 const double DISTANCE_EPSILON = 1.0;
43 constexpr int32_t MIN_COMPATITABLE_VERSION = 5;
44 const double WATCH_SIZE = 466.0;
45 
46 #ifdef WEARABLE_PRODUCT
47 const std::string& VIBRATOR_TYPE_WATCH_CROWN_STRENGTH1 = "watchhaptic.crown.strength1";
48 #endif
49 
50 } // namespace
51 
RenderListItem()52 RenderListItem::RenderListItem()
53 {
54     Initialize();
55 }
56 
Initialize()57 void RenderListItem::Initialize()
58 {
59     clickRecognizer_ = AceType::MakeRefPtr<ClickRecognizer>();
60     clickRecognizer_->SetOnClick([weakItem = AceType::WeakClaim(this)](const ClickInfo&) {
61         auto item = weakItem.Upgrade();
62         if (item) {
63             item->HandleClicked();
64         }
65     });
66     touchRecognizer_ = AceType::MakeRefPtr<RawRecognizer>();
67     touchRecognizer_->SetOnTouchDown([weakItem = AceType::WeakClaim(this)](const TouchEventInfo&) {
68         auto item = weakItem.Upgrade();
69         if (item && item->GetSupportClick()) {
70             item->PlayPressDownAnimation();
71         }
72     });
73     touchRecognizer_->SetOnTouchUp([weakItem = AceType::WeakClaim(this)](const TouchEventInfo&) {
74         auto item = weakItem.Upgrade();
75         if (item && item->GetSupportClick()) {
76             item->PlayPressUpAnimation();
77         }
78     });
79     touchRecognizer_->SetOnTouchCancel([weakItem = AceType::WeakClaim(this)](const TouchEventInfo&) {
80         auto item = weakItem.Upgrade();
81         if (item && item->GetSupportClick()) {
82             item->PlayPressUpAnimation();
83         }
84     });
85 }
86 
PerformLayout()87 void RenderListItem::PerformLayout()
88 {
89     if (!GetChildren().empty()) {
90         auto child = GetChildren().front();
91         auto context = context_.Upgrade();
92         if (NeedDivider() && context && context->GetIsDeclarative()) {
93             auto layoutParam = GetLayoutParam();
94             auto maxSize = layoutParam.GetMaxSize();
95             auto dividerSize = IsListVertical() ? Size(0.0, NormalizeToPx(dividerHeight_))
96                                                 : Size(NormalizeToPx(dividerHeight_), 0.0);
97             maxSize = maxSize - dividerSize;
98             layoutParam.SetMaxSize(maxSize);
99             child->Layout(layoutParam);
100         } else {
101             child->Layout(GetLayoutParam());
102         }
103         child->SetPosition(Offset::Zero());
104         if (NeedDivider()) {
105             auto dividerSize = IsListVertical() ? Size(0.0, NormalizeToPx(dividerHeight_))
106                 : Size(NormalizeToPx(dividerHeight_), 0.0);
107             SetLayoutSize(dividerSize + child->GetLayoutSize());
108         } else {
109             SetLayoutSize(child->GetLayoutSize());
110         }
111         // update focus animation size
112         focusAnimationRRect_.SetRect(Rect(Offset(0, 0), GetPaintSize() * TV_ITEM_SCALE));
113 
114         if (SystemProperties::GetDeviceType() == DeviceType::WATCH) {
115             CalculateScaleFactorOnWatch();
116         }
117     }
118 }
119 
CalculateScaleFactorOnWatch()120 void RenderListItem::CalculateScaleFactorOnWatch()
121 {
122     if (isTitle_) {
123         return;
124     }
125 
126     auto context = GetContext().Upgrade();
127     const static int32_t PLATFORM_VERSION_FIVE = 5;
128     if (context && context->GetMinPlatformVersion() <= PLATFORM_VERSION_FIVE) {
129         Offset itemCenter = GetGlobalOffset() + GetLayoutSize() * HALF_SIZE;
130         double scale = ITEM_DEFAULT_SCALE;
131         double viewScale = ITEM_DEFAULT_SCALE;
132         auto pipelineContext = context_.Upgrade();
133         if (pipelineContext) {
134             scale = pipelineContext->GetDipScale();
135             viewScale = pipelineContext->GetViewScale();
136         }
137         if (NearZero(scale) || NearZero(viewScale)) {
138             LOGE("pipeline parameter is invalid");
139             return;
140         }
141         auto distance = std::abs(itemCenter.GetY() - WATCH_SIZE / viewScale * HALF_SIZE);
142         auto ratio = std::pow(distance, SQUARE) * ITEM_RATIO / std::pow(ITEM_DISTANCE_BASE * scale, SQUARE);
143         scaleFactor_ = std::max(ITEM_ZERO, ratio + ITEM_SCALE_BASE);
144         opacityFactor_ = std::max(ITEM_ZERO, ratio + ITEM_OPACITY_BASE);
145     } else {
146         auto renderList = GetRenderList();
147         if (!renderList) {
148             LOGE("Can not find parent render list");
149             return;
150         }
151 
152         double itemSize = renderList->IsVertical() ? GetLayoutSize().Height() : GetLayoutSize().Width();
153         double itemCenter = GetPositionInList() + renderList->GetListPosition() + itemSize * HALF_SIZE;
154         double scale = ITEM_DEFAULT_SCALE;
155         auto pipelineContext = context_.Upgrade();
156         if (pipelineContext) {
157             scale = pipelineContext->GetDipScale();
158         }
159         if (NearZero(scale)) {
160             LOGE("Pipeline parameter is invalid");
161             return;
162         }
163         double viewPort = renderList->IsVertical() ? viewPort_.Height() : viewPort_.Width();
164         auto distance = std::abs(itemCenter - viewPort * HALF_SIZE);
165         if (NearZero(distance, DISTANCE_EPSILON)) {
166             distance = 0.0;
167         }
168         auto ratio = std::pow(distance, SQUARE) * ITEM_RATIO / std::pow(ITEM_DISTANCE_BASE * scale, SQUARE);
169         scaleFactor_ = std::max(ITEM_ZERO, ratio + ITEM_SCALE_BASE);
170         opacityFactor_ = std::max(ITEM_ZERO, ratio + ITEM_OPACITY_BASE);
171     }
172 }
173 
Update(const RefPtr<Component> & component)174 void RenderListItem::Update(const RefPtr<Component>& component)
175 {
176     itemComponent_ = component;
177     auto item = AceType::DynamicCast<ListItemComponent>(component);
178     if (item) {
179         type_ = item->GetType();
180         index_ = item->GetIndex();
181         columnSpan_ = item->GetColumnSpan();
182         op_ = item->GetOperation();
183         flags_ = item->GetFlags();
184         // update focus animation color
185         focusAnimationColor_ = item->GetFocusAnimationColor();
186         // update focus animation corner radius
187         focusAnimationRRect_.SetCorner({ item->GetTopLeftRadius(), item->GetTopRightRadius(),
188             item->GetBottomRightRadius(), item->GetBottomLeftRadius() });
189         LOGD("[Focus][Dep:%{public}d] Update: focusAnimationRRect top left radius: %{public}.1lf", GetDepth(),
190             focusAnimationRRect_.GetCorner().topLeftRadius.GetX().Value());
191 
192         needVibrate_ = item->NeedVibrate();
193         auto context = context_.Upgrade();
194         if (needVibrate_ && !vibrator_ && context) {
195             vibrator_ = VibratorProxy::GetInstance().GetVibrator(context->GetTaskExecutor());
196         }
197 
198         rotationVibrate_ = item->IsRotationVibrate();
199         if (rotationVibrate_ && !vibrator_ && context) {
200             vibrator_ = VibratorProxy::GetInstance().GetVibrator(context->GetTaskExecutor());
201         }
202 
203         supportScale_ = item->GetSupportScale();
204         supportOpacity_ = item->GetSupportOpacity();
205         supportClick_ = item->GetSupportClick();
206         isTitle_ = item->IsTitle();
207         sticky_ = item->GetSticky();
208         stickyMode_ = item->GetStickyMode();
209         clickColor_ = item->GetClickColor();
210         selfAlign_ = item->GetAlignSelf();
211         Dimension radius = item->GetStickyRadius();
212         if (radius.IsValid()) {
213             stickyRadius_ = context ? context->NormalizeToPx(radius) : stickyRadius_;
214         }
215 
216         if (!primary_ && item->GetPrimary()) {
217             NotifyGroupPrimaryChange();
218         }
219         SetPrimary(item->GetPrimary());
220 
221         // list divider
222         needDivider_ = item->NeedDivider();
223         isActive_ = item->IsActive();
224         dividerLength_ = item->GetDividerLength();
225         dividerHeight_ = item->GetDividerHeight();
226         dividerOrigin_ = item->GetDividerOrigin();
227         dividerColor_ = item->GetDividerColor();
228         auto onClickId = item->GetClickEventId();
229         clickEvent_ = AceAsyncEvent<void()>::Create(onClickId, GetContext());
230         clickRecognizer_->SetUseCatchMode(true);
231         if (!onClickId.GetCatchMode()) {
232             static const int32_t bubbleModeVersion = 6;
233             auto pipeline = context_.Upgrade();
234             if (pipeline && pipeline->GetMinPlatformVersion() >= bubbleModeVersion) {
235                 clickRecognizer_->SetUseCatchMode(false);
236                 return;
237             }
238         }
239         stickyEvent_ = AceAsyncEvent<void(const std::string&)>::Create(item->GetStickyEventId(), GetContext());
240         transitionEffect_ = item->GetTransitionEffect();
241         UpdateAccessibilityAttr();
242         MarkNeedLayout();
243     }
244 }
245 
NotifyGroupPrimaryChange()246 void RenderListItem::NotifyGroupPrimaryChange()
247 {
248     auto parent = GetParent().Upgrade();
249     while (parent) {
250         auto group = AceType::DynamicCast<RenderListItemGroup>(parent);
251         if (group) {
252             group->ItemPrimaryChange(GetIndex());
253             break;
254         }
255         parent = parent->GetParent().Upgrade();
256     }
257 }
258 
GetPositionInList() const259 double RenderListItem::GetPositionInList() const
260 {
261     RefPtr<RenderNode> parentNode = GetParent().Upgrade();
262     while (parentNode) {
263         RefPtr<RenderList> listNode = AceType::DynamicCast<RenderList>(parentNode);
264         if (listNode) {
265             return listNode->GetItemPosition(index_);
266         }
267         parentNode = parentNode->GetParent().Upgrade();
268     }
269     return 0.0;
270 }
271 
IsItemCenter(bool isVertical,Size viewport)272 bool RenderListItem::IsItemCenter(bool isVertical, Size viewport)
273 {
274     auto parent = GetParent().Upgrade();
275     if (!parent) {
276         return false;
277     }
278     Size itemSize = GetLayoutSize();
279     Offset itemPosition = parent->GetPosition();
280     double size = isVertical ? itemSize.Height() : itemSize.Width();
281     double position = isVertical ? itemPosition.GetY() : itemPosition.GetX();
282     double center = (isVertical ? viewport.Height() : viewport.Width()) / 2.0;
283     return center > position && center < (position + size);
284 }
285 
ResetFocusEffect()286 void RenderListItem::ResetFocusEffect()
287 {
288 #ifdef WEARABLE_PRODUCT
289     if (SystemProperties::GetDeviceType() == DeviceType::WATCH) {
290         focusController_ = AceType::MakeRefPtr<WatchInteractiveEffect>(GetContext());
291 #else
292     if (SystemProperties::GetDeviceType() == DeviceType::TV) {
293         focusController_ = AceType::MakeRefPtr<TVInteractiveEffect>(GetContext());
294 #endif
295     } else {
296         focusController_ = AceType::MakeRefPtr<InteractiveEffect>(GetContext());
297     }
298     focusController_->Initialize(GetThemeManager());
299     focusController_->SetItemNode(AceType::WeakClaim(this));
300     auto listNode = GetRenderList();
301     if (listNode) {
302         auto weakList = WeakPtr<RenderList>(listNode);
303         focusController_->SetListNode(weakList);
304     }
305 }
306 
307 void RenderListItem::HandleItemEffect(bool isFromRotate)
308 {
309     if (!focusController_) {
310         ResetFocusEffect();
311     }
312 
313     if (currentState_ != lastState_) {
314 #ifdef WEARABLE_PRODUCT
315         if (needVibrate_ && lastState_ == ItemState::NEARBY && currentState_ == ItemState::FOCUS && vibrator_) {
316             vibrator_->Vibrate(VIBRATOR_TYPE_WATCH_CROWN_STRENGTH1);
317         }
318         if (rotationVibrate_ && isFromRotate) {
319             vibrator_->Vibrate(VIBRATOR_TYPE_WATCH_CROWN_STRENGTH1);
320         }
321 #endif
322         LOGD("item (%{public}d) change from %{public}d to %{public}d.", index_, lastState_, currentState_);
323         lastState_ = currentState_;
324         if (currentState_ != ItemState::NONE && currentState_ != ItemState::CLICK) {
325             focusController_->ShowAnimation(currentState_);
326         } else {
327             // invalid focus
328             LOGD("focus state invalid.");
329         }
330     }
331     MarkNeedRender();
332 }
333 
334 void RenderListItem::HandleClicked()
335 {
336     if (clickEvent_) {
337         clickEvent_();
338     }
339 
340     if (primary_ && clicked_) {
341         clicked_();
342     }
343 }
344 
345 void RenderListItem::PlayPressDownAnimation()
346 {
347     if (!focusController_) {
348         ResetFocusEffect();
349     }
350     pressAnimation_ = true;
351     focusController_->TouchDownAnimation();
352 }
353 
354 void RenderListItem::PlayPressUpAnimation()
355 {
356     if (!focusController_) {
357         ResetFocusEffect();
358     }
359     pressAnimation_ = false;
360     focusController_->TouchUpAnimation();
361 }
362 
363 void RenderListItem::OnCancelPressAnimation()
364 {
365     if (!pressAnimation_) {
366         return;
367     }
368     if (!focusController_) {
369         ResetFocusEffect();
370     }
371     focusController_->CancelTouchAnimation();
372 }
373 
374 void RenderListItem::HandleStickyEvent(bool sticky)
375 {
376     std::string state = sticky ? "true" : "false";
377     std::string param = std::string(R"("sticky",{"state":)").append(state).append("},null");
378     if (stickyEvent_) {
379         stickyEvent_(param);
380     }
381 }
382 
383 void RenderListItem::OnTouchTestHit(
384     const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
385 {
386     if (!GetVisible() && !GetClonedBySticky()) {
387         return;
388     }
389     // supportClick means show click effect
390     bool supportClick = supportClick_;
391     auto pipeline = context_.Upgrade();
392     if (pipeline && pipeline->GetMinPlatformVersion() > MIN_COMPATITABLE_VERSION) {
393         supportClick = true;
394     }
395     if ((!touchRecognizer_) || (!clickRecognizer_) || (!supportClick)) {
396         return;
397     }
398     touchRecognizer_->SetCoordinateOffset(coordinateOffset);
399     clickRecognizer_->SetCoordinateOffset(coordinateOffset);
400     result.emplace_back(touchRecognizer_);
401     result.emplace_back(clickRecognizer_);
402 }
403 
404 void RenderListItem::UpdateItemFocusRect(double scale)
405 {
406     focusAnimationRRect_.SetRect(Rect(Offset(0.0, 0.0), GetPaintSize() * scale));
407 }
408 
409 void RenderListItem::HandleFocusEvent(bool focus, bool isInGroup)
410 {
411     if (SystemProperties::GetDeviceType() == DeviceType::WATCH) {
412         return;
413     }
414     focused_ = focus;
415 
416     ShowFocusAnimation(focus, Rect(0.0, 0.0, 0.0, 0.0));
417 
418     if (focus) {
419         ChangeStatus(RenderStatus::FOCUS);
420         currentState_ = ItemState::FOCUS;
421         if (isInGroup) {
422             HandleItemEffect();
423             return;
424         }
425         RefPtr<RenderNode> parentNode = GetParent().Upgrade();
426         while (parentNode) {
427             RefPtr<RenderList> listNode = AceType::DynamicCast<RenderList>(parentNode);
428             if (listNode) {
429                 listNode->ListItemFocused(index_);
430                 break;
431             }
432             parentNode = parentNode->GetParent().Upgrade();
433         }
434     } else {
435         ChangeStatus(RenderStatus::BLUR);
436         currentState_ = ItemState::BLUR;
437     }
438     HandleItemEffect();
439 }
440 
441 Offset RenderListItem::GetPaintOffset() const
442 {
443     auto globalOffset = GetGlobalOffset();
444     auto layoutSize = GetLayoutSize(); // size include margin
445     auto paintSize = GetPaintSize();   // size exclude margin
446     auto margin = GetMarginInPx();
447     if (layoutSize > paintSize) {
448         globalOffset.SetX(globalOffset.GetX() + margin.LeftPx());
449         globalOffset.SetY(globalOffset.GetY() + margin.TopPx());
450     }
451     return globalOffset;
452 }
453 
454 EdgePx RenderListItem::GetMarginInPx() const
455 {
456     EdgePx marginInPx;
457     auto children = GetChildren();
458     if (children.empty()) {
459         return marginInPx;
460     }
461 
462     auto child = children.front();
463     while (child) {
464         auto box = AceType::DynamicCast<RenderBoxBase>(child);
465         if (box) {
466             auto boxChildren = box->GetChildren();
467             if (boxChildren.empty()) {
468                 break;
469             }
470             auto boxChild = boxChildren.front();
471             if (boxChild && AceType::DynamicCast<RenderFlex>(boxChild)) {
472                 marginInPx = box->GetMargin();
473                 break;
474             }
475         }
476         children = child->GetChildren();
477         if (children.empty()) {
478             break;
479         }
480         child = children.front();
481     }
482     return marginInPx;
483 }
484 
485 Size RenderListItem::GetPaintSize() const
486 {
487     Size size = GetLayoutSize();
488     auto margin = GetMarginInPx();
489     return size - margin.GetLayoutSize();
490 }
491 
492 Border RenderListItem::GetFocusBorder() const
493 {
494     Border border;
495     border.SetTopRightRadius(focusAnimationRRect_.GetCorner().topRightRadius);
496     border.SetTopLeftRadius(focusAnimationRRect_.GetCorner().topLeftRadius);
497     border.SetBottomLeftRadius(focusAnimationRRect_.GetCorner().bottomLeftRadius);
498     border.SetBottomRightRadius(focusAnimationRRect_.GetCorner().bottomRightRadius);
499     return border;
500 }
501 
502 void RenderListItem::ShowFocusAnimation(bool focus, const Rect& listRect, double scale)
503 {
504     // paint focus animation
505     auto context = context_.Upgrade();
506     if (!context) {
507         LOGE("[Focus]Pipeline context is nullptr");
508         return;
509     }
510 
511     LOGD("Index:%{public}d focus:%{public}d %{public}s", index_, focus, GetPaintOffset().ToString().c_str());
512     if (focus) {
513         Size size = GetPaintSize();
514         auto globalOffset = GetPaintOffset() + (size * (DEFAULT_SCALE - scale) * HALF_SIZE);
515         if (listRect.IsValid()) {
516             context->ShowFocusAnimation(focusAnimationRRect_, focusAnimationColor_, globalOffset, listRect);
517             context->ShowShadow(focusAnimationRRect_, globalOffset);
518         } else {
519             context->ShowFocusAnimation(focusAnimationRRect_, focusAnimationColor_, globalOffset);
520             context->ShowShadow(focusAnimationRRect_, globalOffset);
521         }
522     } else {
523         context->CancelFocusAnimation();
524         context->CancelShadow();
525     }
526 }
527 
528 void RenderListItem::UpdateAccessibilityAttr()
529 {
530     auto refPtr = accessibilityNode_.Upgrade();
531     if (!refPtr) {
532         LOGD("Get accessibility node failed.");
533         return;
534     }
535 
536     refPtr->SetClickableState(true);
537     refPtr->SetActionClickImpl([weakItem = AceType::WeakClaim(this)]() {
538         auto item = weakItem.Upgrade();
539         if (item) {
540             LOGI("Trigger ActionClick by Accessibility(%{public}d).", item->index_);
541             item->HandleClicked();
542             item->OnGroupClicked();
543         }
544     });
545 
546     refPtr->SetFocusableState(true);
547     refPtr->SetActionFocusImpl([weakItem = AceType::WeakClaim(this)]() {
548         auto item = weakItem.Upgrade();
549         if (item) {
550             LOGI("Trigger ActionFocus by Accessibility(%{public}d).", item->index_);
551             item->MoveToViewPort();
552         }
553     });
554     refPtr->AddSupportAction(AceAction::ACTION_ACCESSIBILITY_FOCUS);
555 }
556 
557 void RenderListItem::OnGroupClicked()
558 {
559     if (!primary_ || !curPrimary_) {
560         return; // Only trigger group click when current item is primary.
561     }
562     RefPtr<RenderNode> parent = GetParent().Upgrade();
563     while (parent) {
564         RefPtr<RenderListItemGroup> group = AceType::DynamicCast<RenderListItemGroup>(parent);
565         if (group) {
566             return group->HandleClicked();
567         }
568         parent = parent->GetParent().Upgrade();
569     }
570 }
571 
572 void RenderListItem::MoveToViewPort()
573 {
574     RefPtr<RenderNode> parentNode = GetParent().Upgrade();
575     while (parentNode) {
576         RefPtr<RenderList> listNode = AceType::DynamicCast<RenderList>(parentNode);
577         if (listNode) {
578             return listNode->MoveItemToViewPort(GetPositionInList());
579         }
580         parentNode = parentNode->GetParent().Upgrade();
581     }
582 }
583 
584 RefPtr<RenderList> RenderListItem::GetRenderList() const
585 {
586     auto parent = GetParent().Upgrade();
587     while (parent) {
588         auto parentNode = AceType::DynamicCast<RenderList>(parent);
589         if (parentNode) {
590             return parentNode;
591         }
592         parent = parent->GetParent().Upgrade();
593     }
594     return nullptr;
595 }
596 
597 bool RenderListItem::NeedDivider()
598 {
599     return needDivider_ && !IsLastItem();
600 }
601 
602 bool RenderListItem::IsLastItem()
603 {
604     auto renderList = GetRenderList();
605     if (renderList) {
606         int32_t maxCount = renderList->GetMaxCount();
607         if (GetIndex() == maxCount - 1) {
608             return true;
609         }
610     }
611     return false;
612 }
613 
614 bool RenderListItem::IsListVertical()
615 {
616     auto renderList = GetRenderList();
617     if (renderList) {
618         FlexDirection listDirection = renderList->GetDirection();
619         return listDirection == FlexDirection::COLUMN || listDirection == FlexDirection::COLUMN_REVERSE;
620     }
621     return true;
622 }
623 
624 void RenderListItem::Dump()
625 {
626     DumpLog::GetInstance().AddDesc(std::string("index: ").append(std::to_string(GetIndex())));
627 }
628 
629 void RenderListItem::RunCardTransitionAnimation(double shiftHeight)
630 {
631     auto renderList = GetRenderList();
632     if (!renderList) {
633         LOGE("list render is null");
634         return;
635     }
636     makeCardTransition_ = true;
637     renderList->SetMakeCardTransition(makeCardTransition_);
638     if (transitionEffect_ == TransitionEffect::UNFOLD) {
639         renderList->SetShiftHeight(shiftHeight);
640     } else {
641         renderList->SetShiftHeight(0.0);
642     }
643     renderList->MarkNeedRender();
644 }
645 
646 void RenderListItem::StopCardTransitionAnimation()
647 {
648     makeCardTransition_ = false;
649     auto renderList = GetRenderList();
650     if (renderList) {
651         renderList->SetMakeCardTransition(makeCardTransition_);
652         renderList->SetShiftHeight(0.0);
653         renderList->MarkNeedRender();
654     }
655 }
656 
657 RRect RenderListItem::GetRRect() const
658 {
659     auto child = GetFirstChild();
660     while (child) {
661         auto childNode = AceType::DynamicCast<RenderBox>(child);
662         if (childNode) {
663             auto margin = childNode->GetMargin();
664             auto rrect = RRect(Rect(childNode->GetGlobalOffset() + margin.GetOffset(),
665                 childNode->GetLayoutSize() - margin.GetLayoutSize()));
666             if (childNode->GetBackDecoration()) {
667                 auto border = childNode->GetBackDecoration()->GetBorder();
668                 rrect.SetCorner({ border.TopLeftRadius(), border.TopRightRadius(),
669                     border.BottomLeftRadius(), border.BottomRightRadius() });
670             }
671             return rrect;
672         }
673         child = child->GetFirstChild();
674     }
675     return RRect();
676 }
677 
678 } // namespace OHOS::Ace
679