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