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