1 /*
2  * Copyright (c) 2021-2022 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_group.h"
17 
18 #include "core/components/flex/render_flex.h"
19 #include "core/components/list/list_item_group_component.h"
20 
21 namespace OHOS::Ace {
22 namespace {
23 
24 constexpr Dimension BOX_SIZE_WIDTH = 48.0_vp;
25 constexpr Dimension MARGIN_RIGHT_WIDTH = 2.0_vp;
26 constexpr int32_t ANIMATION_DURATION = 250;
27 constexpr float POSITION_ANIMATION_EXPAND_TIME = 0.8f;
28 constexpr float POSITION_ANIMATION_COLLAPSE_TIME = 0.333f;
29 constexpr float OPACITY_ANIMATION_EXPAND_TIME = 0.4f;
30 constexpr float OPACITY_ANIMATION_COLLAPSE_TIME = 0.5f;
31 constexpr float ROTATE_ANGLE = 180.0f;
32 constexpr float ROTATE_ROW_ANGLE = 90.0f;
33 constexpr float ROTATE_ROW_REVERSE_ANGLE = 270.0f;
34 constexpr float ROTATE_ANIMATION_EXPAND_TIME = 0.8f;
35 constexpr float ROTATE_ANIMATION_COLLAPSE_TIME = 0.667f;
36 const char LIST_ITEM_GROUP_EVENT_GROUPCLICK[] = "groupclick";
37 const char LIST_ITEM_GROUP_EVENT_GROUPCOLLAPSE[] = "groupcollapse";
38 const char LIST_ITEM_GROUP_EVENT_GROUPEXPAND[] = "groupexpand";
39 
40 } // namespace
41 
MakeEventParam(const std::string & command)42 std::string RenderListItemGroup::MakeEventParam(const std::string& command)
43 {
44     return std::string("\"").append(command).append("\", ").append(R"({"groupid": ")").append(groupId_).append("\"}");
45 }
46 
HandleClicked()47 void RenderListItemGroup::HandleClicked()
48 {
49     ResetChildVisibleState();
50     expand_ = !expand_;
51     UpdateGroupComponentStatus(expand_);
52     UpdateExpandStatusInList();
53     SetAnimationStop();
54 
55     if (onClicked_) {
56         onClicked_(MakeEventParam(LIST_ITEM_GROUP_EVENT_GROUPCLICK));
57     }
58     FireExpandEvent();
59     MarkNeedLayout();
60 }
61 
FireExpandEvent()62 void RenderListItemGroup::FireExpandEvent()
63 {
64     if (expand_ && onExpand_) {
65         onExpand_(MakeEventParam(LIST_ITEM_GROUP_EVENT_GROUPEXPAND));
66     }
67 
68     if (!expand_ && onCollapse_) {
69         onCollapse_(MakeEventParam(LIST_ITEM_GROUP_EVENT_GROUPCOLLAPSE));
70     }
71 }
72 
GetRotateAngle(bool expand)73 double RenderListItemGroup::GetRotateAngle(bool expand)
74 {
75     double rotateAngle = 0.0;
76     if (direction_ == FlexDirection::COLUMN) {
77         rotateAngle = expand_ ? ROTATE_ANGLE : 0.0;
78     } else if (direction_ == FlexDirection::COLUMN_REVERSE) {
79         rotateAngle = expand_ ? 0.0 : ROTATE_ANGLE;
80     } else if (direction_ == FlexDirection::ROW) {
81         rotateAngle = expand_ ? ROTATE_ROW_REVERSE_ANGLE : ROTATE_ROW_ANGLE;
82     } else {
83         rotateAngle = expand_ ? ROTATE_ROW_ANGLE : ROTATE_ROW_REVERSE_ANGLE;
84     }
85     return rotateAngle;
86 }
87 
MakeInnerLayoutParam() const88 LayoutParam RenderListItemGroup::MakeInnerLayoutParam() const
89 {
90     LayoutParam innerLayout;
91     Size maxSize;
92 
93     if (direction_ == FlexDirection::ROW || direction_ == FlexDirection::ROW_REVERSE) {
94         maxSize = Size(Size::INFINITE_SIZE, GetLayoutParam().GetMaxSize().Height());
95     } else {
96         maxSize = Size(GetLayoutParam().GetMaxSize().Width(), Size::INFINITE_SIZE);
97     }
98     innerLayout.SetMaxSize(maxSize);
99     return innerLayout;
100 }
101 
AddArrowImage(double mainSize)102 void RenderListItemGroup::AddArrowImage(double mainSize)
103 {
104     if (arrowImage_) {
105         if (!GetChildren().empty() && GetChildren().back() != arrowImage_) {
106             RemoveChild(arrowImage_);
107             AddChild(arrowImage_, GetChildren().size());
108         }
109         LayoutParam innerLayout;
110         double width = NormalizeToPx(imageSize_);
111         innerLayout.SetFixedSize(Size(width, width));
112         arrowImage_->Layout(innerLayout);
113 
114         Size primarySize = primary_->GetLayoutSize();
115         double offsetX = 0.0;
116         if (rightToLeft_) {
117             if (direction_ == FlexDirection::COLUMN || direction_ == FlexDirection::COLUMN_REVERSE) {
118                 offsetX = NormalizeToPx(MARGIN_RIGHT_WIDTH);
119             } else {
120                 offsetX = std::max(
121                     mainSize - NormalizeToPx(BOX_SIZE_WIDTH + MARGIN_RIGHT_WIDTH + imageSize_) * FACTOR_HALF, 0.0);
122             }
123         } else {
124             offsetX = std::max(
125                 primarySize.Width() - NormalizeToPx(BOX_SIZE_WIDTH + MARGIN_RIGHT_WIDTH + imageSize_) * FACTOR_HALF,
126                 0.0);
127         }
128         double offsetY = 0.0;
129         auto renderBox = GetRenderBox(primary_);
130         if (renderBox) {
131             double marginTop = NormalizeToPx(renderBox->GetMargin().Top());
132             double marginRight = NormalizeToPx(renderBox->GetMargin().Right());
133             double boxPaintHeight = renderBox->GetPaintSize().Height();
134             offsetY = marginTop + std::max((boxPaintHeight - width) * FACTOR_HALF, 0.0);
135             offsetX = std::max(offsetX - marginRight, 0.0);
136         } else {
137             offsetY = std::max((primarySize.Height() - width) * FACTOR_HALF, 0.0);
138         }
139         arrowImage_->SetPosition(Offset(offsetX, offsetY));
140         double rotateAngle = GetRotateAngle(expand_);
141         arrowImage_->SetRotate(rotateAngle);
142         // arrow need rendered after setRotate
143         arrowImage_->MarkNeedRender();
144     }
145 }
146 
NeedRebuild() const147 bool RenderListItemGroup::NeedRebuild() const
148 {
149     auto children = GetChildren();
150     if (children.empty()) {
151         LOGE("list item group has no list item child");
152         return false;
153     }
154     bool needRebuild = false;
155     for (const auto& iter : children) {
156         auto listItemNode = RenderListItem::GetRenderListItem(iter);
157         if (listItemNode && listItemNode->GetPrimaryChange()) {
158             needRebuild = true;
159             listItemNode->SetPrimaryChange(false);
160         }
161     }
162     return needRebuild;
163 }
164 
GetPrimaryItem()165 void RenderListItemGroup::GetPrimaryItem()
166 {
167     auto children = GetChildren();
168     if (children.empty()) {
169         LOGE("list item group has no list item child");
170         primary_ = nullptr;
171         return;
172     }
173     primary_ = nullptr;
174     // Find primary item.
175     for (const auto& iter : children) {
176         auto listItemNode = RenderListItem::GetRenderListItem(iter);
177         if (listItemNode) {
178             if (listItemNode->GetPrimary()) {
179                 primary_ = iter;
180                 break;
181             }
182             if (!primary_) {
183                 primary_ = iter;
184             }
185         }
186     }
187     auto primaryListItem = RenderListItem::GetRenderListItem(primary_);
188     if (!primaryListItem) {
189         LOGE("list item group has no primary child");
190         primary_ = nullptr;
191         return;
192     }
193     if (!primaryListItem->GetPrimary()) {
194         isDefaultPrimary_ = true;
195     }
196     primaryListItem->SetPrimary(true);
197     primaryListItem->SetCurPrimary(true);
198     primaryListItem->SetIndex(0);
199     primaryListItem->RegisterClickedCallback([weakItemGroup = AceType::WeakClaim(this)]() {
200         auto listItemGroup = weakItemGroup.Upgrade();
201         if (listItemGroup) {
202             listItemGroup->HandleClicked();
203         }
204     });
205 }
206 
LayoutExpandableList(double mainSize)207 void RenderListItemGroup::LayoutExpandableList(double mainSize)
208 {
209     double primarySize = 0.0;
210     if (primary_) {
211         primarySize = GetMainSize(primary_->GetLayoutSize());
212     }
213     if (NearEqual(mainSize, primarySize)) {
214         ResetChildVisibleState();
215         return;
216     }
217     double curSize = 0.0;
218     auto children = GetChildren();
219     auto rIter = children.rbegin();
220     while (rIter != children.rend()) {
221         auto child = *rIter++;
222         if (child == primary_ || child == arrowImage_) {
223             continue;
224         }
225 
226         double childSize = GetMainSize(child->GetLayoutSize());
227         if (childSize > mainSize - curSize - primarySize) {
228             child->SetVisible(false);
229             break;
230         }
231         child->SetVisible(true);
232         if ((rightToLeft_ && direction_ == FlexDirection::ROW) || direction_ == FlexDirection::ROW_REVERSE ||
233             direction_ == FlexDirection::COLUMN_REVERSE) {
234             child->SetPosition(MakeValue<Offset>(curSize, 0.0));
235         } else {
236             child->SetPosition(MakeValue<Offset>(mainSize - curSize - childSize, 0.0));
237         }
238         curSize += childSize;
239         if (curSize + primarySize >= mainSize) {
240             break;
241         }
242     }
243 
244     while (rIter != children.rend()) {
245         auto child = *rIter++;
246         if (child == primary_ || child == arrowImage_) {
247             continue;
248         }
249         child->SetVisible(false);
250     }
251 
252     MarkNeedLayout();
253 }
254 
SetAnimationStop()255 void RenderListItemGroup::SetAnimationStop()
256 {
257     animating_ = false;
258 }
259 
PerformLayout()260 void RenderListItemGroup::PerformLayout()
261 {
262     if (animating_) {
263         return;
264     }
265 
266     GetPrimaryItem();
267     if (!primary_) {
268         ResetLayout();
269         return;
270     }
271 
272     auto forceLayout = false;
273     if (NeedRebuild() && rebuild_) {
274         rebuild_();
275         forceLayout = true;
276         GetPrimaryItem();
277     }
278     double curMainSize = GetMainSize(GetLayoutSize());
279     auto layoutParam = MakeInnerLayoutParam();
280     primary_->Layout(layoutParam);
281 
282     double mainSize = GetMainSize(primary_->GetLayoutSize());
283     double crossSize = std::max(0.0, GetCrossSize(primary_->GetLayoutSize()));
284 
285     // Layout other items.
286     int32_t index = 1;
287     for (const auto& child : GetChildren()) {
288         if (child == primary_ || child == arrowImage_) {
289             continue;
290         }
291         auto listItemNode = RenderListItem::GetRenderListItem(child);
292         if (listItemNode) {
293             listItemNode->SetIndex(index++);
294         }
295         if (expand_ || IsExpanded()) {
296             child->Layout(layoutParam);
297             mainSize += GetMainSize(child->GetLayoutSize());
298             crossSize = std::max(crossSize, GetCrossSize(primary_->GetLayoutSize()));
299         }
300     }
301     AddArrowImage(mainSize);
302 
303     if ((rightToLeft_ && direction_ == FlexDirection::ROW) || direction_ == FlexDirection::ROW_REVERSE ||
304         direction_ == FlexDirection::COLUMN_REVERSE) {
305         primary_->SetPosition(Offset(mainSize - GetMainSize(primary_->GetLayoutSize()), 0.0));
306     } else {
307         primary_->SetPosition(Offset::Zero());
308     }
309 
310     double startPosition = curMainSize;
311     double endPosition = mainSize;
312     if (!NearZero(startPosition) && !forceLayout) {
313         if (NearEqual(startPosition, endPosition)) {
314             ResetChildVisibleState();
315             LOGE("equal return");
316             return;
317         }
318         AnimationPerformLayout(crossSize, startPosition, endPosition);
319         SetLayoutSize(MakeValue<Size>(startPosition, crossSize));
320     } else {
321         LayoutExpandableList(endPosition);
322         SetLayoutSize(MakeValue<Size>(endPosition, crossSize));
323     }
324 }
325 
IsExpanded()326 bool RenderListItemGroup::IsExpanded()
327 {
328     bool flag = expand_;
329     auto listParent = GetParent().Upgrade();
330     RefPtr<RenderList> list = nullptr;
331     while (listParent) {
332         if (AceType::InstanceOf<RenderList>(listParent)) {
333             list = AceType::DynamicCast<RenderList>(listParent);
334             break;
335         }
336         listParent = listParent->GetParent().Upgrade();
337     }
338     if (list) {
339         flag = expand_ || list->GetLayoutManager()->GetExpandStatus(GetIndex());
340     }
341     return flag;
342 }
343 
AnimationPerformLayout(double crossSize,double startPosition,double endPosition)344 void RenderListItemGroup::AnimationPerformLayout(double crossSize, double startPosition, double endPosition)
345 {
346     if (!animator_->IsStopped()) {
347         animator_->Stop();
348     }
349     animator_->ClearInterpolators();
350 
351     RefPtr<KeyframeAnimation<double>> positionAnimation =
352         createPositionAnimation(crossSize, startPosition, endPosition);
353 
354     auto opacityAnimation = CreateOpacityAnimation();
355     auto rotateAnimation = CreateRotateAnimation();
356 
357     animator_->AddInterpolator(positionAnimation);
358     animator_->AddInterpolator(opacityAnimation);
359     animator_->AddInterpolator(rotateAnimation);
360 
361     animator_->SetDuration(ANIMATION_DURATION);
362     animator_->Play();
363     animating_ = true;
364     animator_->AddStopListener([weakItemGroup = AceType::WeakClaim(this)]() {
365         auto itemGroup = weakItemGroup.Upgrade();
366         itemGroup->SetAnimationStop();
367         itemGroup->SetChildStretch(false);
368     });
369 }
370 
createPositionAnimation(double crossSize,double startPosition,double endPosition)371 RefPtr<KeyframeAnimation<double>> RenderListItemGroup::createPositionAnimation(
372     double crossSize, double startPosition, double endPosition)
373 {
374     float secondKeyTime = expand_ ? POSITION_ANIMATION_EXPAND_TIME : POSITION_ANIMATION_COLLAPSE_TIME;
375     double secondKeyValue = expand_ ? endPosition : startPosition;
376     RefPtr<Curve> firstCurve;
377     if (expand_) {
378         firstCurve = Curves::FAST_OUT_LINEAR_IN;
379     } else {
380         firstCurve = Curves::LINEAR;
381     }
382     RefPtr<Curve> secondCurve;
383     if (expand_) {
384         secondCurve = Curves::LINEAR;
385     } else {
386         secondCurve = Curves::FRICTION;
387     }
388     auto positionKeyframe1 = MakeRefPtr<Keyframe<double>>(0.0f, startPosition);
389     auto positionKeyframe2 = MakeRefPtr<Keyframe<double>>(secondKeyTime, secondKeyValue);
390     positionKeyframe2->SetCurve(firstCurve);
391     auto positionKeyframe3 = MakeRefPtr<Keyframe<double>>(1.0f, endPosition);
392     positionKeyframe3->SetCurve(secondCurve);
393     auto positionAnimation = MakeRefPtr<KeyframeAnimation<double>>();
394     positionAnimation->AddKeyframe(positionKeyframe1);
395     positionAnimation->AddKeyframe(positionKeyframe2);
396     positionAnimation->AddKeyframe(positionKeyframe3);
397     positionAnimation->AddListener([weakItemGroup = WeakClaim(this), crossSize](double value) {
398         auto itemGroup = weakItemGroup.Upgrade();
399         if (itemGroup) {
400             itemGroup->LayoutExpandableList(value);
401             itemGroup->SetLayoutSize(itemGroup->MakeValue<Size>(value, crossSize));
402         }
403     });
404     return positionAnimation;
405 }
406 
CreateOpacityAnimation()407 RefPtr<KeyframeAnimation<int32_t>> RenderListItemGroup::CreateOpacityAnimation()
408 {
409     int32_t startOpacity = expand_ ? 0 : UINT8_MAX;
410     int32_t endOpacity = UINT8_MAX - startOpacity;
411     double secondKeyTime = expand_ ? OPACITY_ANIMATION_EXPAND_TIME : OPACITY_ANIMATION_COLLAPSE_TIME;
412     int32_t secondKeyValue = expand_ ? startOpacity : endOpacity;
413     RefPtr<Curve> firstCurve;
414     if (expand_) {
415         firstCurve = Curves::LINEAR;
416     } else {
417         firstCurve = Curves::FAST_OUT_LINEAR_IN;
418     }
419     RefPtr<Curve> secondCurve;
420     if (expand_) {
421         secondCurve = Curves::FRICTION;
422     } else {
423         secondCurve = Curves::LINEAR;
424     }
425     auto opacityKeyframe1 = MakeRefPtr<Keyframe<int32_t>>(0.0f, startOpacity);
426     auto opacityKeyframe2 = MakeRefPtr<Keyframe<int32_t>>(secondKeyTime, secondKeyValue);
427     opacityKeyframe2->SetCurve(firstCurve);
428     auto opacityKeyframe3 = MakeRefPtr<Keyframe<int32_t>>(1.0f, endOpacity);
429     opacityKeyframe3->SetCurve(secondCurve);
430     auto opacityAnimation = MakeRefPtr<KeyframeAnimation<int32_t>>();
431     opacityAnimation->AddKeyframe(opacityKeyframe1);
432     opacityAnimation->AddKeyframe(opacityKeyframe2);
433     opacityAnimation->AddKeyframe(opacityKeyframe3);
434     opacityAnimation->AddListener([weakItemGroup = WeakClaim(this)](int32_t value) {
435         auto itemGroup = weakItemGroup.Upgrade();
436         if (itemGroup) {
437             itemGroup->SetChildOpacity(value);
438         }
439     });
440     return opacityAnimation;
441 }
442 
SetChildOpacity(int32_t opacity)443 void RenderListItemGroup::SetChildOpacity(int32_t opacity)
444 {
445     auto children = GetChildren();
446     auto rIter = children.rbegin();
447     while (rIter != children.rend()) {
448         auto child = *rIter++;
449         if (child == primary_ || child == arrowImage_) {
450             continue;
451         }
452         auto listItem = RenderListItem::GetRenderListItem(child);
453         if (listItem) {
454             listItem->SetStretch(true);
455             listItem->SetExpandOpacity(opacity);
456             listItem->MarkNeedRender();
457         }
458     }
459     MarkNeedLayout();
460 }
461 
SetChildStretch(bool isStretch)462 void RenderListItemGroup::SetChildStretch(bool isStretch)
463 {
464     auto children = GetChildren();
465     auto rIter = children.rbegin();
466     while (rIter != children.rend()) {
467         auto child = *rIter++;
468         if (child == primary_ || child == arrowImage_) {
469             continue;
470         }
471         auto listItem = RenderListItem::GetRenderListItem(child);
472         if (listItem) {
473             listItem->SetStretch(isStretch);
474         }
475     }
476 }
477 
Update(const RefPtr<Component> & component)478 void RenderListItemGroup::Update(const RefPtr<Component>& component)
479 {
480     auto theme = GetTheme<ListItemTheme>();
481     if (theme) {
482         imageSize_ = theme->GetGroupImageSize();
483     }
484 
485     if (!animator_) {
486         animator_ = CREATE_ANIMATOR(GetContext());
487     }
488 
489     itemGroup_ = component;
490     auto itemGroup = AceType::DynamicCast<ListItemGroupComponent>(itemGroup_);
491     if (itemGroup) {
492         groupId_ = itemGroup->GetGroupId();
493         expand_ = itemGroup->GetExpand();
494         onClicked_ = AceAsyncEvent<void(const std::string&)>::Create(itemGroup->GetOnClicked(), GetContext());
495         onCollapse_ = AceAsyncEvent<void(const std::string&)>::Create(itemGroup->GetOnCollapse(), GetContext());
496         onExpand_ = AceAsyncEvent<void(const std::string&)>::Create(itemGroup->GetOnExpand(), GetContext());
497 
498         if (initialUpdate_) {
499             InitialImage();
500             initialUpdate_ = false;
501         }
502 
503         SetAnimationStop();
504         MarkNeedLayout();
505     }
506     RenderListItem::Update(component);
507     InitAccessibilityEventListener();
508 }
509 
InitAccessibilityEventListener()510 void RenderListItemGroup::InitAccessibilityEventListener()
511 {
512     const auto& accessibilityNode = GetAccessibilityNode().Upgrade();
513     if (!accessibilityNode) {
514         return;
515     }
516     accessibilityNode->AddSupportAction(AceAction::ACTION_CLICK);
517     accessibilityNode->SetActionClickImpl([weakPtr = WeakClaim(this)]() {
518         const auto& list = weakPtr.Upgrade();
519         if (list) {
520             list->HandleClicked();
521         }
522     });
523 }
524 
InitialImage()525 void RenderListItemGroup::InitialImage()
526 {
527     InternalResource::ResourceId resourceId = SystemProperties::GetDeviceType() == DeviceType::WATCH
528                                                   ? InternalResource::ResourceId::WATCH_DOWN_ARROW_SVG
529                                                   : InternalResource::ResourceId::DOWN_ARROW_SVG;
530     double rotateAngle = GetRotateAngle(expand_);
531     auto imageComponent = AceType::MakeRefPtr<ImageComponent>(resourceId);
532     imageComponent->SetWidth(imageSize_);
533     imageComponent->SetHeight(imageSize_);
534 
535     arrowImage_ = AceType::DynamicCast<RenderImage>(imageComponent->CreateRenderNode());
536     arrowImage_->SetRotate(rotateAngle);
537     arrowImage_->Attach(GetContext());
538     arrowImage_->Update(imageComponent);
539 }
540 
ChangeDirection(FlexDirection direction)541 void RenderListItemGroup::ChangeDirection(FlexDirection direction)
542 {
543     if (direction_ != direction) {
544         direction_ = direction;
545         expand_ = false;
546         UpdateExpandStatusInList();
547         ResetLayout();
548     }
549 }
550 
ResetLayout()551 void RenderListItemGroup::ResetLayout()
552 {
553     SetLayoutSize(Size(0.0, 0.0));
554     if (primary_) {
555         primary_->SetPosition(Offset::Zero());
556     }
557 }
558 
UpdateExpandStatusInList()559 void RenderListItemGroup::UpdateExpandStatusInList()
560 {
561     auto parentNode = GetParent().Upgrade();
562     while (parentNode) {
563         RefPtr<RenderList> listNode = AceType::DynamicCast<RenderList>(parentNode);
564         if (listNode) {
565             listNode->SetGroupState(GetIndex(), expand_);
566             break;
567         }
568         parentNode = parentNode->GetParent().Upgrade();
569     }
570 }
571 
GetRenderBox(const RefPtr<RenderNode> & renderNode)572 RefPtr<RenderBox> RenderListItemGroup::GetRenderBox(const RefPtr<RenderNode>& renderNode)
573 {
574     RefPtr<RenderNode> box = renderNode;
575     while (box) {
576         auto& children = box->GetChildren();
577         if (children.empty()) {
578             return nullptr;
579         }
580         if (AceType::InstanceOf<RenderBox>(box) && AceType::InstanceOf<RenderFlex>(children.front())) {
581             return AceType::DynamicCast<RenderBox>(box);
582         }
583         box = children.front();
584     }
585     return nullptr;
586 }
587 
CreateRotateAnimation()588 RefPtr<KeyframeAnimation<double>> RenderListItemGroup::CreateRotateAnimation()
589 {
590     double secondKeyTime = expand_ ? ROTATE_ANIMATION_EXPAND_TIME : ROTATE_ANIMATION_COLLAPSE_TIME;
591     auto rotateKeyframe1 = MakeRefPtr<Keyframe<double>>(0.0f, 0.0f);
592     auto rotateKeyframe2 = MakeRefPtr<Keyframe<double>>(secondKeyTime, ROTATE_ANGLE);
593     rotateKeyframe2->SetCurve(Curves::FRICTION);
594     auto rotateKeyframe3 = MakeRefPtr<Keyframe<double>>(1.0f, ROTATE_ANGLE);
595     rotateKeyframe3->SetCurve(Curves::FRICTION);
596     auto rotateAnimation = MakeRefPtr<KeyframeAnimation<double>>();
597     rotateAnimation->AddKeyframe(rotateKeyframe1);
598     rotateAnimation->AddKeyframe(rotateKeyframe2);
599     rotateAnimation->AddKeyframe(rotateKeyframe3);
600     rotateAnimation->AddListener([weakItemGroup = WeakClaim(this)](double value) {
601         auto itemGroup = weakItemGroup.Upgrade();
602         if (itemGroup) {
603             itemGroup->RotateArrow(value);
604         }
605     });
606     return rotateAnimation;
607 }
608 
RotateArrow(double angle)609 void RenderListItemGroup::RotateArrow(double angle)
610 {
611     if (arrowImage_) {
612         double rotateAngle = expand_ ? angle : ROTATE_ANGLE - angle;
613         arrowImage_->SetRotate(rotateAngle);
614         arrowImage_->MarkNeedRender();
615     }
616 }
617 
UpdateGroupComponentStatus(bool expand)618 void RenderListItemGroup::UpdateGroupComponentStatus(bool expand)
619 {
620     // Restore the state on component for recycle and rebuild.
621     auto itemGroup = AceType::DynamicCast<ListItemGroupComponent>(itemGroup_);
622     if (itemGroup) {
623         itemGroup->SetExpand(expand);
624     }
625 }
626 
ResetChildVisibleState()627 void RenderListItemGroup::ResetChildVisibleState()
628 {
629     for (const auto& child : GetChildren()) {
630         if (child == primary_ || child == arrowImage_) {
631             child->SetVisible(true);
632             continue;
633         }
634         child->SetVisible(expand_);
635     }
636 }
637 
GetRenderList()638 RefPtr<RenderList> RenderListItemGroup::GetRenderList()
639 {
640     auto parentNode = GetParent().Upgrade();
641     while (parentNode) {
642         RefPtr<RenderList> listNode = AceType::DynamicCast<RenderList>(parentNode);
643         if (listNode) {
644             return listNode;
645         }
646         parentNode = parentNode->GetParent().Upgrade();
647     }
648     return nullptr;
649 }
650 
ShowFocusAnimation()651 void RenderListItemGroup::ShowFocusAnimation()
652 {
653     RefPtr<RenderListItem> focusItem;
654     if (!expand_) {
655         focusItem = RenderListItem::GetRenderListItem(primary_);
656         if (focusItem) {
657             focusItem->SetFocused(true);
658             focusItem->ShowFocusAnimation(true, Rect(0.0, 0.0, 0.0, 0.0));
659         }
660     }
661     for (const auto& child : GetChildren()) {
662         if (child == arrowImage_) {
663             continue;
664         }
665         focusItem = RenderListItem::GetRenderListItem(child);
666         if (focusItem && focusItem->IsFocused()) {
667             if (expand_) {
668                 focusItem->ShowFocusAnimation(true, Rect(0.0, 0.0, 0.0, 0.0));
669                 break;
670             }
671             if (child != primary_) {
672                 focusItem->SetFocused(false);
673             }
674             break;
675         }
676     }
677 }
678 
GetNextFocusIndex(int32_t lastFocusIndex,bool vertical,bool reverse)679 int32_t RenderListItemGroup::GetNextFocusIndex(int32_t lastFocusIndex, bool vertical, bool reverse)
680 {
681     KeyDirection key = DIRECTION_MAP.at(rightToLeft_).at(direction_).at(vertical).at(reverse);
682     switch (key) {
683         case KeyDirection::UP:
684             return lastFocusIndex - 1;
685         case KeyDirection::DOWN: {
686             return lastFocusIndex + 1;
687         }
688         default:
689             break;
690     }
691     return -1;
692 }
693 
ItemPrimaryChange(int32_t index)694 void RenderListItemGroup::ItemPrimaryChange(int32_t index)
695 {
696     if (isDefaultPrimary_) {
697         for (auto child : GetChildren()) {
698             auto listItem = RenderListItem::GetRenderListItem(child);
699             if (listItem && listItem->GetPrimary()) {
700                 listItem->SetPrimary(false);
701                 isDefaultPrimary_ = false;
702                 break;
703             }
704         }
705     }
706 }
707 
708 } // namespace OHOS::Ace
709