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