• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components/list/list_layout_manager.h"
17 
18 #include <algorithm>
19 
20 #include "base/log/log.h"
21 #include "base/utils/utils.h"
22 #include "core/animation/bilateral_spring_node.h"
23 #include "core/animation/curve.h"
24 #include "core/animation/curve_animation.h"
25 #include "core/components/list/list_component.h"
26 #include "core/components/list/render_list_item.h"
27 #include "core/components/list/render_list_item_group.h"
28 #include "core/components/scroll/render_multi_child_scroll.h"
29 
30 namespace OHOS::Ace {
31 namespace {
32 
33 const double MIN_FRICTION = 0.0;
34 const double MAX_FRICTION = 1.0;
35 const int32_t ADD_DEL_DURATION = 300;
36 const int32_t CHAIN_ANIMATION_NODE_COUNT = 30;
37 
38 } // namespace
39 
ListLayoutManager(RenderList & renderList)40 ListLayoutManager::ListLayoutManager(RenderList& renderList) : renderList_(renderList) {}
41 
Update()42 void ListLayoutManager::Update()
43 {
44     maxCount_ = renderList_.GetMaxCount();
45     cachedCount_ = renderList_.GetCachedCount();
46     beginIndex_ = renderList_.GetBeginIndex();
47     endIndex_ = renderList_.GetEndIndex();
48     repeatedLength_ = renderList_.GetRepeatedLength();
49     length_ = renderList_.GetLength();
50     indexOffset_ = renderList_.GetIndexOffset();
51     itemExtent_ = renderList_.GetItemExtent();
52     rightToLeft_ = renderList_.GetRightToLeft();
53     crossAxisAlign_ = renderList_.GetFlexAlign();
54     direction_ = renderList_.GetDirection();
55     if (direction_ == FlexDirection::COLUMN || direction_ == FlexDirection::COLUMN_REVERSE) {
56         isVertical_ = true;
57     } else {
58         isVertical_ = false;
59     }
60 
61     if (enableChain_ != renderList_.IsEnableChain()) {
62         if (renderList_.IsEnableChain()) {
63             auto context = renderList_.GetContext().Upgrade();
64             if (context) {
65                 chainInterval_ = context->NormalizeToPx(renderList_.GetChainProperty().Interval());
66             }
67             InitChainAnimation(CHAIN_ANIMATION_NODE_COUNT);
68         } else {
69             chainInterval_ = 0.0;
70         }
71         enableChain_ = renderList_.IsEnableChain();
72     }
73     renderList_.MarkNeedLayout();
74 }
75 
MakeInnerLayoutParam(FlexAlign crossAxisAlign) const76 LayoutParam ListLayoutManager::MakeInnerLayoutParam(FlexAlign crossAxisAlign) const
77 {
78     LayoutParam innerLayout;
79     Size maxSize;
80     Size minSize;
81     if (itemExtent_.IsValid()) {
82         double extent = itemExtent_.Value();
83         if (itemExtent_.Unit() == DimensionUnit::PERCENT) {
84             extent *= renderList_.GetMainSize(viewPort_);
85         } else {
86             extent = renderList_.NormalizeToPx(itemExtent_);
87         }
88         if (direction_ == FlexDirection::ROW || direction_ == FlexDirection::ROW_REVERSE) {
89             maxSize = Size(extent, renderList_.GetLayoutParam().GetMaxSize().Height());
90             minSize = Size(extent, renderList_.GetLayoutParam().GetMinSize().Height());
91         } else {
92             maxSize = Size(renderList_.GetLayoutParam().GetMaxSize().Width(), extent);
93             minSize = Size(renderList_.GetLayoutParam().GetMinSize().Width(), extent);
94         }
95         if (crossAxisAlign == FlexAlign::STRETCH) {
96             minSize = maxSize;
97         }
98     } else {
99         if (direction_ == FlexDirection::ROW || direction_ == FlexDirection::ROW_REVERSE) {
100             maxSize = Size(Size::INFINITE_SIZE, renderList_.GetLayoutParam().GetMaxSize().Height());
101             if (crossAxisAlign == FlexAlign::STRETCH) {
102                 minSize = Size(0.0, renderList_.GetLayoutParam().GetMaxSize().Height());
103             } else {
104                 minSize = Size(0.0, renderList_.GetLayoutParam().GetMinSize().Height());
105             }
106         } else {
107             maxSize = Size(renderList_.GetLayoutParam().GetMaxSize().Width(), Size::INFINITE_SIZE);
108             if (crossAxisAlign == FlexAlign::STRETCH) {
109                 minSize = Size(renderList_.GetLayoutParam().GetMaxSize().Width(), 0.0);
110             } else {
111                 minSize = Size(renderList_.GetLayoutParam().GetMinSize().Width(), 0.0);
112             }
113         }
114     }
115     if (NearEqual(renderList_.GetCrossSize(minSize), Size::INFINITE_SIZE)) {
116         renderList_.SetCrossSize(minSize, renderList_.GetCrossSize(viewPort_));
117     }
118     if (NearEqual(renderList_.GetCrossSize(maxSize), Size::INFINITE_SIZE)) {
119         renderList_.SetCrossSize(maxSize, renderList_.GetCrossSize(viewPort_));
120     }
121     innerLayout.SetMaxSize(maxSize);
122     innerLayout.SetMinSize(minSize);
123     return innerLayout;
124 }
125 
CalculateCachedRange(int32_t viewBegin,int32_t viewEnd,int32_t cachedCount,int32_t & cachedBegin,int32_t & cachedEnd)126 void ListLayoutManager::CalculateCachedRange(
127     int32_t viewBegin, int32_t viewEnd, int32_t cachedCount, int32_t& cachedBegin, int32_t& cachedEnd)
128 {
129     cachedBegin = (viewBegin - cachedCount > 0) ? (viewBegin - cachedCount) : 0;
130     if (length_ != LIST_LENGTH_INFINITE) {
131         cachedEnd = (viewEnd + cachedCount > length_) ? length_ : viewEnd + cachedCount;
132     } else {
133         cachedEnd = viewEnd + cachedCount;
134     }
135 }
136 
RequestMoreItemsIfNeeded(int32_t viewBegin,int32_t viewEnd)137 void ListLayoutManager::RequestMoreItemsIfNeeded(int32_t viewBegin, int32_t viewEnd)
138 {
139     int32_t cachedBegin;
140     int32_t cachedEnd;
141 
142     if (beginIndex_ == LIST_PARAM_INVAID || endIndex_ == LIST_PARAM_INVAID) {
143         return;
144     }
145 
146     if (viewBegin > viewEnd) {
147         std::swap(viewBegin, viewEnd);
148     }
149 
150     CalculateCachedRange(viewBegin, viewEnd, cachedCount_, cachedBegin, cachedEnd);
151     if (CalculateRepeatedIndex(cachedBegin) < beginIndex_ || CalculateRepeatedIndex(cachedEnd) > endIndex_) {
152         int32_t requestBegin;
153         int32_t requestEnd;
154         CalculateCachedRange(viewBegin, viewEnd, 2 * cachedCount_, requestBegin, requestEnd);
155         renderList_.RequestMoreItems(CalculateRepeatedIndex(requestBegin), CalculateRepeatedIndex(requestEnd));
156     }
157 }
158 
RefreshLayout()159 void ListLayoutManager::RefreshLayout()
160 {
161     if (!needRefresh_) {
162         return;
163     }
164     needRefresh_ = false;
165     auto items = renderList_.GetItems();
166     auto iter = items.begin();
167     if (iter != items.end()) {
168         auto firstItem = RenderListItem::GetRenderListItem(iter->second);
169         if (firstItem && firstItem->GetOperation() != LIST_ITEM_OP_NONE) {
170             LOGI("First item changed, recycle all child and layout again.");
171             renderList_.RecycleAllChild();
172             renderList_.RefreshOffset(0.0);
173             itemPosition_.clear();
174             itemGroupsExpand_.clear();
175             itemGroupsFocusIndex_.clear();
176         } else {
177             std::vector<int32_t> needRemoveItems;
178             std::map<int32_t, RefPtr<RenderNode>> newItems;
179             int32_t rmCount = 0;
180             while (iter != items.end()) {
181                 auto item = RenderListItem::GetRenderListItem(iter->second);
182                 if (item) {
183                     if (item->GetOperation() == LIST_ITEM_OP_REMOVE) {
184                         LOGI("This item[%{public}d] removed, notify element to recycle.", item->GetIndex());
185                         needRemoveItems.emplace_back(iter->first);
186                         itemPosition_.erase(item->GetIndex());
187                         itemGroupsExpand_.erase(item->GetIndex());
188                         itemGroupsFocusIndex_.erase(item->GetIndex());
189                         ++rmCount;
190                     } else {
191                         if (rmCount > 0) {
192                             itemPosition_.erase(item->GetIndex());
193                             itemGroupsExpand_.erase(item->GetIndex());
194                             itemGroupsFocusIndex_.erase(item->GetIndex());
195                         }
196                         item->SetIndex(item->GetIndex() - rmCount);
197                         newItems.emplace(std::make_pair(item->GetIndex(), iter->second));
198                     }
199                 }
200                 iter++;
201             }
202             renderList_.RecycleByItems(needRemoveItems);
203             renderList_.ResetItems(newItems);
204         }
205     }
206     renderList_.OnRefreshed();
207 }
208 
NotifyNeedRefresh()209 void ListLayoutManager::NotifyNeedRefresh()
210 {
211     if (isAnimating_) {
212         controller_->ClearStopListeners();
213         controller_->ClearInterpolators();
214         controller_->Stop();
215         isAnimating_ = false;
216         friction_ = MAX_FRICTION;
217         RefreshLayout();
218         mainOffset_ = -1.0;
219     }
220 }
221 
CheckNeedAnimation()222 bool ListLayoutManager::CheckNeedAnimation()
223 {
224     // First layout all item is OP(Add), no need to animate.
225     if (firstLayout_) {
226         RefreshLayout();
227         return false;
228     }
229 
230     // If current refresh tag is false, do not trigger animate.
231     if (!needRefresh_) {
232         return false;
233     }
234 
235     // If current is Playing animation, do not trigger again.
236     if (isAnimating_) {
237         return false;
238     }
239 
240     // Check all the items Whether bring ADD/DEL Operation.
241     bool needAnimation = false;
242     auto iter = renderList_.GetItems().begin();
243     while (iter != renderList_.GetItems().end()) {
244         auto item = RenderListItem::GetRenderListItem(iter->second);
245         if (item && item->GetOperation() != LIST_ITEM_OP_NONE) {
246             needAnimation = true;
247             break;
248         }
249         iter++;
250     }
251     if (!needAnimation) {
252         return false;
253     }
254 
255     // create the animate controller.
256     if (!controller_) {
257         auto context = renderList_.GetContext().Upgrade();
258         if (!context) {
259             return false;
260         }
261         controller_ = AceType::MakeRefPtr<Animator>(context);
262     }
263     return true;
264 }
265 
AnimationForItemUpdate()266 void ListLayoutManager::AnimationForItemUpdate()
267 {
268     if (!CheckNeedAnimation()) {
269         return;
270     }
271 
272     if (!controller_->IsStopped() || isAnimating_) {
273         controller_->Stop();
274         isAnimating_ = false;
275     }
276     controller_->ClearStopListeners();
277     controller_->ClearInterpolators();
278     auto animation = AceType::MakeRefPtr<CurveAnimation<double>>(MIN_FRICTION, MAX_FRICTION, Curves::FRICTION);
279     animation->AddListener([weakList = AceType::WeakClaim(this)](double value) {
280         auto list = weakList.Upgrade();
281         if (list) {
282             list->friction_ = value;
283             list->renderList_.MarkNeedLayout(true);
284         }
285     });
286     controller_->AddInterpolator(animation);
287     controller_->SetDuration(ADD_DEL_DURATION);
288     controller_->Play();
289     isAnimating_ = true;
290     friction_ = MIN_FRICTION;
291     controller_->AddStopListener([weakList = AceType::WeakClaim(this)]() {
292         auto list = weakList.Upgrade();
293         if (list) {
294             list->isAnimating_ = false;
295             list->friction_ = MAX_FRICTION;
296             list->RefreshLayout();
297             list->mainOffset_ = -1.0;
298             list->renderList_.RefreshScrollExtent();
299         }
300     });
301 }
302 
AdjustLayoutParam(const RefPtr<RenderNode> child,LayoutParam param)303 LayoutParam ListLayoutManager::AdjustLayoutParam(const RefPtr<RenderNode> child, LayoutParam param)
304 {
305     LayoutParam layoutParam = param;
306     if (!isAnimating_) {
307         return layoutParam;
308     }
309     auto listItem = RenderListItem::GetRenderListItem(child);
310     if (!listItem) {
311         return layoutParam;
312     }
313     double symbol = 1.0;
314     double friction = 0.0;
315     switch (listItem->GetOperation()) {
316         case LIST_ITEM_OP_ADD: {
317             friction = friction_;
318             break;
319         }
320         case LIST_ITEM_OP_REMOVE: {
321             friction = 1.0 - friction_;
322             symbol = -1.0;
323             break;
324         }
325         default: {
326             return layoutParam;
327         }
328     }
329 
330     Size layoutSize = listItem->GetPreLayoutSize();
331     if (layoutSize == Size(0.0, 0.0)) {
332         child->Layout(param);
333         layoutSize = child->GetLayoutSize();
334         listItem->SetPreLayoutSize(layoutSize);
335     }
336     double listMainSize = renderList_.GetMainSize(renderList_.GetLayoutSize());
337     double viewPortSize = renderList_.GetMainSize(viewPort_);
338     double maxOffset = std::max(0.0, listMainSize - viewPortSize);
339     // Only remove will adjust current offset.
340     if (symbol < 0.0 && listItem->GetIndex() >= renderList_.GetCurrentMaxIndex()) {
341         if (mainOffset_ < 0.0) {
342             mainOffset_ = -renderList_.GetMainPosition(position_);
343         }
344         double offset = mainOffset_ + renderList_.GetMainSize(layoutSize) * friction_ * symbol;
345         offset = std::clamp(offset, 0.0, maxOffset);
346         renderList_.RefreshOffset(offset);
347         ResetLayoutRange(head_, tail_, renderList_.MakeValue<Offset>(-offset, 0.0), viewPort_);
348     } else if (maxOffset < -renderList_.GetMainPosition(position_)) {
349         renderList_.RefreshOffset(maxOffset);
350         ResetLayoutRange(head_, tail_, renderList_.MakeValue<Offset>(-maxOffset, 0.0), viewPort_);
351     }
352     Size size = isVertical_ ? Size(layoutSize.Width(), layoutSize.Height() * friction)
353                             : Size(layoutSize.Width() * friction, layoutSize.Height());
354     layoutParam.SetMinSize(size);
355     layoutParam.SetMaxSize(size);
356     return layoutParam;
357 }
358 
FlushChainAnimation()359 double ListLayoutManager::FlushChainAnimation()
360 {
361     if (!enableChain_ || !chain_ || !chainAdapter_) {
362         return 0.0;
363     }
364     double deltaDistance = 0.0;
365     bool needSetValue = false;
366     RefPtr<RenderNode> parentNode = renderList_.GetParent().Upgrade();
367     RefPtr<RenderMultiChildScroll> scroll = AceType::DynamicCast<RenderMultiChildScroll>(parentNode);
368     if (!scroll) {
369         LOGE("Flush chain animation failed, Get Parent failed.");
370         return 0.0;
371     }
372     bool overScroll = scroll->IsSpringMotionRunning();
373     if (chainOverScroll_ != overScroll) {
374         if (overScroll) {
375             const auto& springProperty = renderList_.GetOverSpringProperty();
376             if (springProperty && springProperty->IsValid()) {
377                 chain_->SetControlStiffness(springProperty->Stiffness());
378                 chain_->SetControlDamping(springProperty->Damping());
379             }
380         } else {
381             chain_->SetControlStiffness(renderList_.GetChainProperty().ControlStiffness());
382             chain_->SetControlDamping(renderList_.GetChainProperty().ControlDamping());
383         }
384         chain_->OnControlNodeChange();
385         chainOverScroll_ = overScroll;
386     }
387     chain_->FlushAnimation();
388     if (renderList_.GetDragStartIndexPending() != renderList_.GetDragStartIndex()) {
389         deltaDistance =
390             chainAdapter_->ResetControl(renderList_.GetDragStartIndexPending() - renderList_.GetDragStartIndex());
391         LOGD("Switch chain control node. %{public}d -> %{public}d, deltaDistance: %{public}.1f",
392             renderList_.GetDragStartIndex(), renderList_.GetDragStartIndexPending(), deltaDistance);
393         renderList_.SetDragStartIndex(renderList_.GetDragStartIndexPending());
394         chainAdapter_->SetDeltaValue(-deltaDistance);
395         chainOffset_ += -deltaDistance;
396         needSetValue = true;
397     }
398     if (!NearZero(renderList_.GetCurrentDelta())) {
399         LOGD("Set delta chain value. delta: %{public}.1f", renderList_.GetCurrentDelta());
400         chainAdapter_->SetDeltaValue(renderList_.GetCurrentDelta());
401         chainOffset_ += renderList_.GetCurrentDelta();
402         renderList_.ResetCurrentDelta();
403         needSetValue = true;
404     }
405     if (needSetValue) {
406         LOGD("FlushChainAnimation: %{public}s", chainAdapter_->DumpNodes().c_str());
407         chain_->SetValue(0.0);
408     }
409     return deltaDistance;
410 }
411 
PerformLayout()412 void ListLayoutManager::PerformLayout()
413 {
414     LOGD("PerformLayout head:%{public}lf tail:%{public}lf", head_, tail_);
415     if (renderList_.GetAddDeleteEffect()) {
416         AnimationForItemUpdate();
417     } else {
418         RefreshLayout();
419     }
420 
421     UpdateItemPosition();
422     int32_t itemIndex = GetIndexByPosition(head_ - chainOffset_);
423     int32_t firstIndex = itemIndex;
424     renderList_.RecycleHead(itemIndex - 1); // Recycle head items.
425     double curMainSize = GetItemPosition(itemIndex);
426     double curAnimateMainSize = curMainSize + GetItemAnimationValue(itemIndex);
427     LayoutParam innerLayout = MakeInnerLayoutParam(crossAxisAlign_);
428     auto itemChild = renderList_.GetChildByIndex(itemIndex);
429     while (!itemChild && curAnimateMainSize < tail_ - chainOffset_ && CheckItemPosition(itemIndex)) {
430         itemChild = renderList_.GetChildByIndex(itemIndex);
431         curMainSize = GetItemPosition(itemIndex);
432         curAnimateMainSize = curMainSize + GetItemAnimationValue(itemIndex);
433         ++itemIndex;
434     }
435     while (itemChild) {
436         UpdateItemGroupAttr(itemChild);
437         itemChild->Layout(AdjustLayoutParam(itemChild, innerLayout));
438         if (enableChain_ && firstIndex != itemIndex) {
439             curMainSize += chainInterval_ * HALF_ITEM_SIZE;
440         }
441         itemPosition_[itemIndex] = curMainSize;
442         SetChildPosition(itemChild, itemIndex, curMainSize);
443         curAnimateMainSize = renderList_.GetMainPosition(itemChild->GetPosition() - position_);
444         double childLayoutSize = renderList_.GetMainSize(itemChild->GetLayoutSize());
445         curMainSize += childLayoutSize;
446         curAnimateMainSize += childLayoutSize;
447         if (curAnimateMainSize >= tail_ - chainOffset_) {
448             break;
449         }
450         itemChild = renderList_.GetChildByIndex(++itemIndex);
451         if (enableChain_ && itemChild) {
452             curMainSize += chainInterval_ * HALF_ITEM_SIZE;
453         }
454     }
455     RequestMoreItemsIfNeeded(firstIndex, itemIndex);
456     renderList_.RecycleTail(itemIndex + 1); // Recycle tail items.
457 
458     UpdateMaxEndOffset(itemIndex, curMainSize);
459     Size layoutSize = renderList_.MakeValue<Size>(maxEndOffset_, renderList_.GetCrossSize(viewPort_));
460     renderList_.SetLayoutSize(layoutSize);
461     renderList_.CalculateStickyItem(position_);
462     ShowItemFocusAnimation();
463     firstLayout_ = false;
464     chainOffset_ = 0.0;
465 }
466 
MoveItemToViewPort(double position)467 void ListLayoutManager::MoveItemToViewPort(double position)
468 {
469     RefPtr<RenderNode> item = renderList_.GetChildByPosition(position);
470     if (!item) {
471         LOGE("[ListFocus]MoveItemToViewPort, Get item failed.");
472         return;
473     }
474     double size = renderList_.GetMainSize(item->GetLayoutSize());
475 
476     // jump to this item using position
477     RefPtr<RenderNode> parentNode = renderList_.GetParent().Upgrade();
478     RefPtr<RenderMultiChildScroll> scroll = AceType::DynamicCast<RenderMultiChildScroll>(parentNode);
479     if (!scroll) {
480         LOGE("[ListFocus]MoveItemToViewPort, Get Parent failed.");
481         return;
482     }
483     double focusEffectWidth = size * (TV_ITEM_SCALE - DEFAULT_SCALE) * HALF_ITEM_SIZE;
484     double animationOffset = scroll->NormalizeToPx(FOCUS_BOUNDARY);
485     scroll->MoveItemToViewPort(position, size, animationOffset + focusEffectWidth);
486     LOGD("[ListFocus][list] Direction:%{public}d, Item position:%{public}.1lf.", direction_, position);
487 }
488 
MoveItemGroupToViewPort(double position,double size)489 void ListLayoutManager::MoveItemGroupToViewPort(double position, double size)
490 {
491     // jump to this item using position
492     RefPtr<RenderNode> parentNode = renderList_.GetParent().Upgrade();
493     RefPtr<RenderMultiChildScroll> scroll = AceType::DynamicCast<RenderMultiChildScroll>(parentNode);
494     if (!scroll) {
495         LOGE("[ListFocus]MoveItemGroupToViewPort, Get Parent failed.");
496         return;
497     }
498     double focusEffectWidth = size * (TV_ITEM_SCALE - DEFAULT_SCALE) * HALF_ITEM_SIZE;
499     double animationOffset = scroll->NormalizeToPx(FOCUS_BOUNDARY);
500     scroll->MoveItemToViewPort(position, size, animationOffset + focusEffectWidth);
501     LOGD("[ListFocus][list] Direction:%{public}d, Item position:%{public}.1lf.", direction_, position);
502 }
503 
ShowItemFocusAnimation()504 void ListLayoutManager::ShowItemFocusAnimation()
505 {
506     RefPtr<RenderListItem> focusItem;
507     for (const auto& item : renderList_.GetItems()) {
508         if (!item.second || item.second->GetChildren().empty()) {
509             continue;
510         }
511 
512         focusItem = RenderListItem::GetRenderListItem(item.second);
513         if (!focusItem) {
514             break;
515         } else {
516             if (focusItem->IsFocused()) {
517                 auto focusItemGroup = AceType::DynamicCast<RenderListItemGroup>(focusItem);
518                 if (focusItemGroup) {
519                     focusItemGroup->ShowFocusAnimation();
520                 } else {
521                     focusItem->ShowFocusAnimation(true, Rect(renderList_.GetGlobalOffset(), viewPort_));
522                 }
523             }
524         }
525     }
526 }
527 
focusMove(KeyDirection direction)528 int32_t ListLayoutManager::focusMove(KeyDirection direction)
529 {
530     int32_t index = focusMove_;
531     switch (direction) {
532         case KeyDirection::UP:
533         case KeyDirection::DOWN: {
534             auto next = renderList_.GetItemByIndex(direction == KeyDirection::UP ? --index : ++index);
535             if (next) {
536                 return index;
537             }
538             break;
539         }
540         default:
541             break;
542     }
543     return -1;
544 }
545 
LayoutToItem(int32_t toIndex)546 void ListLayoutManager::LayoutToItem(int32_t toIndex)
547 {
548     int32_t curHeadIndex = renderList_.GetCurrentMinIndex();
549     int32_t curTailIndex = renderList_.GetCurrentMaxIndex();
550     curTailIndex = std::max(curTailIndex, 0);
551     double curMainSize = GetItemPosition(curTailIndex);
552     LayoutParam innerLayout = MakeInnerLayoutParam(crossAxisAlign_);
553     auto itemChild = renderList_.GetChildByIndex(curTailIndex);
554     while (itemChild) {
555         itemChild->Layout(innerLayout);
556         if (enableChain_ && curTailIndex != curHeadIndex) {
557             curMainSize += chainInterval_ * HALF_ITEM_SIZE;
558         }
559         SetChildPosition(itemChild, curTailIndex, curMainSize);
560         itemPosition_[curTailIndex] = curMainSize;
561         curMainSize += renderList_.GetMainSize(itemChild->GetLayoutSize());
562         if (curTailIndex >= toIndex) {
563             break; // Already layout to the target index.
564         }
565         itemChild = renderList_.GetChildByIndex(++curTailIndex);
566         renderList_.RecycleHead(curHeadIndex++);
567         if (enableChain_ && itemChild) {
568             curMainSize += chainInterval_ * HALF_ITEM_SIZE;
569         }
570     }
571     UpdateMaxEndOffset(curTailIndex, curMainSize);
572     curMainSize = AdjustLayoutSize(maxEndOffset_);
573     Size layoutSize = renderList_.MakeValue<Size>(curMainSize, renderList_.GetCrossSize(viewPort_));
574     renderList_.SetLayoutSize(layoutSize);
575 }
576 
LayoutToPosition(double position)577 void ListLayoutManager::LayoutToPosition(double position)
578 {
579     int32_t curHeadIndex = renderList_.GetCurrentMinIndex();
580     int32_t curTailIndex = renderList_.GetCurrentMaxIndex();
581     curTailIndex = std::max(curTailIndex, 0);
582     double curMainSize = GetItemPosition(curTailIndex);
583     LayoutParam innerLayout = MakeInnerLayoutParam(crossAxisAlign_);
584     auto itemChild = renderList_.GetChildByIndex(curTailIndex);
585     while (itemChild) {
586         itemChild->Layout(innerLayout);
587         if (enableChain_ && curTailIndex != curHeadIndex) {
588             curMainSize += chainInterval_ * HALF_ITEM_SIZE;
589         }
590         SetChildPosition(itemChild, curTailIndex, curMainSize);
591         itemPosition_[curTailIndex] = curMainSize;
592         curMainSize += renderList_.GetMainSize(itemChild->GetLayoutSize());
593         if (curMainSize >= position) {
594             break; // Already layout to the target position.
595         }
596         itemChild = renderList_.GetChildByIndex(++curTailIndex);
597         renderList_.RecycleHead(curHeadIndex++);
598         if (enableChain_ && itemChild) {
599             curMainSize += chainInterval_ * HALF_ITEM_SIZE;
600         }
601     }
602     UpdateMaxEndOffset(curTailIndex, curMainSize);
603     curMainSize = AdjustLayoutSize(maxEndOffset_);
604     Size layoutSize = renderList_.MakeValue<Size>(curMainSize, renderList_.GetCrossSize(viewPort_));
605     renderList_.SetLayoutSize(layoutSize);
606 }
607 
LayoutMore(double incDistance)608 void ListLayoutManager::LayoutMore(double incDistance)
609 {
610     int32_t curHeadIndex = renderList_.GetCurrentMinIndex();
611     // Use to load about one page size, so not need to recycle child.
612     int32_t curTailIndex = renderList_.GetCurrentMaxIndex();
613     curTailIndex = std::max(curTailIndex, 0);
614     double curMainSize = GetItemPosition(curTailIndex);
615     LayoutParam innerLayout = MakeInnerLayoutParam(crossAxisAlign_);
616     auto itemChild = renderList_.GetChildByIndex(curTailIndex);
617     double incMainSize = 0.0;
618     while (itemChild) {
619         itemChild->Layout(innerLayout);
620         if (enableChain_ && curTailIndex != curHeadIndex) {
621             curMainSize += chainInterval_ * HALF_ITEM_SIZE;
622         }
623         SetChildPosition(itemChild, curTailIndex, curMainSize);
624         itemPosition_[curTailIndex] = curMainSize;
625         curMainSize += renderList_.GetMainSize(itemChild->GetLayoutSize());
626         incMainSize += renderList_.GetMainSize(itemChild->GetLayoutSize());
627         if (incMainSize >= incDistance) {
628             break; // Already layout enough main size.
629         }
630         itemChild = renderList_.GetChildByIndex(++curTailIndex);
631         if (enableChain_ && itemChild) {
632             curMainSize += chainInterval_ * HALF_ITEM_SIZE;
633         }
634     }
635     UpdateMaxEndOffset(curTailIndex, curMainSize);
636     curMainSize = AdjustLayoutSize(maxEndOffset_);
637     Size layoutSize = renderList_.MakeValue<Size>(curMainSize, renderList_.GetCrossSize(viewPort_));
638     renderList_.SetLayoutSize(layoutSize);
639 }
640 
CalculateFocusIndexPosition()641 void ListLayoutManager::CalculateFocusIndexPosition()
642 {
643     double focusPosition = GetItemPosition(focusIndex_);
644     if (NearZero(focusPosition) && focusIndex_ != 0) {
645         renderList_.CalculateItemPosition(focusIndex_, ScrollType::SCROLL_INDEX);
646     }
647 }
648 
SetChildPosition(const RefPtr<RenderNode> & child,int32_t index,double mainSize)649 void ListLayoutManager::SetChildPosition(const RefPtr<RenderNode>& child, int32_t index, double mainSize)
650 {
651     double mainLen = renderList_.GetMainSize(viewPort_);
652     double crossLen = renderList_.GetCrossSize(viewPort_);
653     double mainAxis = mainSize;
654     double crossAxis = 0.0;
655     auto listItemChild = RenderListItem::GetRenderListItem(child);
656     FlexAlign selfAlign = listItemChild ? listItemChild->GetSelfAlign() : FlexAlign::AUTO;
657     FlexAlign align = selfAlign == FlexAlign::AUTO ? crossAxisAlign_ : selfAlign;
658     // second layout for self align.
659     if (crossAxisAlign_ == FlexAlign::STRETCH && selfAlign != FlexAlign::AUTO && selfAlign != FlexAlign::STRETCH) {
660         auto innerLayout = MakeInnerLayoutParam(align);
661         child->Layout(innerLayout);
662     }
663     if (crossAxisAlign_ != FlexAlign::STRETCH && selfAlign == FlexAlign::STRETCH) {
664         auto innerLayout = MakeInnerLayoutParam(align);
665         child->Layout(innerLayout);
666     }
667     if (enableChain_) {
668         mainAxis += GetChainDelta(index);
669     }
670     if (rightToLeft_) {
671         if (align == FlexAlign::FLEX_END) {
672             align = FlexAlign::FLEX_START;
673         } else if (align == FlexAlign::FLEX_START) {
674             align = FlexAlign::FLEX_END;
675         }
676     }
677     switch (align) {
678         case FlexAlign::FLEX_END:
679             crossAxis = crossLen - renderList_.GetCrossSize(child->GetLayoutSize());
680             break;
681         case FlexAlign::CENTER:
682             crossAxis = (crossLen - renderList_.GetCrossSize(child->GetLayoutSize())) / 2.0;
683             break;
684         case FlexAlign::STRETCH:
685         case FlexAlign::FLEX_START:
686         default:
687             break;
688     }
689 
690     if (isVertical_) {
691         if (IsColReverse()) {
692             mainAxis = mainLen - renderList_.GetMainSize(child->GetLayoutSize()) - mainAxis;
693         }
694         child->SetPosition(Offset(crossAxis, mainAxis) + position_);
695     } else {
696         if (IsRowReverse()) {
697             mainAxis = mainLen - renderList_.GetMainSize(child->GetLayoutSize()) - mainAxis;
698         }
699         child->SetPosition(Offset(mainAxis, crossAxis) + position_);
700     }
701     LOGD("Child[%{public}d]:%{public}lf %{public}lf %{public}s %{public}s", index, mainAxis, mainSize,
702         position_.ToString().c_str(), child->GetPosition().ToString().c_str());
703 }
704 
UpdateItemGroupAttr(RefPtr<RenderNode> & itemChild)705 void ListLayoutManager::UpdateItemGroupAttr(RefPtr<RenderNode>& itemChild)
706 {
707     auto renderListItem = RenderListItem::GetRenderListItem(itemChild);
708     if (renderListItem) {
709         auto renderListItemGroup = AceType::DynamicCast<RenderListItemGroup>(renderListItem);
710         if (renderListItemGroup) {
711             int32_t groupIndex = renderListItemGroup->GetIndex();
712             bool expand = GetExpandStatus(groupIndex);
713             renderListItemGroup->SetExpand(expand);
714             AddItemGroupExpand(groupIndex, expand);
715             if (expand) {
716                 int32_t groupFocusIndex = GetItemGroupFocusIndex(groupIndex);
717                 AddItemGroupFocusIndex(groupIndex, groupFocusIndex);
718             } else {
719                 AddItemGroupFocusIndex(groupIndex, 0);
720             }
721             renderListItemGroup->ChangeDirection(renderList_.GetDirection());
722             renderListItemGroup->SetRightToLeft(rightToLeft_);
723         }
724     }
725 }
726 
GetChainDelta(int32_t index) const727 double ListLayoutManager::GetChainDelta(int32_t index) const
728 {
729     if (!chainAdapter_) {
730         return 0.0;
731     }
732     double value = 0.0;
733     RefPtr<BilateralSpringNode> node;
734     int32_t controlIndex = renderList_.GetDragStartIndex();
735     int32_t baseIndex = controlIndex - chainAdapter_->GetControlIndex();
736     int32_t targetIndex = std::clamp(index - baseIndex, 0, CHAIN_ANIMATION_NODE_COUNT - 1);
737     node = AceType::DynamicCast<BilateralSpringNode>(chainAdapter_->GetNode(targetIndex));
738     if (node) {
739         value = node->GetValue();
740     }
741     LOGD("ChainDelta. controlIndex: %{public}d, index: %{public}d, value: %{public}.3lf", controlIndex, index, value);
742     return value;
743 }
744 
InitChainAnimation(int32_t nodeCount)745 void ListLayoutManager::InitChainAnimation(int32_t nodeCount)
746 {
747     auto context = renderList_.GetContext().Upgrade();
748     if (!context) {
749         LOGE("Init chain animation failed. context is null");
750         return;
751     }
752 
753     if (chainAdapter_ && chain_) {
754         return;
755     }
756     chainAdapter_ = AceType::MakeRefPtr<BilateralSpringAdapter>();
757     chain_ = AceType::MakeRefPtr<SimpleSpringChain>(chainAdapter_);
758     const auto& property = renderList_.GetChainProperty();
759     chain_->SetFrameDelta(property.FrameDelay());
760     if (property.StiffnessTransfer()) {
761         chain_->SetStiffnessTransfer(AceType::MakeRefPtr<ExpParamTransfer>(property.StiffnessCoefficient()));
762     } else {
763         chain_->SetStiffnessTransfer(AceType::MakeRefPtr<LinearParamTransfer>(property.StiffnessCoefficient()));
764     }
765     if (property.DampingTransfer()) {
766         chain_->SetDampingTransfer(AceType::MakeRefPtr<ExpParamTransfer>(property.DampingCoefficient()));
767     } else {
768         chain_->SetDampingTransfer(AceType::MakeRefPtr<LinearParamTransfer>(property.DampingCoefficient()));
769     }
770     chain_->SetControlDamping(property.ControlDamping());
771     chain_->SetControlStiffness(property.ControlStiffness());
772     chain_->SetDecoration(context->NormalizeToPx(renderList_.GetChainProperty().Interval()));
773     chain_->SetMinDecoration(context->NormalizeToPx(renderList_.GetChainProperty().MinInterval()));
774     chain_->SetMaxDecoration(context->NormalizeToPx(renderList_.GetChainProperty().MaxInterval()));
775     for (int32_t index = 0; index < nodeCount; index++) {
776         auto node = AceType::MakeRefPtr<BilateralSpringNode>(renderList_.GetContext(), index, 0.0);
777         WeakPtr<BilateralSpringNode> nodeWeak(node);
778         WeakPtr<SimpleSpringAdapter> adapterWeak(chainAdapter_);
779         node->AddUpdateListener(
780             [weak = AceType::WeakClaim(this), nodeWeak, adapterWeak](double value, double velocity) {
781                 auto manager = weak.Upgrade();
782                 auto node = nodeWeak.Upgrade();
783                 auto adapter = adapterWeak.Upgrade();
784                 if (!manager || !node || !adapter) {
785                     return;
786                 }
787                 if (node->GetIndex() == adapter->GetControlIndex()) {
788                     manager->controlValue_ = std::abs(value);
789                 }
790                 manager->renderList_.MarkNeedLayout();
791             });
792         chainAdapter_->AddNode(node);
793     }
794     chainAdapter_->NotifyControlIndexChange();
795 }
796 
GetItemAnimationValue(int32_t index) const797 double ListLayoutManager::GetItemAnimationValue(int32_t index) const
798 {
799     return enableChain_ ? GetChainDelta(index) : 0.0;
800 }
801 
UpdateItemPosition(void)802 void ListLayoutManager::UpdateItemPosition(void)
803 {
804     double position = 0;
805     bool positionVaild = false;
806     for (auto it = itemPosition_.begin(); it != itemPosition_.end(); it++) {
807         if (positionVaild) {
808             it->second = position;
809         }
810         auto itemChild = renderList_.FindChildByIndex(it->first);
811         if (!itemChild) {
812             positionVaild = false;
813             continue;
814         }
815         double childSize = renderList_.GetMainSize(itemChild->GetLayoutSize());
816         position = it->second + childSize;
817         positionVaild = true;
818     }
819 }
820 
UpdateMaxEndOffset(int32_t index,double curMainSize)821 void ListLayoutManager::UpdateMaxEndOffset(int32_t index, double curMainSize)
822 {
823     if ((index >= maxEndIndex_) || (index + 1 >= renderList_.GetMaxCount()) ||
824         LessNotEqual(maxEndOffset_, curMainSize)) {
825         maxEndIndex_ = index;
826         maxEndOffset_ = curMainSize;
827     }
828 }
829 
830 } // namespace OHOS::Ace