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