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