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