1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "core/components_v2/list/render_list.h"
17
18 #include "base/geometry/axis.h"
19 #include "base/log/ace_trace.h"
20 #include "base/log/log.h"
21 #include "base/memory/ace_type.h"
22 #include "base/utils/string_utils.h"
23 #include "base/utils/utils.h"
24 #include "core/animation/bilateral_spring_node.h"
25 #include "core/common/text_field_manager.h"
26 #include "core/components/box/drag_drop_event.h"
27 #include "core/components/scroll/render_scroll.h"
28 #include "core/components/scroll/render_single_child_scroll.h"
29 #include "core/components/scroll/scroll_spring_effect.h"
30 #include "core/components/scroll/scrollable.h"
31 #include "core/components/stack/stack_element.h"
32 #include "core/components_v2/list/list_component.h"
33 #include "core/components_v2/list/list_scroll_bar_controller.h"
34 #include "core/components_v2/list/render_list_item_group.h"
35 #include "core/event/axis_event.h"
36 #include "core/gestures/long_press_recognizer.h"
37 #include "core/gestures/pan_recognizer.h"
38 #include "core/gestures/sequenced_recognizer.h"
39
40 namespace OHOS::Ace::V2 {
41 namespace {
42
43 constexpr double VIEW_PORT_SCALE = 1.2;
44 constexpr int32_t CHAIN_ANIMATION_NODE_COUNT = 30;
45 constexpr int32_t DEFAULT_SOURCE = 3;
46 constexpr float SCROLL_MAX_TIME = 300.0f; // Scroll Animate max time 0.3 second
47 constexpr int32_t SCROLL_FROM_JUMP = 3;
48 constexpr int32_t DEFAULT_FINGERS = 1;
49 constexpr int32_t DEFAULT_DURATION = 200;
50 constexpr int32_t DEFAULT_DISTANCE = 0;
51
52 constexpr bool DIR_HORIZONTAL = false;
53 constexpr bool DIR_VERTICAL = true;
54 constexpr bool DIR_FORWARD = false;
55 constexpr bool DIR_REVERSE = true;
56 constexpr int32_t STEP_FORWARD = 1;
57 constexpr int32_t STEP_BACK = -1;
58 constexpr int32_t STEP_INVALID = 10;
59 constexpr int32_t CENTER_ALIGN_DIVIDER = 2;
60
61 // IsRightToLeft | IsListVertical | IsDirectionVertical | IsDirectionReverse
62 const std::map<bool, std::map<bool, std::map<bool, std::map<bool, int32_t>>>> DIRECTION_MAP = {
63 { false, // RTL is false
64 { { false, { { DIR_HORIZONTAL, { { DIR_FORWARD, STEP_FORWARD }, { DIR_REVERSE, STEP_BACK } } },
65 { DIR_VERTICAL, { { DIR_FORWARD, STEP_INVALID }, { DIR_REVERSE, STEP_INVALID } } } } },
66 { true, { { DIR_HORIZONTAL, { { DIR_FORWARD, STEP_INVALID }, { DIR_REVERSE, STEP_INVALID } } },
67 { DIR_VERTICAL, { { DIR_FORWARD, STEP_FORWARD }, { DIR_REVERSE, STEP_BACK } } } } } } },
68 { true, // RTL is true
69 { { false, { { DIR_HORIZONTAL, { { DIR_FORWARD, STEP_BACK }, { DIR_REVERSE, STEP_FORWARD } } },
70 { DIR_VERTICAL, { { DIR_FORWARD, STEP_INVALID }, { DIR_REVERSE, STEP_INVALID } } } } },
71 { true, { { DIR_HORIZONTAL, { { DIR_FORWARD, STEP_INVALID }, { DIR_REVERSE, STEP_INVALID } } },
72 { DIR_VERTICAL, { { DIR_FORWARD, STEP_BACK }, { DIR_REVERSE, STEP_FORWARD } } } } } } }
73 };
74 } // namespace
75
~RenderList()76 RenderList::~RenderList()
77 {
78 if (scrollBarProxy_) {
79 scrollBarProxy_->UnRegisterScrollableNode(AceType::WeakClaim(this));
80 }
81 }
82
Update(const RefPtr<Component> & component)83 void RenderList::Update(const RefPtr<Component>& component)
84 {
85 component_ = AceType::DynamicCast<ListComponent>(component);
86 ACE_DCHECK(component_);
87
88 isRightToLeft_ = component_->GetTextDirection() == TextDirection::RTL ? true : false;
89 RemoveAllItems();
90
91 auto axis = component_->GetDirection();
92 vertical_ = axis == Axis::VERTICAL;
93
94 InitScrollBar();
95
96 // Start index should be updated only for the first time
97 if (startIndex_ == INITIAL_CHILD_INDEX) {
98 initialIndex_ = static_cast<size_t>(component_->GetInitialIndex());
99 startIndex_ = initialIndex_ > 0 ? initialIndex_ : 0;
100 useEstimateCurrentOffset_ = true;
101 }
102 // maybe change startIndex
103 ApplyRestoreInfo();
104
105 const auto& divider = component_->GetItemDivider();
106 listSpace_ = component_->GetSpace();
107 cachedCount_ = static_cast<size_t>(component_->GetCachedCount());
108
109 LOGI("cached count: %{public}zu", cachedCount_);
110 spaceWidth_ = std::max(NormalizePercentToPx(component_->GetSpace(), vertical_),
111 divider ? NormalizePercentToPx(divider->strokeWidth, vertical_) : 0.0);
112 InitScrollable(axis);
113 // now only support spring
114 if (component_->GetEdgeEffect() == EdgeEffect::SPRING) {
115 if (!scrollEffect_ || scrollEffect_->GetEdgeEffect() != EdgeEffect::SPRING) {
116 scrollEffect_ = AceType::MakeRefPtr<ScrollSpringEffect>();
117 ResetEdgeEffect();
118 }
119 } else if (component_->GetEdgeEffect() == EdgeEffect::FADE) {
120 if (!scrollEffect_ || scrollEffect_->GetEdgeEffect() != EdgeEffect::FADE) {
121 scrollEffect_ = AceType::MakeRefPtr<ScrollFadeEffect>();
122 ResetEdgeEffect();
123 }
124 } else {
125 scrollEffect_ = nullptr;
126 }
127
128 auto controller = component_->GetScrollController();
129 if (controller) {
130 controller->SetScrollNode(AceType::WeakClaim(this));
131 }
132 if (!animator_) {
133 animator_ = CREATE_ANIMATOR(GetContext());
134 }
135
136 // chainAnimation
137 if (chainAnimation_ != component_->GetChainAnimation()) {
138 chainAnimation_ = component_->GetChainAnimation();
139 if (chainAnimation_) {
140 InitChainAnimation(CHAIN_ANIMATION_NODE_COUNT);
141 overSpringProperty_ = SpringChainProperty::GetDefaultOverSpringProperty();
142 } else {
143 overSpringProperty_ = nullptr;
144 chain_ = nullptr;
145 chainAdapter_ = nullptr;
146 }
147 }
148
149 if (chainAnimation_) {
150 // add chain interval length
151 spaceWidth_ += NormalizeToPx(chainProperty_.Interval());
152 }
153
154 scrollBarProxy_ = component_->GetScrollBarProxy();
155 InitScrollBarProxy();
156
157 onItemDragStart_ = component_->GetOnItemDragStartId();
158 onItemDragEnter_ = component_->GetOnItemDragEnterId();
159 onItemDragMove_ = component_->GetOnItemDragMoveId();
160 onItemDragLeave_ = component_->GetOnItemDragLeaveId();
161 onItemDrop_ = component_->GetOnItemDropId();
162
163 if (onItemDragStart_) {
164 CreateDragDropRecognizer();
165 }
166 FindRefreshParent(AceType::WeakClaim(this));
167
168 isMultiSelectable_ = component_->GetMultiSelectable();
169 hasHeight_ = component_->GetHasHeight();
170 hasWidth_ = component_->GetHasWidth();
171 isLaneList_ = (component_->GetLanes() != -1) || (component_->GetLaneConstrain() != std::nullopt);
172 sticky_ = component_->GetSticky();
173 if (!vertical_ || scrollBar_->GetDisplayMode() != DisplayMode::OFF || chainAnimation_ ||
174 sticky_ != StickyStyle::NONE || isLaneList_ || !scrollable_->Available()) {
175 drivenRender_ = false;
176 } else {
177 drivenRender_ = true;
178 }
179 MarkNeedLayout();
180 }
181
InitScrollable(Axis axis)182 void RenderList::InitScrollable(Axis axis)
183 {
184 if (scrollable_) {
185 scrollable_->SetAxis(axis);
186 scrollable_->SetOnScrollBegin(component_->GetOnScrollBegin());
187 return;
188 }
189
190 auto callback = [weak = AceType::WeakClaim(this)](double offset, int32_t source) {
191 auto renderList = weak.Upgrade();
192
193 if (!renderList) {
194 return false;
195 }
196
197 if (source == SCROLL_FROM_START) {
198 renderList->ProcessDragStart(offset);
199 return true;
200 }
201
202 Offset delta;
203 if (renderList->vertical_) {
204 delta.SetX(0.0);
205 delta.SetY(offset);
206 } else {
207 delta.SetX(offset);
208 delta.SetY(0.0);
209 }
210 renderList->AdjustOffset(delta, source);
211 if ((source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_ANIMATION_SPRING) &&
212 renderList->currentOffset_ >= 0.0) {
213 if (renderList->scrollable_->RelatedScrollEventDoing(Offset(0.0, -offset))) {
214 return false;
215 }
216 }
217 renderList->ProcessDragUpdate(renderList->GetMainAxis(delta));
218
219 // Stop animator of scroll bar.
220 auto scrollBarProxy = renderList->scrollBarProxy_;
221 if (scrollBarProxy) {
222 scrollBarProxy->StopScrollBarAnimator();
223 }
224 return renderList->UpdateScrollPosition(renderList->GetMainAxis(delta), source);
225 };
226 scrollable_ = AceType::MakeRefPtr<Scrollable>(callback, axis);
227 scrollable_->SetNotifyScrollOverCallBack([weak = AceType::WeakClaim(this)](double velocity) {
228 auto list = weak.Upgrade();
229 if (!list) {
230 return;
231 }
232 list->ProcessScrollOverCallback(velocity);
233 });
234 scrollable_->SetScrollEndCallback([weak = AceType::WeakClaim(this)]() {
235 auto list = weak.Upgrade();
236 if (!list) {
237 LOGW("render list Upgrade fail in scroll end callback");
238 return;
239 }
240 auto proxy = list->scrollBarProxy_;
241 if (proxy) {
242 proxy->StartScrollBarAnimator();
243 }
244 auto scrollBar = list->scrollBar_;
245 if (scrollBar) {
246 scrollBar->HandleScrollBarEnd();
247 }
248 list->listEventFlags_[ListEvents::SCROLL_STOP] = true;
249 list->HandleListEvent();
250 });
251 InitializeScrollable(scrollable_);
252 scrollable_->SetOnScrollBegin(component_->GetOnScrollBegin());
253 if (vertical_) {
254 scrollable_->InitRelatedParent(GetParent());
255 }
256 scrollable_->Initialize(context_);
257 scrollable_->SetNodeId(GetAccessibilityNodeId());
258 }
259
InitScrollBarProxy()260 void RenderList::InitScrollBarProxy()
261 {
262 if (!scrollBarProxy_) {
263 return;
264 }
265 auto callback = [weak = AceType::WeakClaim(this)](double value, int32_t source) {
266 auto renderList = weak.Upgrade();
267 if (!renderList) {
268 LOGE("render list is released");
269 return false;
270 }
271 return renderList->UpdateScrollPosition(value, source);
272 };
273 scrollBarProxy_->UnRegisterScrollableNode(AceType::WeakClaim(this));
274 scrollBarProxy_->RegisterScrollableNode({ AceType::WeakClaim(this), callback });
275 }
276
IsReachStart()277 bool RenderList::IsReachStart()
278 {
279 bool scrollUpToReachStart = GreatNotEqual(prevOffset_, 0.0) && LessOrEqual(currentOffset_, 0.0);
280 bool scrollDownToReachStart = LessNotEqual(prevOffset_, 0.0) && GreatOrEqual(currentOffset_, 0.0);
281 return scrollUpToReachStart || scrollDownToReachStart;
282 }
283
InitScrollBar()284 void RenderList::InitScrollBar()
285 {
286 if (!component_) {
287 LOGE("InitScrollBar failed, component_ is null.");
288 return;
289 }
290 if (scrollBar_) {
291 scrollBar_->SetDisplayMode(component_->GetScrollBar());
292 scrollBar_->Reset();
293 return;
294 }
295 const RefPtr<ScrollBarTheme> theme = GetTheme<ScrollBarTheme>();
296 if (!theme) {
297 return;
298 }
299
300 scrollBar_ = AceType::MakeRefPtr<ScrollBar>(component_->GetScrollBar(), theme->GetShapeMode());
301 RefPtr<ListScrollBarController> controller = AceType::MakeRefPtr<ListScrollBarController>();
302 scrollBar_->SetScrollBarController(controller);
303
304 // set the scroll bar style
305 scrollBar_->SetReservedHeight(theme->GetReservedHeight());
306 scrollBar_->SetMinHeight(theme->GetMinHeight());
307 scrollBar_->SetMinDynamicHeight(theme->GetMinDynamicHeight());
308 scrollBar_->SetForegroundColor(theme->GetForegroundColor());
309 scrollBar_->SetBackgroundColor(theme->GetBackgroundColor());
310 scrollBar_->SetPadding(theme->GetPadding());
311 scrollBar_->SetScrollable(true);
312 scrollBar_->SetInactiveWidth(theme->GetNormalWidth());
313 scrollBar_->SetNormalWidth(theme->GetNormalWidth());
314 scrollBar_->SetActiveWidth(theme->GetActiveWidth());
315 scrollBar_->SetTouchWidth(theme->GetTouchWidth());
316 if (!vertical_) {
317 scrollBar_->SetPositionMode(PositionMode::BOTTOM);
318 } else {
319 if (isRightToLeft_) {
320 scrollBar_->SetPositionMode(PositionMode::LEFT);
321 }
322 }
323 scrollBar_->InitScrollBar(AceType::WeakClaim(this), GetContext());
324 SetScrollBarCallback();
325 }
326
SetScrollBarCallback()327 void RenderList::SetScrollBarCallback()
328 {
329 if (!scrollBar_ || !scrollBar_->NeedScrollBar()) {
330 return;
331 }
332 auto&& scrollCallback = [weakList = AceType::WeakClaim(this)](double value, int32_t source) {
333 auto list = weakList.Upgrade();
334 if (!list) {
335 LOGE("render list is released");
336 return false;
337 }
338 return list->UpdateScrollPosition(value, source);
339 };
340 auto&& barEndCallback = [weakList = AceType::WeakClaim(this)](int32_t value) {
341 auto list = weakList.Upgrade();
342 if (!list) {
343 LOGE("render list is released.");
344 return;
345 }
346 list->scrollBarOpacity_ = value;
347 list->MarkNeedRender();
348 };
349 auto&& scrollEndCallback = []() {
350 // nothing to do
351 };
352 scrollBar_->SetCallBack(scrollCallback, barEndCallback, scrollEndCallback);
353 }
354
GetLaneLengthInPx(const Dimension & length)355 double RenderList::GetLaneLengthInPx(const Dimension& length)
356 {
357 if (length.Unit() == DimensionUnit::PERCENT) {
358 return NormalizePercentToPx(length, !vertical_, true);
359 }
360 return NormalizeToPx(length);
361 }
362
ModifyLaneLength(const std::optional<std::pair<Dimension,Dimension>> & laneConstrain)363 void RenderList::ModifyLaneLength(const std::optional<std::pair<Dimension, Dimension>>& laneConstrain)
364 {
365 minLaneLength_ = GetLaneLengthInPx(laneConstrain.value().first);
366 maxLaneLength_ = GetLaneLengthInPx(laneConstrain.value().second);
367 if (LessOrEqual(maxLaneLength_, 0.0)) {
368 maxLaneLength_ = GetCrossSize(GetLayoutSize());
369 }
370 if (LessOrEqual(minLaneLength_, 0.0)) {
371 minLaneLength_ = std::min(GetCrossSize(GetLayoutSize()), maxLaneLength_);
372 }
373 if (GreatNotEqual(minLaneLength_, maxLaneLength_)) {
374 LOGI("minLaneLength: %{public}f is greater than maxLaneLength: %{public}f, assign minLaneLength to"
375 " maxLaneLength",
376 minLaneLength_, maxLaneLength_);
377 maxLaneLength_ = minLaneLength_;
378 }
379 }
380
CalculateLanes()381 void RenderList::CalculateLanes()
382 {
383 auto lanes = component_->GetLanes();
384 do {
385 auto laneConstrain = component_->GetLaneConstrain();
386 // Case 1: lane length constrain is not set
387 // 1.1: use [lanes_] set by user if [lanes_] is set
388 // 1.2: set [lanes_] to 1 if [lanes_] is not set
389 if (!laneConstrain) {
390 if (lanes <= 0) {
391 lanes = 1;
392 }
393 maxLaneLength_ = GetCrossSize(GetLayoutParam().GetMaxSize()) / lanes;
394 minLaneLength_ = GetCrossSize(GetLayoutParam().GetMinSize()) / lanes;
395 break;
396 }
397 // Case 2: lane length constrain is set --> need to calculate [lanes_] according to contraint.
398 // We agreed on such rules (assuming we have a vertical list here):
399 // rule 1: [minLaneLength_] has a higher priority than [maxLaneLength_] when decide [lanes_], for e.g.,
400 // if [minLaneLength_] is 40, [maxLaneLength_] is 60, list's width is 120,
401 // the [lanes_] is 3 rather than 2.
402 // rule 2: after [lanes_] is determined by rule 1, the width of lane will be as large as it can be, for e.g.,
403 // if [minLaneLength_] is 40, [maxLaneLength_] is 60, list's width is 132, the [lanes_] is 3
404 // according to rule 1, then the width of lane will be 132 / 3 = 44 rather than 40,
405 // its [minLaneLength_].
406
407 // set layout size temporarily to calculate percent unit of constrain
408 SetLayoutSize(GetLayoutParam().GetMaxSize());
409 ModifyLaneLength(laneConstrain);
410
411 // if minLaneLength is 40, maxLaneLength is 60
412 // when list's width is 120, lanes_ = 3
413 // when list's width is 80, lanes_ = 2
414 // when list's width is 70, lanes_ = 1
415 auto maxCrossSize = GetCrossSize(GetLayoutSize());
416 double maxLanes = maxCrossSize / minLaneLength_;
417 double minLanes = maxCrossSize / maxLaneLength_;
418 // let's considerate scenarios when maxCrossSize > 0
419 // now it's guaranteed that [minLaneLength_] <= [maxLaneLength_], i.e., maxLanes >= minLanes > 0
420 // there are 3 scenarios:
421 // 1. 1 > maxLanes >= minLanes > 0
422 // 2. maxLanes >= 1 >= minLanes > 0
423 // 3. maxLanes >= minLanes > 1
424
425 // 1. 1 > maxLanes >= minLanes > 0 ---> maxCrossSize < minLaneLength_ =< maxLaneLength
426 if (GreatNotEqual(1, maxLanes) && GreatOrEqual(maxLanes, minLanes)) {
427 lanes = 1;
428 minLaneLength_ = maxCrossSize;
429 maxLaneLength_ = maxCrossSize;
430 break;
431 }
432 // 2. maxLanes >= 1 >= minLanes > 0 ---> minLaneLength_ = maxCrossSize < maxLaneLength
433 if (GreatOrEqual(maxLanes, 1) && LessOrEqual(minLanes, 1)) {
434 lanes = std::floor(maxLanes);
435 maxLaneLength_ = maxCrossSize;
436 break;
437 }
438 // 3. maxLanes >= minLanes > 1 ---> minLaneLength_ <= maxLaneLength < maxCrossSize
439 if (GreatOrEqual(maxLanes, minLanes) && GreatNotEqual(minLanes, 1)) {
440 lanes = std::floor(maxLanes);
441 break;
442 }
443 lanes = 1;
444 LOGE("unexpected situation, set lanes to 1, maxLanes: %{public}f, minLanes: %{public}f, minLaneLength_: "
445 "%{public}f, maxLaneLength_: %{public}f",
446 maxLanes, minLanes, minLaneLength_, maxLaneLength_);
447 } while (0);
448 if (lanes != lanes_) { // if lanes changes, adjust startIndex_
449 lanes_ = lanes;
450 if (lanes > 1) {
451 size_t startIndex = startIndex_ - GetItemRelativeIndex(startIndex_) % lanes;
452 if (startIndex_ != startIndex) {
453 RemoveAllItems();
454 }
455 }
456 }
457 }
458
RequestNewItemsAtEndForLaneList(double & curMainPos,double mainSize)459 void RenderList::RequestNewItemsAtEndForLaneList(double& curMainPos, double mainSize)
460 {
461 int newItemCntInLine = 0;
462 double lineMainSize = 0;
463 for (size_t newIndex = startIndex_ + items_.size();; ++newIndex) {
464 bool breakWhenRequestNewItem = false;
465 RefPtr<RenderListItemGroup> itemGroup;
466 do {
467 if (GreatOrEqual(curMainPos, endMainPos_)) {
468 breakWhenRequestNewItem = true;
469 break;
470 }
471 auto child = RequestAndLayoutNewItem(newIndex, curMainPos);
472 if (!child) {
473 startIndex_ = std::min(startIndex_, TotalCount());
474 breakWhenRequestNewItem = true;
475 break;
476 }
477 if (GreatOrEqual(curMainPos, mainSize)) {
478 ++endCachedCount_;
479 }
480 itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
481 if (itemGroup) {
482 break;
483 }
484 lineMainSize = std::max(lineMainSize, GetMainSize(child->GetLayoutSize()));
485 ++newItemCntInLine;
486 } while (0);
487 bool singleLaneDoneAddItem = (lanes_ == 1) && !breakWhenRequestNewItem;
488 bool multiLaneDoneSupplyOneLine = (lanes_ > 1) && (newItemCntInLine == lanes_);
489 bool multiLaneStartSupplyLine = (itemGroup || breakWhenRequestNewItem) && (newItemCntInLine >= 1);
490 if (singleLaneDoneAddItem || multiLaneDoneSupplyOneLine || multiLaneStartSupplyLine) {
491 curMainPos += lineMainSize + spaceWidth_;
492 newItemCntInLine = 0;
493 lineMainSize = 0;
494 }
495 if (itemGroup) {
496 double size = GetMainSize(itemGroup->GetLayoutSize());
497 curMainPos += size + spaceWidth_;
498 }
499 if (breakWhenRequestNewItem) {
500 break;
501 }
502 }
503 }
504
RequestNewItemsAtEnd(double & curMainPos,double mainSize)505 void RenderList::RequestNewItemsAtEnd(double& curMainPos, double mainSize)
506 {
507 for (size_t newIndex = startIndex_ + items_.size();; ++newIndex) {
508 if (cachedCount_ != 0) {
509 if (endCachedCount_ >= cachedCount_ && GreatOrEqual(curMainPos, mainSize)) {
510 break;
511 }
512 } else {
513 if (GreatOrEqual(curMainPos, endMainPos_)) {
514 break;
515 }
516 }
517 auto child = RequestAndLayoutNewItem(newIndex, curMainPos);
518 if (!child) {
519 startIndex_ = std::min(startIndex_, TotalCount());
520 break;
521 }
522 if (GreatOrEqual(curMainPos, mainSize)) {
523 auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
524 if (itemGroup) {
525 endCachedCount_ += itemGroup->GetCurrEndCacheCount();
526 } else {
527 ++endCachedCount_;
528 }
529 }
530 curMainPos += GetMainSize(child->GetLayoutSize()) + spaceWidth_;
531 }
532
533 if (selectedItem_ && selectedItemIndex_ < startIndex_) {
534 curMainPos += GetMainSize(selectedItem_->GetLayoutSize()) + spaceWidth_;
535 }
536 }
537
RequestNewItemsAtStartForLaneList()538 void RenderList::RequestNewItemsAtStartForLaneList()
539 {
540 int newItemCntInLine = 0;
541 double lineMainSize = 0;
542 for (; startIndex_ > 0; --startIndex_) {
543 bool breakWhenRequestNewItem = false;
544 RefPtr<RenderListItemGroup> itemGroup;
545 do {
546 if (LessOrEqual(currentOffset_, startMainPos_)) {
547 breakWhenRequestNewItem = true;
548 break;
549 }
550 auto child = RequestAndLayoutNewItem(startIndex_ - 1, currentOffset_ - spaceWidth_, false);
551 if (!child) {
552 breakWhenRequestNewItem = true;
553 break;
554 }
555 if (selectedItemIndex_ == startIndex_) {
556 continue;
557 }
558 if (LessOrEqual(currentOffset_, 0.0)) {
559 ++startCachedCount_;
560 }
561 itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
562 if (itemGroup) {
563 break;
564 }
565 lineMainSize = std::max(lineMainSize, GetMainSize(child->GetLayoutSize()));
566 ++newItemCntInLine;
567 } while (0);
568 bool singleLaneDoneAddItem = (lanes_ == 1) && !breakWhenRequestNewItem;
569 bool isLaneStart = !itemGroup && (lanes_ > 1) && (GetItemRelativeIndex(startIndex_ - 1) % lanes_ == 0);
570 bool multiLaneSupplyLine = (itemGroup || breakWhenRequestNewItem || isLaneStart) && (newItemCntInLine >= 1);
571 if (singleLaneDoneAddItem || multiLaneSupplyLine) {
572 currentOffset_ -= lineMainSize + spaceWidth_;
573 startIndexOffset_ -= lineMainSize + spaceWidth_;
574 newItemCntInLine = 0;
575 lineMainSize = 0;
576 }
577 if (itemGroup) {
578 double size = GetMainSize(itemGroup->GetLayoutSize());
579 currentOffset_ -= size + spaceWidth_;
580 startIndexOffset_ -= size + spaceWidth_;
581 }
582 if (breakWhenRequestNewItem) {
583 break;
584 }
585 }
586 }
587
RequestNewItemsAtStart()588 void RenderList::RequestNewItemsAtStart()
589 {
590 for (; startIndex_ > 0; --startIndex_) {
591 if (cachedCount_ != 0) {
592 if (startCachedCount_ >= cachedCount_ && LessOrEqual(currentOffset_, 0.0)) {
593 break;
594 }
595 } else {
596 if (LessOrEqual(currentOffset_, startMainPos_)) {
597 break;
598 }
599 }
600 auto child = RequestAndLayoutNewItem(startIndex_ - 1, currentOffset_ - spaceWidth_, false);
601 if (!child) {
602 break;
603 }
604 if (selectedItemIndex_ == startIndex_) {
605 continue;
606 }
607 if (LessOrEqual(currentOffset_, 0.0)) {
608 auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
609 if (itemGroup) {
610 startCachedCount_ += itemGroup->GetCurrStartCacheCount();
611 } else {
612 ++startCachedCount_;
613 }
614 }
615 currentOffset_ -= GetMainSize(child->GetLayoutSize()) + spaceWidth_;
616 startIndexOffset_ -= GetMainSize(child->GetLayoutSize()) + spaceWidth_;
617 }
618 }
619
PerformLayout()620 void RenderList::PerformLayout()
621 {
622 UpdateAccessibilityAttr();
623 // Check validation of layout size
624 const double mainSize = ApplyLayoutParam();
625 if (NearZero(mainSize)) {
626 LOGW("Cannot layout using invalid view port");
627 return;
628 }
629 if (isLaneList_) {
630 CalculateLanes();
631 }
632
633 double prevTotalOffset = startIndexOffset_ - prevOffset_;
634 double curMainPos = 0.0;
635 if (isLaneList_) {
636 curMainPos = LayoutOrRecycleCurrentItemsForLaneList(mainSize);
637 } else {
638 curMainPos = LayoutOrRecycleCurrentItems(mainSize);
639 }
640
641 // Try to request new items at end if needed
642 if (isLaneList_) {
643 RequestNewItemsAtEndForLaneList(curMainPos, mainSize);
644 } else {
645 RequestNewItemsAtEnd(curMainPos, mainSize);
646 }
647
648 if (selectedItem_ && selectedItemIndex_ < startIndex_) {
649 curMainPos += GetMainSize(selectedItem_->GetLayoutSize()) + spaceWidth_;
650 }
651
652 if (startIndex_ + items_.size() >= TotalCount()) {
653 curMainPos -= spaceWidth_;
654 }
655
656 // Check if reach the end of list
657 reachEnd_ = LessOrEqual(curMainPos, mainSize);
658 bool noEdgeEffect = (scrollable_ && scrollable_->IsAnimationNotRunning()) ||
659 !(scrollEffect_ && scrollEffect_->IsSpringEffect()) || autoScrollingForItemMove_;
660 if (noEdgeEffect && reachEnd_) {
661 // Adjust end of list to match the end of layout
662 if (LessNotEqual(curMainPos, mainSize)) {
663 AdjustForReachEnd(mainSize, curMainPos);
664 }
665 curMainPos = mainSize;
666 }
667
668 // Try to request new items at start if needed
669 if (isLaneList_) {
670 RequestNewItemsAtStartForLaneList();
671 } else {
672 RequestNewItemsAtStart();
673 }
674
675 // Check if reach the start of list
676 reachStart_ = GreatOrEqual(currentOffset_, 0.0);
677 if (noEdgeEffect && reachStart_) {
678 if (GreatOrEqual(currentOffset_, 0.0)) {
679 AdjustForReachStart(curMainPos);
680 }
681 currentOffset_ = 0;
682 if (isLaneList_) {
683 RequestNewItemsAtEndForLaneList(curMainPos, mainSize);
684 } else {
685 RequestNewItemsAtEnd(curMainPos, mainSize);
686 }
687 }
688
689 if (IsReachStart()) {
690 listEventFlags_[ListEvents::REACH_START] = true;
691 }
692 bool scrollDownToReachEnd = LessNotEqual(prevMainPos_, mainSize) && GreatOrEqual(curMainPos, mainSize);
693 bool scrollUpToReachEnd = GreatNotEqual(prevMainPos_, mainSize) && LessOrEqual(curMainPos, mainSize);
694 // verify layout size to avoid trigger reach_end event at first [PerformLayout] when layout size is zero
695 if ((scrollDownToReachEnd || scrollUpToReachEnd) && GetLayoutSize().IsValid()) {
696 listEventFlags_[ListEvents::REACH_END] = true;
697 }
698 if (!fixedMainSize_) {
699 fixedMainSize_ = !(reachStart_ && reachEnd_);
700 }
701 // Check if disable or enable scrollable
702 CalculateMainScrollExtent(curMainPos, mainSize);
703
704 // Set position for each child
705 Size layoutSize;
706 if (isLaneList_) {
707 layoutSize = SetItemsPositionForLaneList(mainSize);
708 } else {
709 layoutSize = SetItemsPosition(mainSize);
710 }
711
712 // Set layout size of list component itself
713 if ((hasHeight_ && vertical_) || (hasWidth_ && !vertical_)) {
714 SetLayoutSize(GetLayoutParam().GetMaxSize());
715 } else {
716 SetLayoutSize(GetLayoutParam().Constrain(layoutSize));
717 }
718
719 // Clear auto scrolling flags
720 autoScrollingForItemMove_ = false;
721 double currentTotalOffset = startIndexOffset_ - currentOffset_;
722
723 if (!NearEqual(currentTotalOffset, prevTotalOffset)) {
724 SetPaintState(true);
725 } else {
726 SetPaintState(false);
727 }
728
729 if (!NearEqual(currentTotalOffset, prevTotalOffset)) {
730 auto offset = Dimension((currentTotalOffset - prevTotalOffset) / dipScale_, DimensionUnit::VP);
731 if (scrollable_ && scrollable_->Idle()) {
732 ResumeEventCallback(component_, &ListComponent::GetOnScroll, offset, ScrollState::IDLE);
733 } else {
734 ResumeEventCallback(component_, &ListComponent::GetOnScroll, offset, scrollState_);
735 }
736 }
737
738 realMainSize_ = curMainPos - currentOffset_;
739 HandleListEvent();
740 prevOffset_ = currentOffset_;
741 prevMainPos_ = curMainPos;
742 UpdateAccessibilityScrollAttr();
743 UpdateAccessibilityVisible();
744 }
745
746 #define CASE_OF_LIST_EVENT_WITH_NO_PARAM(eventNumber, callback) \
747 case ListEvents::eventNumber: \
748 if (event.second) { \
749 ResumeEventCallback(component_, &ListComponent::callback); \
750 LOGD("list event %{public}s triggered.", #eventNumber); \
751 event.second = false; \
752 } \
753 break;
754
HandleListEvent()755 void RenderList::HandleListEvent()
756 {
757 for (auto& event : listEventFlags_) {
758 switch (event.first) {
759 CASE_OF_LIST_EVENT_WITH_NO_PARAM(SCROLL_STOP, GetOnScrollStop);
760 CASE_OF_LIST_EVENT_WITH_NO_PARAM(REACH_START, GetOnReachStart);
761 CASE_OF_LIST_EVENT_WITH_NO_PARAM(REACH_END, GetOnReachEnd);
762 default:
763 LOGW("This event does not handle in here, please check. event number: %{public}d", event.first);
764 break;
765 }
766 }
767 }
768
CalculateLaneCrossOffset(double crossSize,double childCrossSize)769 double RenderList::CalculateLaneCrossOffset(double crossSize, double childCrossSize)
770 {
771 double delta = crossSize - childCrossSize;
772 // TODO: modify in rtl scenario
773 switch (component_->GetAlignListItemAlign()) {
774 case ListItemAlign::START:
775 return 0.0;
776 case ListItemAlign::CENTER:
777 return delta / CENTER_ALIGN_DIVIDER;
778 case ListItemAlign::END:
779 return delta;
780 default:
781 LOGW("Invalid ListItemAlign: %{public}d", component_->GetAlignListItemAlign());
782 return 0.0;
783 }
784 }
785
SetItemsPositionForLaneList(double mainSize)786 Size RenderList::SetItemsPositionForLaneList(double mainSize)
787 {
788 double crossSize = fixedCrossSize_ ? GetCrossSize(GetLayoutParam().GetMaxSize()) : 0.0;
789 if (items_.empty()) {
790 return MakeValue<Size>(fixedMainSize_ ? mainSize : 0.0, crossSize);
791 }
792
793 double curMainPos = currentOffset_;
794 size_t index = startIndex_;
795 size_t newStickyIndex = 0;
796 RefPtr<RenderListItem> newStickyItem;
797 RefPtr<RenderListItem> nextStickyItem;
798 double nextStickyMainAxis = Size::INFINITE_SIZE;
799 size_t firstIdx = INITIAL_CHILD_INDEX;
800 size_t lastIdx = 0;
801 double selectedItemMainSize = selectedItem_ ? GetMainSize(selectedItem_->GetLayoutSize()) : 0.0;
802
803 for (auto iter = items_.begin(); iter != items_.end();) {
804 RefPtr<RenderListItem> child;
805 double childMainSize = 0.0;
806 double childCrossSize = 0.0;
807 std::vector<RefPtr<RenderListItem>> itemSet;
808 // start set child position in a row
809 for (int32_t rowIndex = 0; rowIndex < lanes_; rowIndex++) {
810 child = *iter;
811 auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
812 if (itemGroup && rowIndex > 0) {
813 break;
814 }
815 double singleChildSize = GetMainSize(child->GetLayoutSize());
816 childCrossSize += GetCrossSize(child->GetLayoutSize());
817 childMainSize = std::max(childMainSize, singleChildSize); // get max item height in a row as row height
818 // store items in a row, set position of each item after done getting [childMainSize]
819 itemSet.emplace_back(child);
820 if ((++iter) == items_.end() || itemGroup) {
821 break;
822 }
823 }
824 if (!fixedCrossSize_) {
825 crossSize = std::max(crossSize, childCrossSize);
826 }
827 auto offset = MakeValue<Offset>(curMainPos, 0.0);
828 if (chainAnimation_) {
829 double chainDelta = GetChainDelta(index);
830 auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
831 if (itemGroup) {
832 itemGroup->SetChainOffset(-chainDelta);
833 }
834 offset += MakeValue<Offset>(-chainDelta, 0.0);
835 }
836 // set item position for one row
837 for (size_t i = 0; i < itemSet.size(); i++) {
838 auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(itemSet[i]);
839 double itemCrossSize = itemGroup ? crossSize : crossSize / lanes_;
840 auto offsetCross = CalculateLaneCrossOffset(itemCrossSize, GetCrossSize(itemSet[i]->GetLayoutSize()));
841 auto position = offset + MakeValue<Offset>(0.0, itemCrossSize * i + offsetCross);
842 if (isRightToLeft_) {
843 if (IsVertical()) {
844 position = MakeValue<Offset>(
845 GetMainAxis(position), crossSize - childCrossSize / itemSet.size() - GetCrossAxis(position));
846 } else {
847 position =
848 MakeValue<Offset>(mainSize - childMainSize - GetMainAxis(position), GetCrossAxis(position));
849 }
850 }
851 SetChildPosition(itemSet[i], position);
852 }
853
854 if (lanes_ == 1) {
855 if (selectedItem_) {
856 double range = std::min(selectedItemMainSize, childMainSize) / 2.0;
857 bool beforeSelectedItem = index <= selectedItemIndex_;
858 if (beforeSelectedItem && targetIndex_ == index) {
859 targetMainAxis_ = curMainPos;
860 curMainPos += selectedItemMainSize + spaceWidth_;
861 }
862
863 if (movingForward_) {
864 double axis = selectedItemMainAxis_;
865 if (GreatOrEqual(axis, curMainPos) && LessNotEqual(axis, curMainPos + range)) {
866 targetIndex_ = beforeSelectedItem ? index : index - 1;
867 targetMainAxis_ = curMainPos;
868 curMainPos += selectedItemMainSize + spaceWidth_;
869 }
870 } else {
871 double axis = selectedItemMainAxis_ + selectedItemMainSize;
872 double limit = curMainPos + childMainSize;
873 if (GreatNotEqual(axis, limit - range) && LessOrEqual(axis, limit)) {
874 targetIndex_ = beforeSelectedItem ? index + 1 : index;
875 targetMainAxis_ = curMainPos;
876 curMainPos -= selectedItemMainSize + spaceWidth_;
877 }
878 }
879 }
880
881 // Disable sticky mode while expand all items
882 if (fixedMainSize_ && itemSet[0]->GetSticky() != StickyMode::NONE) {
883 if (LessOrEqual(curMainPos, 0.0)) {
884 newStickyItem = itemSet[0];
885 newStickyIndex = index;
886 } else if (!nextStickyItem) {
887 nextStickyItem = itemSet[0];
888 nextStickyMainAxis = curMainPos;
889 }
890 }
891 }
892 itemSet.clear();
893
894 childMainSize += spaceWidth_;
895 if (LessNotEqual(curMainPos, mainSize) && GreatNotEqual(curMainPos + childMainSize, 0.0)) {
896 firstIdx = std::min(firstIdx, index);
897 lastIdx = std::max(lastIdx, index);
898 }
899
900 if (child != selectedItem_) {
901 curMainPos += childMainSize;
902 }
903
904 if (selectedItem_ && index > selectedItemIndex_ && targetIndex_ == index) {
905 targetMainAxis_ = curMainPos;
906 curMainPos += selectedItemMainSize + spaceWidth_;
907 }
908
909 ++index;
910 }
911 if (firstIdx != firstDisplayIndex_ || lastIdx != lastDisplayIndex_) {
912 firstDisplayIndex_ = firstIdx;
913 lastDisplayIndex_ = lastIdx;
914 ResumeEventCallback(component_, &ListComponent::GetOnScrollIndex, static_cast<int32_t>(firstDisplayIndex_),
915 static_cast<int32_t>(lastDisplayIndex_), static_cast<int32_t>(midDisplayIndex_));
916 }
917
918 // Disable sticky mode while expand all items
919 if (!fixedMainSize_) {
920 return MakeValue<Size>(curMainPos - spaceWidth_, crossSize);
921 }
922
923 if (lanes_ == 1) {
924 UpdateStickyListItem(newStickyItem, newStickyIndex, nextStickyItem);
925 if (currentStickyItem_) {
926 const auto& stickyItemLayoutSize = currentStickyItem_->GetLayoutSize();
927 const double mainStickySize = GetMainSize(stickyItemLayoutSize) + spaceWidth_;
928 if (nextStickyItem && LessNotEqual(nextStickyMainAxis, mainStickySize)) {
929 auto position = MakeValue<Offset>(nextStickyMainAxis - mainStickySize, 0.0);
930 if (isRightToLeft_) {
931 if (IsVertical()) {
932 position = MakeValue<Offset>(GetMainAxis(position),
933 crossSize - GetCrossSize(stickyItemLayoutSize) - GetCrossAxis(position));
934 } else {
935 position = MakeValue<Offset>(
936 mainSize - mainStickySize - GetMainAxis(position), GetCrossAxis(position));
937 }
938 }
939 currentStickyItem_->SetPosition(position);
940 } else {
941 currentStickyItem_->SetPosition(MakeValue<Offset>(0.0, 0.0));
942 }
943
944 if (!fixedCrossSize_) {
945 crossSize = std::max(crossSize, GetCrossSize(stickyItemLayoutSize));
946 }
947 }
948 }
949
950 return MakeValue<Size>(mainSize, crossSize);
951 }
952
SetItemsPosition(double mainSize)953 Size RenderList::SetItemsPosition(double mainSize)
954 {
955 double crossSize = fixedCrossSize_ ? GetCrossSize(GetLayoutParam().GetMaxSize()) : 0.0;
956 if (items_.empty()) {
957 return MakeValue<Size>(fixedMainSize_ ? mainSize : 0.0, crossSize);
958 }
959
960 double curMainPos = currentOffset_;
961 size_t index = startIndex_;
962 size_t newStickyIndex = 0;
963 RefPtr<RenderListItem> newStickyItem;
964 RefPtr<RenderListItem> nextStickyItem;
965 double nextStickyMainAxis = Size::INFINITE_SIZE;
966 size_t firstIdx = INITIAL_CHILD_INDEX;
967 size_t lastIdx = 0;
968 double selectedItemMainSize = selectedItem_ ? GetMainSize(selectedItem_->GetLayoutSize()) : 0.0;
969
970 for (const auto& child : items_) {
971 const auto& childLayoutSize = child->GetLayoutSize();
972 double childMainSize = GetMainSize(childLayoutSize);
973
974 if (selectedItem_) {
975 double range = std::min(selectedItemMainSize, childMainSize) / 2.0;
976 bool beforeSelectedItem = index <= selectedItemIndex_;
977 if (beforeSelectedItem && targetIndex_ == index) {
978 targetMainAxis_ = curMainPos;
979 curMainPos += selectedItemMainSize + spaceWidth_;
980 }
981
982 if (movingForward_) {
983 double axis = selectedItemMainAxis_;
984 if (GreatOrEqual(axis, curMainPos) && LessNotEqual(axis, curMainPos + range)) {
985 targetIndex_ = beforeSelectedItem ? index : index - 1;
986 targetMainAxis_ = curMainPos;
987 curMainPos += selectedItemMainSize + spaceWidth_;
988 }
989 } else {
990 double axis = selectedItemMainAxis_ + selectedItemMainSize;
991 double limit = curMainPos + childMainSize;
992 if (GreatNotEqual(axis, limit - range) && LessOrEqual(axis, limit)) {
993 targetIndex_ = beforeSelectedItem ? index + 1 : index;
994 targetMainAxis_ = curMainPos;
995 curMainPos -= selectedItemMainSize + spaceWidth_;
996 }
997 }
998 }
999
1000 auto offsetCross = CalculateLaneCrossOffset(crossSize, GetCrossSize(childLayoutSize));
1001 auto offset = MakeValue<Offset>(curMainPos, offsetCross);
1002 if (chainAnimation_) {
1003 double chainDelta = GetChainDelta(index);
1004 auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
1005 if (itemGroup) {
1006 itemGroup->SetChainOffset(-chainDelta);
1007 }
1008 offset += MakeValue<Offset>(-chainDelta, 0.0);
1009 }
1010
1011 if (isRightToLeft_) {
1012 if (IsVertical()) {
1013 offset = MakeValue<Offset>(
1014 GetMainAxis(offset), crossSize - GetCrossSize(childLayoutSize) - GetCrossAxis(offset));
1015 } else {
1016 offset = MakeValue<Offset>(
1017 mainSize - GetMainSize(childLayoutSize) - GetMainAxis(offset), GetCrossAxis(offset));
1018 }
1019 }
1020 SetChildPosition(child, offset);
1021 // Disable sticky mode while expand all items
1022 if (fixedMainSize_ && child->GetSticky() != StickyMode::NONE) {
1023 if (LessOrEqual(curMainPos, 0.0)) {
1024 newStickyItem = child;
1025 newStickyIndex = index;
1026 } else if (!nextStickyItem) {
1027 nextStickyItem = child;
1028 nextStickyMainAxis = curMainPos;
1029 }
1030 }
1031
1032 childMainSize += spaceWidth_;
1033 if (LessNotEqual(curMainPos, mainSize) && GreatNotEqual(curMainPos + childMainSize, 0.0)) {
1034 if (!fixedCrossSize_) {
1035 crossSize = std::max(crossSize, GetCrossSize(childLayoutSize));
1036 }
1037 firstIdx = std::min(firstIdx, index);
1038 lastIdx = std::max(lastIdx, index);
1039 }
1040
1041 if (child != selectedItem_) {
1042 curMainPos += childMainSize;
1043 }
1044
1045 if (selectedItem_ && index > selectedItemIndex_ && targetIndex_ == index) {
1046 targetMainAxis_ = curMainPos;
1047 curMainPos += selectedItemMainSize + spaceWidth_;
1048 }
1049
1050 ++index;
1051 }
1052 if (firstIdx != firstDisplayIndex_ || lastIdx != lastDisplayIndex_) {
1053 firstDisplayIndex_ = firstIdx;
1054 lastDisplayIndex_ = lastIdx;
1055 ResumeEventCallback(component_, &ListComponent::GetOnScrollIndex, static_cast<int32_t>(firstDisplayIndex_),
1056 static_cast<int32_t>(lastDisplayIndex_), static_cast<int32_t>(midDisplayIndex_));
1057 }
1058
1059 // Disable sticky mode while expand all items
1060 if (!fixedMainSize_) {
1061 return MakeValue<Size>(curMainPos - spaceWidth_, crossSize);
1062 }
1063
1064 UpdateStickyListItem(newStickyItem, newStickyIndex, nextStickyItem);
1065 if (currentStickyItem_) {
1066 const auto& stickyItemLayoutSize = currentStickyItem_->GetLayoutSize();
1067 const double mainStickySize = GetMainSize(stickyItemLayoutSize) + spaceWidth_;
1068 auto offsetCross = CalculateLaneCrossOffset(crossSize, GetCrossSize(currentStickyItem_->GetLayoutSize()));
1069 if (nextStickyItem && LessNotEqual(nextStickyMainAxis, mainStickySize)) {
1070 auto position = MakeValue<Offset>(nextStickyMainAxis - mainStickySize, offsetCross);
1071 if (isRightToLeft_) {
1072 if (IsVertical()) {
1073 position = MakeValue<Offset>(
1074 GetMainAxis(position), crossSize - GetCrossSize(stickyItemLayoutSize) - GetCrossAxis(position));
1075 } else {
1076 position =
1077 MakeValue<Offset>(mainSize - mainStickySize - GetMainAxis(position), GetCrossAxis(position));
1078 }
1079 }
1080 currentStickyItem_->SetPosition(position);
1081 } else {
1082 currentStickyItem_->SetPosition(MakeValue<Offset>(0.0, offsetCross));
1083 }
1084
1085 if (!fixedCrossSize_) {
1086 crossSize = std::max(crossSize, GetCrossSize(stickyItemLayoutSize));
1087 }
1088 }
1089
1090 return MakeValue<Size>(mainSize, crossSize);
1091 }
1092
UpdateStickyListItem(const RefPtr<RenderListItem> & newStickyItem,size_t newStickyItemIndex,const RefPtr<RenderListItem> & nextStickyItem)1093 void RenderList::UpdateStickyListItem(const RefPtr<RenderListItem>& newStickyItem, size_t newStickyItemIndex,
1094 const RefPtr<RenderListItem>& nextStickyItem)
1095 {
1096 if (newStickyItem) {
1097 if (newStickyItem == currentStickyItem_) {
1098 return;
1099 }
1100
1101 if (currentStickyItem_ && currentStickyIndex_ < startIndex_) {
1102 RecycleListItem(currentStickyIndex_);
1103 }
1104
1105 currentStickyItem_ = newStickyItem;
1106 currentStickyIndex_ = newStickyItemIndex;
1107 return;
1108 }
1109
1110 if (nextStickyItem && nextStickyItem == currentStickyItem_) {
1111 ApplyPreviousStickyListItem(currentStickyIndex_ - 1, true);
1112 return;
1113 }
1114
1115 if (currentStickyIndex_ == INITIAL_CHILD_INDEX && startIndex_ > 0) {
1116 ApplyPreviousStickyListItem(startIndex_ - 1, true);
1117 }
1118 }
1119
MakeInnerLayout()1120 LayoutParam RenderList::MakeInnerLayout()
1121 {
1122 Size maxSize;
1123 Size minSize;
1124 if (vertical_) {
1125 maxSize = Size(GetLayoutParam().GetMaxSize().Width(), Size::INFINITE_SIZE);
1126 minSize = Size(GetLayoutParam().GetMinSize().Width(), 0.0);
1127 } else {
1128 maxSize = Size(Size::INFINITE_SIZE, GetLayoutParam().GetMaxSize().Height());
1129 minSize = Size(0.0, GetLayoutParam().GetMinSize().Height());
1130 }
1131 return LayoutParam(maxSize, minSize);
1132 }
1133
MakeInnerLayoutForLane()1134 LayoutParam RenderList::MakeInnerLayoutForLane()
1135 {
1136 Size maxSize;
1137 Size minSize;
1138 if (vertical_) {
1139 maxSize = Size(std::min(GetLayoutParam().GetMaxSize().Width() / lanes_, maxLaneLength_), Size::INFINITE_SIZE);
1140 minSize = Size(GetLayoutParam().GetMinSize().Width(), 0.0);
1141 } else {
1142 maxSize = Size(Size::INFINITE_SIZE, std::min(GetLayoutParam().GetMaxSize().Height() / lanes_, maxLaneLength_));
1143 minSize = Size(0.0, GetLayoutParam().GetMinSize().Height());
1144 }
1145 return LayoutParam(maxSize, minSize);
1146 }
1147
GetCurMainPosAndMainSize(double & curMainPos,double & mainSize)1148 bool RenderList::GetCurMainPosAndMainSize(double& curMainPos, double& mainSize)
1149 {
1150 // Check validation of layout size
1151 mainSize = ApplyLayoutParam();
1152 if (NearZero(mainSize)) {
1153 LOGW("Cannot layout using invalid view port");
1154 return false;
1155 }
1156 if (isLaneList_) {
1157 curMainPos = LayoutOrRecycleCurrentItemsForLaneList(mainSize);
1158 } else {
1159 curMainPos = LayoutOrRecycleCurrentItems(mainSize);
1160 }
1161 // Try to request new items at end if needed
1162 for (size_t newIndex = startIndex_ + items_.size();; ++newIndex) {
1163 if (cachedCount_ != 0) {
1164 if (endCachedCount_ >= cachedCount_) {
1165 break;
1166 }
1167 } else {
1168 if (GreatOrEqual(curMainPos, endMainPos_)) {
1169 break;
1170 }
1171 }
1172 auto child = RequestAndLayoutNewItem(newIndex, curMainPos);
1173 if (!child) {
1174 startIndex_ = std::min(startIndex_, TotalCount());
1175 break;
1176 }
1177 if (GreatOrEqual(curMainPos, mainSize)) {
1178 ++endCachedCount_;
1179 }
1180 curMainPos += GetMainSize(child->GetLayoutSize()) + spaceWidth_;
1181 }
1182 if (selectedItem_ && selectedItemIndex_ < startIndex_) {
1183 curMainPos += GetMainSize(selectedItem_->GetLayoutSize()) + spaceWidth_;
1184 }
1185 curMainPos -= spaceWidth_;
1186 return true;
1187 }
1188
HandleOverScroll()1189 bool RenderList::HandleOverScroll()
1190 {
1191 if (scrollEffect_ && scrollEffect_->IsFadeEffect() && (reachStart_ || reachEnd_)) {
1192 double overScroll = scrollEffect_->CalculateOverScroll(prevOffset_, reachEnd_);
1193 if (!NearZero(overScroll)) {
1194 Axis axis = IsVertical() ? Axis::VERTICAL : Axis::HORIZONTAL;
1195 scrollEffect_->HandleOverScroll(axis, overScroll, viewPort_);
1196 }
1197 return false;
1198 }
1199 return true;
1200 }
1201
UpdateScrollPosition(double offset,int32_t source)1202 bool RenderList::UpdateScrollPosition(double offset, int32_t source)
1203 {
1204 if (source == SCROLL_FROM_START) {
1205 return true;
1206 }
1207 if (NearZero(offset)) {
1208 return true;
1209 }
1210 if (scrollBar_ && scrollBar_->NeedScrollBar()) {
1211 scrollBar_->SetActive(SCROLL_FROM_CHILD != source);
1212 }
1213 if (reachStart_ && HandleRefreshEffect(offset, source, currentOffset_)) {
1214 return false;
1215 }
1216 if (reachStart_ && reachEnd_) {
1217 return false;
1218 }
1219 if (offset > 0.0) {
1220 if (reachStart_ && (!scrollEffect_ || source == SCROLL_FROM_AXIS)) {
1221 return false;
1222 }
1223 reachEnd_ = false;
1224 } else {
1225 if (reachEnd_ && (!scrollEffect_ || source == SCROLL_FROM_AXIS)) {
1226 return false;
1227 }
1228 reachStart_ = false;
1229 }
1230 auto context = context_.Upgrade();
1231 if (context) {
1232 dipScale_ = context->GetDipScale();
1233 }
1234 offset_ = offset;
1235 if (source == SCROLL_FROM_UPDATE) {
1236 scrollState_ = ScrollState::SCROLL;
1237 } else if (source == SCROLL_FROM_ANIMATION || source == SCROLL_FROM_ANIMATION_SPRING) {
1238 scrollState_ = ScrollState::FLING;
1239 } else {
1240 scrollState_ = ScrollState::IDLE;
1241 }
1242 currentOffset_ += offset;
1243 bool next = HandleOverScroll();
1244 MarkNeedLayout(true);
1245 return next;
1246 }
1247
TouchTest(const Point & globalPoint,const Point & parentLocalPoint,const TouchRestrict & touchRestrict,TouchTestResult & result)1248 bool RenderList::TouchTest(const Point& globalPoint, const Point& parentLocalPoint, const TouchRestrict& touchRestrict,
1249 TouchTestResult& result)
1250 {
1251 // when click point is in sticky item, consume the touch event to avoid clicking on the list item underneath.
1252 if (currentStickyItem_ && currentStickyItem_->GetPaintRect().IsInRegion(parentLocalPoint)) {
1253 currentStickyItem_->TouchTest(globalPoint, parentLocalPoint, touchRestrict, result);
1254 return true;
1255 }
1256
1257 return RenderNode::TouchTest(globalPoint, parentLocalPoint, touchRestrict, result);
1258 }
1259
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)1260 void RenderList::OnTouchTestHit(
1261 const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
1262 {
1263 if (!GetVisible()) {
1264 return;
1265 }
1266
1267 if (component_->GetEditMode() && dragDropGesture_) {
1268 dragDropGesture_->SetCoordinateOffset(coordinateOffset);
1269 result.emplace_back(dragDropGesture_);
1270 }
1271
1272 // Disable scroll while expand all items
1273 if (!fixedMainSize_) {
1274 return;
1275 }
1276 if (!scrollable_) {
1277 return;
1278 }
1279 if (scrollable_->Available() && scrollBar_ && scrollBar_->InBarRegion(globalPoint_ - coordinateOffset)) {
1280 scrollBar_->AddScrollBarController(coordinateOffset, result);
1281 } else {
1282 scrollable_->SetCoordinateOffset(coordinateOffset);
1283 result.emplace_back(scrollable_);
1284 }
1285 }
1286
ApplyLayoutParam()1287 double RenderList::ApplyLayoutParam()
1288 {
1289 auto maxLayoutSize = GetLayoutParam().GetMaxSize();
1290 if (!maxLayoutSize.IsValid() || maxLayoutSize.IsEmpty()) {
1291 if (!GetVisible()) {
1292 SetLayoutSize(Size());
1293 }
1294 return 0.0;
1295 }
1296
1297 auto maxMainSize = GetMainSize(maxLayoutSize);
1298
1299 // Update layout info for list weather layout param is changed
1300 if (IsLayoutParamChanged()) {
1301 // Minimum layout param MUST NOT be INFINITE
1302 ACE_DCHECK(!GetLayoutParam().GetMinSize().IsInfinite());
1303
1304 if (NearEqual(maxMainSize, Size::INFINITE_SIZE)) {
1305 // Clear all child items
1306 RemoveAllItems();
1307 startIndex_ = 0;
1308 startIndexOffset_ = 0.0;
1309 currentOffset_ = 0.0;
1310
1311 startMainPos_ = 0.0;
1312 endMainPos_ = std::numeric_limits<decltype(endMainPos_)>::max();
1313 fixedMainSizeByLayoutParam_ = false;
1314 } else {
1315 startMainPos_ = (1.0 - VIEW_PORT_SCALE) / 2 * maxMainSize;
1316 endMainPos_ = startMainPos_ + (maxMainSize * VIEW_PORT_SCALE);
1317 fixedMainSizeByLayoutParam_ = NearEqual(maxMainSize, GetMainSize(GetLayoutParam().GetMinSize()));
1318 SizeChangeOffset(maxMainSize);
1319 }
1320
1321 fixedCrossSize_ = !NearEqual(GetCrossSize(maxLayoutSize), Size::INFINITE_SIZE);
1322 TakeBoundary(fixedMainSizeByLayoutParam_ && fixedCrossSize_);
1323 }
1324
1325 fixedMainSize_ = fixedMainSizeByLayoutParam_;
1326 mainSize_ = maxMainSize;
1327 return maxMainSize;
1328 }
1329
GetItemPositionState(double curMainPos,double lastItemMainSize)1330 ItemPositionState RenderList::GetItemPositionState(double curMainPos, double lastItemMainSize)
1331 {
1332 // curMainPos <= startMainPos_
1333 if (LessOrEqual(curMainPos, startMainPos_)) {
1334 return ItemPositionState::AHEAD_OF_VIEWPORT;
1335 }
1336 // (curMainPos > startMainPos_) and ((curMainPos <= endMainPos_) or (curMainPos - lastItemMainSize <= endMainPos_))
1337 if (LessOrEqual(curMainPos, endMainPos_) || (curMainPos - lastItemMainSize <= endMainPos_)) {
1338 return ItemPositionState::IN_VIEWPORT;
1339 }
1340 // curMainPos_ - lastItemMainSize > endMainPos_
1341 if (GreatNotEqual(curMainPos, endMainPos_)) {
1342 return ItemPositionState::BEHIND_VIEWPORT;
1343 }
1344 LOGE("invalid place of list item, curMainPos: %{public}f", curMainPos);
1345 return ItemPositionState::IN_VIEWPORT;
1346 }
1347
1348 #define RECYCLE_AND_ERASE_ITEMS_OUT_OF_VIEWPORT() \
1349 do { \
1350 for (size_t i = 0; i < itemsInOneRow.size(); i++) { \
1351 if (currentStickyItem_ != itemsInOneRow[i] && selectedItem_ != itemsInOneRow[i]) { \
1352 /* Recycle list items out of view port */ \
1353 RecycleListItem(curIndex - i); \
1354 } \
1355 it = items_.erase(--it); \
1356 } \
1357 } while (0);
1358
LayoutOrRecycleCurrentItemsForLaneList(double mainSize)1359 double RenderList::LayoutOrRecycleCurrentItemsForLaneList(double mainSize)
1360 {
1361 if (currentStickyItem_) {
1362 LayoutChild(currentStickyItem_);
1363 }
1364
1365 double curMainPos = currentOffset_;
1366 size_t curIndex = startIndex_ - 1;
1367 std::vector<RefPtr<RenderListItem>> itemsInOneRow;
1368 for (auto it = items_.begin(); it != items_.end();) {
1369 int32_t lackItemCount = 0;
1370 // 1. layout children in a row
1371 double mainSize = 0.0;
1372 itemsInOneRow.clear();
1373 for (int32_t i = 0; i < lanes_; i++) {
1374 RefPtr<RenderListItem> child = *(it);
1375 auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
1376 if (itemGroup && i > 0) {
1377 break;
1378 }
1379 if (child->IsForwardLayout()) {
1380 LayoutChild(child, curMainPos);
1381 double childMainSize = GetMainSize(child->GetLayoutSize());
1382 mainSize = std::max(mainSize, childMainSize);
1383 } else {
1384 double childMainSize = GetMainSize(child->GetLayoutSize());
1385 mainSize = std::max(mainSize, childMainSize);
1386 LayoutChild(child, curMainPos + mainSize, false);
1387 double newChildMainSizee = GetMainSize(child->GetLayoutSize());
1388 currentOffset_ -= (newChildMainSizee - childMainSize);
1389 startIndexOffset_ -= (newChildMainSizee - childMainSize);
1390 }
1391 itemsInOneRow.emplace_back(child);
1392 ++curIndex;
1393 ++it;
1394 if (itemGroup) {
1395 break;
1396 }
1397 // reach end of [items_]
1398 if (it == items_.end()) {
1399 lackItemCount = lanes_ - i - 1;
1400 break;
1401 }
1402 }
1403 // 2. calculate [curMainPos] after layout current row, deciding whether or not to request new item to fill
1404 // current row or to erase and recycle items in current row
1405 curMainPos += mainSize + spaceWidth_;
1406
1407 // 3. do different processing according to item position state
1408 auto itemPositionState = GetItemPositionState(curMainPos, mainSize + spaceWidth_);
1409 switch (itemPositionState) {
1410 // when items are ahead of viewport, do the following things:
1411 // 1. update [startIndex_] and [currentOffset_]
1412 // 2. recycle items and erase them from [items_]
1413 case ItemPositionState::AHEAD_OF_VIEWPORT: {
1414 startIndex_ = curIndex + 1;
1415 startIndexOffset_ += curMainPos - currentOffset_;
1416 currentOffset_ = curMainPos;
1417 RECYCLE_AND_ERASE_ITEMS_OUT_OF_VIEWPORT();
1418 break;
1419 }
1420 // when items are in viewport, continue to layout next row
1421 // if current item is the last one in [items_], request new items to supply current row
1422 case ItemPositionState::IN_VIEWPORT: {
1423 if (lanes_ == 1) {
1424 continue; // if list only has one lane, do not need to do suppliment for current row
1425 }
1426 size_t target = lackItemCount + items_.size() + startIndex_;
1427 for (size_t newIndex = startIndex_ + items_.size(); newIndex < target; newIndex++) {
1428 auto child = RequestAndLayoutNewItem(newIndex, curMainPos);
1429 if (!child) {
1430 startIndex_ = std::min(startIndex_, TotalCount());
1431 break;
1432 }
1433 if (AceType::DynamicCast<RenderListItemGroup>(child)) {
1434 break;
1435 }
1436 }
1437 break;
1438 }
1439 // when items are behind viewport, recycle items and erase them from [items_]
1440 case ItemPositionState::BEHIND_VIEWPORT: {
1441 RECYCLE_AND_ERASE_ITEMS_OUT_OF_VIEWPORT();
1442 curMainPos -= mainSize + spaceWidth_;
1443 break;
1444 }
1445 default:
1446 LOGW("unexpected item position state: %{public}d", itemPositionState);
1447 break;
1448 }
1449 }
1450 return curMainPos;
1451 }
1452
BackwardLayoutForCache(size_t & backwardLayoutIndex,double & backwardLayoutOffset)1453 void RenderList::BackwardLayoutForCache(size_t& backwardLayoutIndex, double& backwardLayoutOffset)
1454 {
1455 auto rit = items_.rend();
1456 std::advance(rit, (startIndex_ - backwardLayoutIndex));
1457 while (backwardLayoutIndex > startIndex_ && rit != items_.rend()) {
1458 const auto& child = *(rit++);
1459 if (!child->IsForwardLayout()) {
1460 LayoutChild(child, backwardLayoutOffset - spaceWidth_, false);
1461 }
1462 if (LessOrEqual(backwardLayoutOffset, 0.0)) {
1463 auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
1464 if (itemGroup) {
1465 startCachedCount_ += itemGroup->GetCurrStartCacheCount();
1466 } else {
1467 startCachedCount_++;
1468 }
1469 }
1470 double childSize = GetMainSize(child->GetLayoutSize());
1471 backwardLayoutOffset -= childSize + spaceWidth_;
1472 backwardLayoutIndex--;
1473 if (startCachedCount_ >= cachedCount_) {
1474 break;
1475 }
1476 }
1477 }
1478
LayoutOrRecycleCurrentItemsForCache(double mainSize)1479 double RenderList::LayoutOrRecycleCurrentItemsForCache(double mainSize)
1480 {
1481 double curMainPos = currentOffset_;
1482 size_t curIndex = startIndex_;
1483 size_t backwardLayoutIndex = startIndex_;
1484 double backwardLayoutOffset = currentOffset_;
1485 startCachedCount_ = 0;
1486 endCachedCount_ = 0;
1487 bool recycleAll = false;
1488 if (GreatOrEqual(curMainPos, mainSize)) {
1489 recycleAll = true;
1490 }
1491 for (auto it = items_.begin(); it != items_.end(); ++curIndex) {
1492 const auto& child = *(it);
1493 if (recycleAll || endCachedCount_ >= cachedCount_) {
1494 if (currentStickyItem_ != child && selectedItem_ != child) {
1495 // Recycle list items out of view port
1496 RecycleListItem(curIndex);
1497 }
1498 it = items_.erase(it);
1499 continue;
1500 }
1501
1502 if (GreatOrEqual(curMainPos, mainSize)) {
1503 auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
1504 endCachedCount_ += itemGroup ? itemGroup->GetCurrEndCacheCount() : 1;
1505 }
1506
1507 if (child->IsForwardLayout()) {
1508 LayoutChild(child, curMainPos);
1509 }
1510 double childSize = GetMainSize(child->GetLayoutSize());
1511 curMainPos += childSize + spaceWidth_;
1512
1513 if (LessOrEqual(curMainPos, 0.0) || !child->IsForwardLayout()) {
1514 backwardLayoutIndex = curIndex + 1;
1515 backwardLayoutOffset = curMainPos;
1516 }
1517 ++it;
1518 }
1519
1520 if (backwardLayoutIndex > startIndex_) {
1521 BackwardLayoutForCache(backwardLayoutIndex, backwardLayoutOffset);
1522 }
1523
1524 curIndex = startIndex_;
1525 for (auto it = items_.begin(); it != items_.end() && curIndex < backwardLayoutIndex; ++curIndex) {
1526 const auto& child = *(it);
1527 if (currentStickyItem_ != child && selectedItem_ != child) {
1528 // Recycle list items out of view port
1529 RecycleListItem(curIndex);
1530 }
1531 it = items_.erase(it);
1532 }
1533 startIndex_ = backwardLayoutIndex;
1534 startIndexOffset_ += backwardLayoutOffset - currentOffset_;
1535 currentOffset_ = backwardLayoutOffset;
1536 return curMainPos;
1537 }
1538
LayoutOrRecycleCurrentItems(double mainSize)1539 double RenderList::LayoutOrRecycleCurrentItems(double mainSize)
1540 {
1541 if (currentStickyItem_) {
1542 LayoutChild(currentStickyItem_);
1543 }
1544
1545 if (cachedCount_ != 0) {
1546 return LayoutOrRecycleCurrentItemsForCache(mainSize);
1547 }
1548
1549 double curMainPos = currentOffset_;
1550 size_t curIndex = startIndex_;
1551 for (auto it = items_.begin(); it != items_.end(); ++curIndex) {
1552 const auto& child = *(it);
1553 if (LessOrEqual(curMainPos, endMainPos_)) {
1554 if (child->IsForwardLayout()) {
1555 LayoutChild(child, curMainPos);
1556 double childSize = GetMainSize(child->GetLayoutSize());
1557 curMainPos += childSize + spaceWidth_;
1558 } else {
1559 double oldChildSize = GetMainSize(child->GetLayoutSize());
1560 LayoutChild(child, curMainPos + oldChildSize, false);
1561 curMainPos += oldChildSize + spaceWidth_;
1562 double childSize = GetMainSize(child->GetLayoutSize());
1563 currentOffset_ -= (childSize - oldChildSize);
1564 startIndexOffset_ -= (childSize - oldChildSize);
1565 }
1566 if (GreatOrEqual(curMainPos, startMainPos_)) {
1567 ++it;
1568 continue;
1569 }
1570 startIndexOffset_ += curMainPos - currentOffset_;
1571 currentOffset_ = curMainPos;
1572 startIndex_ = curIndex + 1;
1573 }
1574
1575 if (currentStickyItem_ != child && selectedItem_ != child) {
1576 // Recycle list items out of view port
1577 RecycleListItem(curIndex);
1578 }
1579 it = items_.erase(it);
1580 }
1581
1582 return curMainPos;
1583 }
1584
RequestAndLayoutNewItem(size_t index,double currMainPos,bool forward)1585 RefPtr<RenderListItem> RenderList::RequestAndLayoutNewItem(size_t index, double currMainPos, bool forward)
1586 {
1587 RefPtr<RenderListItem> newChild;
1588 if (index == currentStickyIndex_ && currentStickyItem_) {
1589 newChild = currentStickyItem_;
1590 } else {
1591 {
1592 ACE_SCOPED_TRACE("RenderList:BuildListItem");
1593 newChild = RequestListItem(index);
1594 }
1595 if (newChild) {
1596 ACE_SCOPED_TRACE("RenderList:MeasureListItem");
1597 AddChildItem(newChild);
1598 LayoutChild(newChild, currMainPos, forward);
1599 }
1600 }
1601
1602 if (newChild) {
1603 if (index < startIndex_) {
1604 items_.emplace_front(newChild);
1605 } else {
1606 items_.emplace_back(newChild);
1607 }
1608 }
1609 return newChild;
1610 }
1611
RequestListItem(size_t index)1612 RefPtr<RenderListItem> RenderList::RequestListItem(size_t index)
1613 {
1614 auto generator = itemGenerator_.Upgrade();
1615 auto newItem = generator ? generator->RequestListItem(index) : RefPtr<RenderListItem>();
1616 if (!newItem) {
1617 return newItem;
1618 }
1619
1620 if (component_->GetEditMode()) {
1621 newItem->SetEditMode(true);
1622 newItem->SetOnDeleteClick([weak = AceType::WeakClaim(this)](RefPtr<RenderListItem> item) {
1623 auto spThis = weak.Upgrade();
1624 if (!spThis) {
1625 return;
1626 }
1627 spThis->OnItemDelete(item);
1628 });
1629
1630 newItem->SetOnSelect([weak = AceType::WeakClaim(this)](RefPtr<RenderListItem> item) {
1631 auto spThis = weak.Upgrade();
1632 if (!spThis) {
1633 return;
1634 }
1635 spThis->OnItemSelect(item);
1636 });
1637 }
1638
1639 if (!newItem->GetVisible()) {
1640 newItem->SetVisible(true);
1641 }
1642
1643 if (newItem->GetHidden()) {
1644 newItem->SetHidden(false);
1645 }
1646
1647 return newItem;
1648 }
1649
RecycleListItem(size_t index)1650 void RenderList::RecycleListItem(size_t index)
1651 {
1652 auto generator = itemGenerator_.Upgrade();
1653 if (generator) {
1654 generator->RecycleListItem(index);
1655 }
1656 }
1657
FindItemStartIndex(size_t index)1658 size_t RenderList::FindItemStartIndex(size_t index)
1659 {
1660 auto generator = itemGenerator_.Upgrade();
1661 if (generator) {
1662 return generator->FindItemStartIndex(index);
1663 }
1664 return 0;
1665 }
1666
GetItemRelativeIndex(size_t index)1667 size_t RenderList::GetItemRelativeIndex(size_t index)
1668 {
1669 return index - FindItemStartIndex(index);
1670 }
1671
TotalCount()1672 size_t RenderList::TotalCount()
1673 {
1674 auto generator = itemGenerator_.Upgrade();
1675 return generator ? generator->TotalCount() : 0;
1676 }
1677
FindPreviousStickyListItem(size_t index)1678 size_t RenderList::FindPreviousStickyListItem(size_t index)
1679 {
1680 auto generator = itemGenerator_.Upgrade();
1681 return generator ? generator->FindPreviousStickyListItem(index) : ListItemGenerator::INVALID_INDEX;
1682 }
1683
OnItemDelete(const RefPtr<RenderListItem> & item)1684 void RenderList::OnItemDelete(const RefPtr<RenderListItem>& item)
1685 {
1686 size_t index = GetIndexByListItem(item);
1687 if (!ResumeEventCallback(component_, &ListComponent::GetOnItemDelete, false, static_cast<int32_t>(index))) {
1688 LOGI("User canceled, stop deleting item");
1689 return;
1690 }
1691
1692 if (index < startIndex_) {
1693 --startIndex_;
1694 useEstimateCurrentOffset_ = true;
1695 }
1696 }
1697
OnItemSelect(const RefPtr<RenderListItem> & item)1698 void RenderList::OnItemSelect(const RefPtr<RenderListItem>& item)
1699 {
1700 targetIndex_ = GetIndexByListItem(item);
1701 selectedItemIndex_ = targetIndex_;
1702 selectedItem_ = item;
1703 selectedItemMainAxis_ = GetMainAxis(item->GetPosition());
1704 LOGI("Select list item %{private}zu to move", selectedItemIndex_);
1705 }
1706
GetIndexByListItem(const RefPtr<RenderListItem> & item) const1707 size_t RenderList::GetIndexByListItem(const RefPtr<RenderListItem>& item) const
1708 {
1709 ACE_DCHECK(item);
1710
1711 auto it = std::find(items_.begin(), items_.end(), item);
1712 if (it != items_.end()) {
1713 int32_t offset = std::distance(items_.begin(), it);
1714 ACE_DCHECK(offset >= 0);
1715 return startIndex_ + offset;
1716 }
1717
1718 ACE_DCHECK(fixedMainSize_);
1719 ACE_DCHECK(item == currentStickyItem_);
1720 return currentStickyIndex_;
1721 }
1722
RemoveAllItems()1723 void RenderList::RemoveAllItems()
1724 {
1725 items_.clear();
1726 ClearChildren();
1727 currentStickyItem_.Reset();
1728 currentStickyIndex_ = INITIAL_CHILD_INDEX;
1729 isActionByScroll_ = false;
1730 }
1731
ApplyPreviousStickyListItem(size_t index,bool needLayout)1732 void RenderList::ApplyPreviousStickyListItem(size_t index, bool needLayout)
1733 {
1734 size_t newIndex = FindPreviousStickyListItem(index);
1735 if (newIndex == ListItemGenerator::INVALID_INDEX) {
1736 currentStickyItem_.Reset();
1737 currentStickyIndex_ = INVALID_CHILD_INDEX;
1738 return;
1739 }
1740
1741 currentStickyIndex_ = newIndex;
1742 currentStickyItem_ = RequestListItem(currentStickyIndex_);
1743 if (currentStickyIndex_ < startIndex_) {
1744 AddChildItem(currentStickyItem_);
1745 if (needLayout) {
1746 LayoutChild(currentStickyItem_);
1747 }
1748 }
1749 }
1750
JumpToIndex(int32_t idx,int32_t source)1751 void RenderList::JumpToIndex(int32_t idx, int32_t source)
1752 {
1753 RemoveAllItems();
1754 startIndex_ = static_cast<size_t>(idx);
1755 useEstimateCurrentOffset_ = true;
1756 currentOffset_ = 0.0;
1757 MarkNeedLayout(true);
1758 }
1759
AnimateTo(const Dimension & position,float duration,const RefPtr<Curve> & curve)1760 void RenderList::AnimateTo(const Dimension& position, float duration, const RefPtr<Curve>& curve)
1761 {
1762 if (!animator_->IsStopped()) {
1763 animator_->Stop();
1764 }
1765 animator_->ClearInterpolators();
1766 auto pos = NormalizePercentToPx(position, IsVertical());
1767 double currentOffset = startIndexOffset_ - currentOffset_;
1768 auto animation = AceType::MakeRefPtr<CurveAnimation<double>>(currentOffset, pos, curve);
1769 animation->AddListener([weak = AceType::WeakClaim(this)](double pos) {
1770 auto renderList = weak.Upgrade();
1771 if (!renderList) {
1772 return;
1773 }
1774 if (renderList->scrollable_ && !renderList->scrollable_->IsSpringMotionRunning()) {
1775 double delta = (renderList->startIndexOffset_ - renderList->currentOffset_) - pos;
1776 renderList->UpdateScrollPosition(delta, SCROLL_FROM_JUMP);
1777 }
1778 });
1779 animator_->AddInterpolator(animation);
1780 animator_->SetDuration(std::min(duration, SCROLL_MAX_TIME));
1781 animator_->ClearStopListeners();
1782 animator_->Play();
1783 }
1784
CurrentOffset()1785 Offset RenderList::CurrentOffset()
1786 {
1787 double currentOffset = startIndexOffset_ - currentOffset_;
1788 auto ctx = GetContext().Upgrade();
1789 if (!ctx) {
1790 return vertical_ ? Offset(0.0, currentOffset) : Offset(currentOffset, 0.0);
1791 }
1792 auto mainOffset = ctx->ConvertPxToVp(Dimension(currentOffset, DimensionUnit::PX));
1793 return vertical_ ? Offset(0.0, mainOffset) : Offset(mainOffset, 0.0);
1794 }
1795
ScrollToEdge(ScrollEdgeType scrollEdgeType,bool smooth)1796 void RenderList::ScrollToEdge(ScrollEdgeType scrollEdgeType, bool smooth)
1797 {
1798 if (scrollable_ && !scrollable_->IsAnimationNotRunning()) {
1799 scrollable_->StopScrollable();
1800 }
1801 if (vertical_) {
1802 if (scrollEdgeType == ScrollEdgeType::SCROLL_TOP) {
1803 JumpToIndex(0, SCROLL_FROM_JUMP);
1804 } else if (scrollEdgeType == ScrollEdgeType::SCROLL_BOTTOM) {
1805 JumpToIndex(TotalCount(), SCROLL_FROM_JUMP);
1806 }
1807 } else {
1808 if (scrollEdgeType == ScrollEdgeType::SCROLL_LEFT) {
1809 JumpToIndex(0, SCROLL_FROM_JUMP);
1810 } else if (scrollEdgeType == ScrollEdgeType::SCROLL_RIGHT) {
1811 JumpToIndex(TotalCount(), SCROLL_FROM_JUMP);
1812 }
1813 }
1814 }
1815
ScrollPage(bool reverse,bool smooth)1816 void RenderList::ScrollPage(bool reverse, bool smooth)
1817 {
1818 if (scrollable_ && !scrollable_->IsAnimationNotRunning()) {
1819 scrollable_->StopScrollable();
1820 }
1821 double pageSize = GetMainSize(GetLayoutSize());
1822 if (reverse) {
1823 if (!reachStart_) {
1824 currentOffset_ += pageSize;
1825 MarkNeedLayout();
1826 }
1827 } else {
1828 if (!reachEnd_) {
1829 currentOffset_ -= pageSize;
1830 MarkNeedLayout();
1831 }
1832 }
1833 }
1834
ScrollBy(double pixelX,double pixelY)1835 void RenderList::ScrollBy(double pixelX, double pixelY)
1836 {
1837 if (IsVertical()) {
1838 currentOffset_ -= pixelY;
1839 } else {
1840 currentOffset_ -= pixelX;
1841 }
1842 MarkNeedLayout();
1843 }
1844
AdjustOffset(Offset & delta,int32_t source)1845 void RenderList::AdjustOffset(Offset& delta, int32_t source)
1846 {
1847 // when scrollEffect equal to none, no need to adjust offset
1848 if (!scrollEffect_) {
1849 return;
1850 }
1851
1852 if (delta.IsZero() || source == SCROLL_FROM_ANIMATION || source == SCROLL_FROM_ANIMATION_SPRING) {
1853 return;
1854 }
1855
1856 double viewPortSize = GetMainSize(GetPaintRect().GetSize());
1857 double offset = GetMainAxis(delta);
1858 if (NearZero(viewPortSize) || NearZero(offset)) {
1859 return;
1860 }
1861
1862 double maxScrollExtent = mainScrollExtent_ - viewPortSize;
1863 double overscrollPastStart = 0.0;
1864 double overscrollPastEnd = 0.0;
1865 double overscrollPast = 0.0;
1866 bool easing = false;
1867
1868 overscrollPastStart = std::max(GetCurrentPosition(), 0.0);
1869 overscrollPastEnd = std::max(-GetCurrentPosition() - maxScrollExtent, 0.0);
1870 // do not adjust offset if direction opposite from the overScroll direction when out of boundary
1871 if ((overscrollPastStart > 0.0 && offset < 0.0) || (overscrollPastEnd > 0.0 && offset > 0.0)) {
1872 return;
1873 }
1874 easing = (overscrollPastStart > 0.0 && offset > 0.0) || (overscrollPastEnd > 0.0 && offset < 0.0);
1875
1876 overscrollPast = std::max(overscrollPastStart, overscrollPastEnd);
1877 double friction = easing ? RenderScroll::CalculateFriction((overscrollPast - std::abs(offset)) / viewPortSize)
1878 : RenderScroll::CalculateFriction(overscrollPast / viewPortSize);
1879 double direction = offset / std::abs(offset);
1880 offset = direction * RenderScroll::CalculateOffsetByFriction(overscrollPast, std::abs(offset), friction);
1881 vertical_ ? delta.SetY(offset) : delta.SetX(offset);
1882 }
1883
GetCurrentPosition() const1884 double RenderList::GetCurrentPosition() const
1885 {
1886 return currentOffset_;
1887 }
1888
IsOutOfBoundary() const1889 bool RenderList::IsOutOfBoundary() const
1890 {
1891 return isOutOfBoundary_;
1892 }
1893
ResetEdgeEffect()1894 void RenderList::ResetEdgeEffect()
1895 {
1896 if (!scrollEffect_) {
1897 LOGE("ResetEdgeEffect failed, scrollEffect_ is nullptr");
1898 return;
1899 }
1900
1901 scrollEffect_->SetCurrentPositionCallback([weak = AceType::WeakClaim(this)]() {
1902 auto list = weak.Upgrade();
1903 if (list) {
1904 return list->GetCurrentPosition();
1905 }
1906 return 0.0;
1907 });
1908 scrollEffect_->SetLeadingCallback([weak = AceType::WeakClaim(this)]() {
1909 auto list = weak.Upgrade();
1910 if (list) {
1911 return list->GetMainSize(list->GetLayoutSize()) - list->mainScrollExtent_;
1912 }
1913 return 0.0;
1914 });
1915
1916 scrollEffect_->SetTrailingCallback([weak = AceType::WeakClaim(this)]() { return 0.0; });
1917 scrollEffect_->SetInitLeadingCallback([weak = AceType::WeakClaim(this)]() {
1918 auto list = weak.Upgrade();
1919 if (list) {
1920 return list->GetMainSize(list->GetLayoutSize()) - list->mainScrollExtent_;
1921 }
1922 return 0.0;
1923 });
1924 scrollEffect_->SetInitTrailingCallback([weak = AceType::WeakClaim(this)]() { return 0.0; });
1925 scrollEffect_->SetScrollNode(AceType::WeakClaim(this));
1926 SetEdgeEffectAttribute();
1927 scrollEffect_->InitialEdgeEffect();
1928 }
1929
SetEdgeEffectAttribute()1930 void RenderList::SetEdgeEffectAttribute()
1931 {
1932 if (scrollEffect_ && scrollable_) {
1933 scrollEffect_->SetScrollable(scrollable_);
1934 scrollEffect_->RegisterSpringCallback();
1935 if (scrollEffect_->IsSpringEffect()) {
1936 scrollable_->SetOutBoundaryCallback([weakScroll = AceType::WeakClaim(this)]() {
1937 auto scroll = weakScroll.Upgrade();
1938 if (scroll) {
1939 return scroll->IsOutOfBoundary();
1940 }
1941 return false;
1942 });
1943 }
1944 }
1945 }
1946
CalculateMainScrollExtent(double curMainPos,double mainSize)1947 void RenderList::CalculateMainScrollExtent(double curMainPos, double mainSize)
1948 {
1949 // check current is out of boundary
1950 isOutOfBoundary_ = LessNotEqual(curMainPos, mainSize) || GreatNotEqual(currentOffset_, 0.0);
1951 // content length
1952 mainScrollExtent_ = curMainPos - currentOffset_;
1953 if (GetChildren().empty()) {
1954 return;
1955 }
1956 Size itemSize; // Calculate all children layout size.
1957 for (const auto& child : GetChildren()) {
1958 itemSize += child->GetLayoutSize();
1959 }
1960 auto averageItemHeight = GetMainSize(itemSize) / GetChildren().size() + spaceWidth_;
1961 estimatedHeight_ = averageItemHeight * TotalCount();
1962 lastOffset_ = startIndex_ * averageItemHeight - currentOffset_;
1963 if (startIndex_ == 0) {
1964 startIndexOffset_ = 0.0;
1965 } else if (useEstimateCurrentOffset_) {
1966 useEstimateCurrentOffset_ = false;
1967 startIndexOffset_ = startIndex_ * averageItemHeight;
1968 }
1969 if (scrollBar_) {
1970 scrollBar_->SetScrollable(estimatedHeight_ > GetMainSize(GetLayoutSize()));
1971 }
1972 }
1973
ProcessDragStart(double startPosition)1974 void RenderList::ProcessDragStart(double startPosition)
1975 {
1976 auto globalMainOffset = GetMainAxis(GetGlobalOffset());
1977 auto localOffset = startPosition - globalMainOffset;
1978 auto index = GetNearChildByPosition(localOffset);
1979 if (index == INVALID_CHILD_INDEX) {
1980 LOGE("GetNearChildByPosition failed, localOffset = %lf not in item index [ %zu, %zu )", localOffset,
1981 startIndex_, startIndex_ + items_.size());
1982 return;
1983 }
1984 dragStartIndexPending_ = index;
1985 }
1986
ProcessDragUpdate(double dragOffset)1987 void RenderList::ProcessDragUpdate(double dragOffset)
1988 {
1989 if (!chainAnimation_) {
1990 return;
1991 }
1992
1993 if (NearZero(dragOffset)) {
1994 return;
1995 }
1996
1997 currentDelta_ = dragOffset;
1998 double delta = FlushChainAnimation();
1999 currentOffset_ += delta;
2000 if (!NearZero(delta)) {
2001 LOGE("ProcessDragUpdate delta = %lf currentOffset_ = %lf", delta, currentOffset_);
2002 }
2003 }
2004
ProcessScrollOverCallback(double velocity)2005 void RenderList::ProcessScrollOverCallback(double velocity)
2006 {
2007 if (!chainAnimation_) {
2008 LOGD("chain animation is null, no need to handle it.");
2009 return;
2010 }
2011
2012 if (NearZero(velocity)) {
2013 LOGD("velocity is zero, no need to handle in chain animation.");
2014 return;
2015 }
2016
2017 if (reachStart_) {
2018 dragStartIndexPending_ = startIndex_;
2019 } else if (reachEnd_) {
2020 dragStartIndexPending_ = startIndex_;
2021 if (!items_.empty()) {
2022 dragStartIndexPending_ += items_.size() - 1;
2023 }
2024 }
2025
2026 double delta = FlushChainAnimation();
2027 currentOffset_ += delta;
2028 if (!NearZero(delta)) {
2029 LOGE("ProcessScrollOverCallback delta = %lf currentOffset_ = %lf", delta, currentOffset_);
2030 }
2031 }
2032
InitChainAnimation(int32_t nodeCount)2033 void RenderList::InitChainAnimation(int32_t nodeCount)
2034 {
2035 auto context = GetContext().Upgrade();
2036 if (!context) {
2037 LOGE("Init chain animation failed. context is null");
2038 return;
2039 }
2040
2041 if (chainAdapter_ && chain_) {
2042 return;
2043 }
2044 chainAdapter_ = AceType::MakeRefPtr<BilateralSpringAdapter>();
2045 chain_ = AceType::MakeRefPtr<SimpleSpringChain>(chainAdapter_);
2046 const auto& property = GetChainProperty();
2047 chain_->SetFrameDelta(property.FrameDelay());
2048 if (property.StiffnessTransfer()) {
2049 chain_->SetStiffnessTransfer(AceType::MakeRefPtr<ExpParamTransfer>(property.StiffnessCoefficient()));
2050 } else {
2051 chain_->SetStiffnessTransfer(AceType::MakeRefPtr<LinearParamTransfer>(property.StiffnessCoefficient()));
2052 }
2053 if (property.DampingTransfer()) {
2054 chain_->SetDampingTransfer(AceType::MakeRefPtr<ExpParamTransfer>(property.DampingCoefficient()));
2055 } else {
2056 chain_->SetDampingTransfer(AceType::MakeRefPtr<LinearParamTransfer>(property.DampingCoefficient()));
2057 }
2058 chain_->SetControlDamping(property.ControlDamping());
2059 chain_->SetControlStiffness(property.ControlStiffness());
2060 chain_->SetDecoration(context->NormalizeToPx(property.Interval()));
2061 chain_->SetMinDecoration(context->NormalizeToPx(property.MinInterval()));
2062 chain_->SetMaxDecoration(context->NormalizeToPx(property.MaxInterval()));
2063 for (int32_t index = 0; index < nodeCount; index++) {
2064 auto node = AceType::MakeRefPtr<BilateralSpringNode>(GetContext(), index, 0.0);
2065 WeakPtr<BilateralSpringNode> nodeWeak(node);
2066 WeakPtr<SimpleSpringAdapter> adapterWeak(chainAdapter_);
2067 node->AddUpdateListener(
2068 [weak = AceType::WeakClaim(this), nodeWeak, adapterWeak](double value, double velocity) {
2069 auto renderList = weak.Upgrade();
2070 auto node = nodeWeak.Upgrade();
2071 auto adapter = adapterWeak.Upgrade();
2072 if (!renderList || !node || !adapter) {
2073 return;
2074 }
2075 renderList->MarkNeedLayout();
2076 });
2077 chainAdapter_->AddNode(node);
2078 }
2079 chainAdapter_->NotifyControlIndexChange();
2080 }
2081
GetChainDelta(int32_t index) const2082 double RenderList::GetChainDelta(int32_t index) const
2083 {
2084 if (!chainAdapter_) {
2085 return 0.0;
2086 }
2087 double value = 0.0;
2088 RefPtr<BilateralSpringNode> node;
2089 int32_t controlIndex = dragStartIndex_;
2090 int32_t baseIndex = controlIndex - chainAdapter_->GetControlIndex();
2091 auto targetIndex = std::clamp(index - baseIndex, 0, CHAIN_ANIMATION_NODE_COUNT - 1);
2092 node = AceType::DynamicCast<BilateralSpringNode>(chainAdapter_->GetNode(targetIndex));
2093 if (node) {
2094 value = node->GetValue();
2095 }
2096 LOGD("ChainDelta. controlIndex: %{public}d, index: %{public}d, value: %{public}.3lf", controlIndex, index, value);
2097 return value;
2098 }
2099
GetNearChildByPosition(double mainOffset) const2100 size_t RenderList::GetNearChildByPosition(double mainOffset) const
2101 {
2102 size_t index = startIndex_;
2103 size_t prevIndex = INVALID_CHILD_INDEX;
2104
2105 for (auto& child : items_) {
2106 auto childMainOffset = GetMainAxis(child->GetPosition());
2107
2108 if (childMainOffset > mainOffset) {
2109 return prevIndex;
2110 }
2111 prevIndex = index++;
2112 }
2113 return prevIndex;
2114 }
2115
FlushChainAnimation()2116 double RenderList::FlushChainAnimation()
2117 {
2118 if (!chainAnimation_ || !chain_ || !chainAdapter_) {
2119 return 0.0;
2120 }
2121 double deltaDistance = 0.0;
2122 bool needSetValue = false;
2123 bool overScroll = scrollable_ && scrollable_->IsSpringMotionRunning();
2124 if (chainOverScroll_ != overScroll) {
2125 if (overScroll) {
2126 const auto& springProperty = GetOverSpringProperty();
2127 if (springProperty && springProperty->IsValid()) {
2128 chain_->SetControlStiffness(springProperty->Stiffness());
2129 chain_->SetControlDamping(springProperty->Damping());
2130 }
2131 } else {
2132 chain_->SetControlStiffness(GetChainProperty().ControlStiffness());
2133 chain_->SetControlDamping(GetChainProperty().ControlDamping());
2134 }
2135 chain_->OnControlNodeChange();
2136 chainOverScroll_ = overScroll;
2137 }
2138 chain_->FlushAnimation();
2139 if (dragStartIndexPending_ != dragStartIndex_) {
2140 deltaDistance = chainAdapter_->ResetControl(dragStartIndexPending_ - dragStartIndex_);
2141 LOGD("Switch chain control node. %{public}zu -> %{public}zu, deltaDistance: %{public}.1f", dragStartIndex_,
2142 dragStartIndexPending_, deltaDistance);
2143 dragStartIndex_ = dragStartIndexPending_;
2144 chainAdapter_->SetDeltaValue(-deltaDistance);
2145 needSetValue = true;
2146 }
2147 if (!NearZero(currentDelta_)) {
2148 LOGD("Set delta chain value. delta: %{public}.1f", currentDelta_);
2149 chainAdapter_->SetDeltaValue(currentDelta_);
2150 currentDelta_ = 0.0;
2151 needSetValue = true;
2152 }
2153 if (needSetValue) {
2154 chain_->SetValue(0.0);
2155 LOGD("FlushChainAnimation: %{public}s", chainAdapter_->DumpNodes().c_str());
2156 }
2157 return deltaDistance;
2158 }
2159
UpdateAccessibilityAttr()2160 void RenderList::UpdateAccessibilityAttr()
2161 {
2162 if (!component_) {
2163 LOGE("RenderList: component is null.");
2164 return;
2165 }
2166
2167 auto accessibilityNode = GetAccessibilityNode().Upgrade();
2168 if (!accessibilityNode) {
2169 LOGD("RenderList: current accessibilityNode is null.");
2170 return;
2171 }
2172
2173 auto collectionInfo = accessibilityNode->GetCollectionInfo();
2174 size_t count = TotalCount() > 0 ? TotalCount() : 1;
2175 if (vertical_) {
2176 collectionInfo.rows = static_cast<int32_t>(count);
2177 collectionInfo.columns = 1;
2178 } else {
2179 collectionInfo.rows = 1;
2180 collectionInfo.columns = static_cast<int32_t>(count);
2181 }
2182 accessibilityNode->SetCollectionInfo(collectionInfo);
2183 accessibilityNode->SetScrollableState(true);
2184 accessibilityNode->SetActionScrollForward([weakList = AceType::WeakClaim(this)]() {
2185 auto list = weakList.Upgrade();
2186 if (list) {
2187 LOGI("Trigger ScrollForward by Accessibility.");
2188 return list->HandleActionScroll(true);
2189 }
2190 return false;
2191 });
2192 accessibilityNode->SetActionScrollBackward([weakList = AceType::WeakClaim(this)]() {
2193 auto list = weakList.Upgrade();
2194 if (list) {
2195 LOGI("Trigger ScrollBackward by Accessibility.");
2196 return list->HandleActionScroll(false);
2197 }
2198 return false;
2199 });
2200
2201 accessibilityNode->AddSupportAction(AceAction::ACTION_SCROLL_FORWARD);
2202 accessibilityNode->AddSupportAction(AceAction::ACTION_SCROLL_BACKWARD);
2203
2204 scrollFinishEventBack_ = [weakList = AceType::WeakClaim(this)] {
2205 auto list = weakList.Upgrade();
2206 if (list) {
2207 list->ModifyActionScroll();
2208 }
2209 };
2210 }
2211
UpdateAccessibilityScrollAttr()2212 void RenderList::UpdateAccessibilityScrollAttr()
2213 {
2214 auto accessibilityNode = GetAccessibilityNode().Upgrade();
2215 if (accessibilityNode) {
2216 accessibilityNode->SetListBeginIndex(firstDisplayIndex_);
2217 accessibilityNode->SetListEndIndex(lastDisplayIndex_);
2218 accessibilityNode->SetListItemCounts(items_.size());
2219 }
2220 }
2221
UpdateAccessibilityVisible()2222 void RenderList::UpdateAccessibilityVisible()
2223 {
2224 auto accessibilityNode = GetAccessibilityNode().Upgrade();
2225 if (!accessibilityNode) {
2226 return;
2227 }
2228 Offset globalOffset = GetGlobalOffset();
2229 Rect listItemRect;
2230 Rect viewPortRect = Rect(globalOffset, GetLayoutSize());
2231 for (const auto& listItem : items_) {
2232 if (!listItem || listItem->GetChildren().empty()) {
2233 continue;
2234 }
2235 // RenderListItem's accessibility node is List's in v2, see ViewStackProcessor::WrapComponents() and
2236 // RenderElement::SetAccessibilityNode
2237 auto listItemWithAccessibilityNode = listItem->GetFirstChild();
2238 auto node = listItemWithAccessibilityNode->GetAccessibilityNode().Upgrade();
2239 if (!node) {
2240 continue;
2241 }
2242 bool visible = GetVisible();
2243 if (visible) {
2244 listItemRect.SetSize(listItem->GetLayoutSize());
2245 listItemRect.SetOffset(globalOffset + listItem->GetPosition());
2246 visible = listItemRect.IsIntersectWith(viewPortRect);
2247 }
2248 listItemWithAccessibilityNode->SetAccessibilityVisible(visible);
2249 if (visible) {
2250 Rect clampRect = listItemRect.Constrain(viewPortRect);
2251 listItemWithAccessibilityNode->SetAccessibilityRect(clampRect);
2252 } else {
2253 listItem->NotifyPaintFinish();
2254 }
2255 }
2256 }
2257
ActionByScroll(bool forward,ScrollEventBack scrollEventBack)2258 bool RenderList::ActionByScroll(bool forward, ScrollEventBack scrollEventBack)
2259 {
2260 LOGI("Handle action by Scroll.");
2261 auto node = GetParent().Upgrade();
2262 while (node) {
2263 auto scroll = AceType::DynamicCast<RenderSingleChildScroll>(node);
2264 if (!scroll) {
2265 node = node->GetParent().Upgrade();
2266 continue;
2267 }
2268
2269 scroll->ScrollPage(!forward, true, scrollEventBack);
2270 return true;
2271 }
2272 return false;
2273 }
2274
HandleActionScroll(bool forward)2275 bool RenderList::HandleActionScroll(bool forward)
2276 {
2277 if (isActionByScroll_) {
2278 return ActionByScroll(forward, scrollFinishEventBack_);
2279 }
2280
2281 if (forward) {
2282 JumpToIndex(lastDisplayIndex_, DEFAULT_SOURCE);
2283 } else {
2284 JumpToIndex(startIndex_, DEFAULT_SOURCE);
2285 }
2286 if (scrollFinishEventBack_) {
2287 scrollFinishEventBack_();
2288 }
2289 return true;
2290 }
2291
ModifyActionScroll()2292 void RenderList::ModifyActionScroll()
2293 {
2294 hasActionScroll_ = true;
2295 }
2296
OnPaintFinish()2297 void RenderList::OnPaintFinish()
2298 {
2299 if (!hasActionScroll_) {
2300 return;
2301 }
2302
2303 hasActionScroll_ = false;
2304 auto context = context_.Upgrade();
2305 if (!context) {
2306 LOGE("RenderList: context is null.");
2307 return;
2308 }
2309
2310 AccessibilityEvent scrollEvent;
2311 scrollEvent.nodeId = GetAccessibilityNodeId();
2312 scrollEvent.eventType = "scrollend";
2313 context->SendEventToAccessibility(scrollEvent);
2314 }
2315
IsUseOnly()2316 bool RenderList::IsUseOnly()
2317 {
2318 return true;
2319 }
2320
PrepareRawRecognizer()2321 bool RenderList::PrepareRawRecognizer()
2322 {
2323 if (rawRecognizer_) {
2324 return true;
2325 }
2326
2327 rawRecognizer_ = AceType::MakeRefPtr<RawRecognizer>();
2328 auto weak = AceType::WeakClaim(this);
2329 rawRecognizer_->SetOnTouchDown([weak](const TouchEventInfo& info) {
2330 if (info.GetTouches().empty()) {
2331 return;
2332 }
2333 auto spThis = weak.Upgrade();
2334 if (spThis) {
2335 spThis->lastPos_ = spThis->GetMainAxis(info.GetTouches().front().GetLocalLocation());
2336 }
2337 });
2338 rawRecognizer_->SetOnTouchMove([weak](const TouchEventInfo& info) {
2339 if (info.GetTouches().empty()) {
2340 return;
2341 }
2342
2343 auto spThis = weak.Upgrade();
2344 if (!spThis || !spThis->selectedItem_) {
2345 return;
2346 }
2347 double currentPos = spThis->GetMainAxis(info.GetTouches().front().GetLocalLocation());
2348 spThis->OnSelectedItemMove(currentPos);
2349 });
2350 rawRecognizer_->SetOnTouchUp([weak](const TouchEventInfo& info) {
2351 auto spThis = weak.Upgrade();
2352 if (spThis) {
2353 spThis->OnSelectedItemStopMoving(false);
2354 }
2355 });
2356 rawRecognizer_->SetOnTouchCancel([weak](const TouchEventInfo& info) {
2357 auto spThis = weak.Upgrade();
2358 if (spThis) {
2359 spThis->OnSelectedItemStopMoving(true);
2360 }
2361 });
2362
2363 return true;
2364 }
2365
OnSelectedItemMove(double position)2366 void RenderList::OnSelectedItemMove(double position)
2367 {
2368 double deltaPos = position - lastPos_;
2369
2370 movingForward_ = LessOrEqual(deltaPos, 0.0);
2371 selectedItemMainAxis_ += deltaPos;
2372 deltaPos = -deltaPos;
2373 if (LessOrEqual(selectedItemMainAxis_, 0.0)) {
2374 selectedItemMainAxis_ = 0.0;
2375 } else {
2376 double maxMainSize = GetMainSize(GetLayoutSize());
2377 double mainSize = GetMainSize(selectedItem_->GetLayoutSize());
2378 if (GreatOrEqual(selectedItemMainAxis_ + mainSize, maxMainSize)) {
2379 selectedItemMainAxis_ = maxMainSize - mainSize;
2380 } else {
2381 deltaPos = 0.0;
2382 lastPos_ = position;
2383 }
2384 }
2385
2386 if (!NearZero(deltaPos)) {
2387 currentOffset_ += deltaPos;
2388 autoScrollingForItemMove_ = true;
2389 }
2390
2391 MarkNeedLayout();
2392 }
2393
OnSelectedItemStopMoving(bool canceled)2394 void RenderList::OnSelectedItemStopMoving(bool canceled)
2395 {
2396 if (!canceled && targetIndex_ != selectedItemIndex_) {
2397 auto from = static_cast<int32_t>(selectedItemIndex_);
2398 auto to = static_cast<int32_t>(targetIndex_);
2399 LOGI("Moving item from %{private}d to %{private}d", from, to);
2400 if (!ResumeEventCallback(component_, &ListComponent::GetOnItemMove, false, from, to)) {
2401 LOGI("User canceled, stop moving item");
2402 }
2403 }
2404
2405 if (selectedItemIndex_ < startIndex_ || selectedItemIndex_ >= startIndex_ + items_.size()) {
2406 RecycleListItem(selectedItemIndex_);
2407 }
2408
2409 targetIndex_ = INITIAL_CHILD_INDEX;
2410 selectedItemIndex_ = INITIAL_CHILD_INDEX;
2411 selectedItem_ = nullptr;
2412 MarkNeedLayout();
2413 }
2414
CreateDragDropRecognizer()2415 void RenderList::CreateDragDropRecognizer()
2416 {
2417 if (dragDropGesture_) {
2418 return;
2419 }
2420
2421 auto longPressRecognizer =
2422 AceType::MakeRefPtr<OHOS::Ace::LongPressRecognizer>(context_, DEFAULT_DURATION, DEFAULT_FINGERS, false);
2423 longPressRecognizer->SetOnAction([weakRenderList = AceType::WeakClaim(this)](const GestureEvent& info) {
2424 auto renderList = weakRenderList.Upgrade();
2425 if (!renderList) {
2426 LOGE("LongPress action RenderList is null.");
2427 return;
2428 }
2429 renderList->scrollable_->MarkAvailable(false);
2430 });
2431 PanDirection panDirection;
2432 auto panRecognizer =
2433 AceType::MakeRefPtr<OHOS::Ace::PanRecognizer>(context_, DEFAULT_FINGERS, panDirection, DEFAULT_DISTANCE);
2434 panRecognizer->SetOnActionStart([weakRenderList = AceType::WeakClaim(this), context = context_,
2435 onItemDragStart = onItemDragStart_](const GestureEvent& info) {
2436 if (onItemDragStart) {
2437 auto pipelineContext = context.Upgrade();
2438 if (!pipelineContext) {
2439 LOGE("Context is null.");
2440 return;
2441 }
2442
2443 auto renderList = weakRenderList.Upgrade();
2444 if (!renderList) {
2445 LOGE("RenderList is null.");
2446 return;
2447 }
2448
2449 ItemDragInfo dragInfo;
2450 dragInfo.SetX(info.GetGlobalPoint().GetX());
2451 dragInfo.SetY(info.GetGlobalPoint().GetY());
2452
2453 Point point = info.GetGlobalPoint() - renderList->GetGlobalOffset();
2454 auto listItem = renderList->FindCurrentListItem(point);
2455 if (!listItem) {
2456 LOGW("There is no listitem at the point.");
2457 return;
2458 }
2459
2460 if (!listItem->IsMovable()) {
2461 LOGI("This list item is not movable.");
2462 return;
2463 }
2464 renderList->selectedDragItem_ = listItem;
2465 renderList->selectedItemIndex_ = renderList->GetIndexByListItem(listItem);
2466 renderList->selectedDragItem_->SetHidden(true);
2467 renderList->MarkNeedLayout();
2468
2469 auto customComponent =
2470 DynamicCast<Component>(onItemDragStart(dragInfo, int32_t(renderList->selectedItemIndex_)));
2471 if (!customComponent) {
2472 LOGE("Custom component is null.");
2473 return;
2474 }
2475 auto stackElement = pipelineContext->GetLastStack();
2476 auto positionedComponent = AceType::MakeRefPtr<PositionedComponent>(customComponent);
2477 positionedComponent->SetTop(Dimension(listItem->GetGlobalOffset().GetY()));
2478 positionedComponent->SetLeft(Dimension(listItem->GetGlobalOffset().GetX()));
2479 renderList->SetBetweenItemAndBuilder(
2480 Offset(info.GetGlobalPoint().GetX() - listItem->GetGlobalOffset().GetX(),
2481 info.GetGlobalPoint().GetY() - listItem->GetGlobalOffset().GetY()));
2482
2483 auto updatePosition = [weak = weakRenderList](
2484 const std::function<void(const Dimension&, const Dimension&)>& func) {
2485 auto renderList = weak.Upgrade();
2486 if (!renderList) {
2487 return;
2488 }
2489 renderList->SetUpdateBuilderFuncId(func);
2490 };
2491
2492 positionedComponent->SetUpdatePositionFuncId(updatePosition);
2493 stackElement->PushComponent(positionedComponent);
2494 renderList->hasDragItem_ = true;
2495 }
2496 });
2497 panRecognizer->SetOnActionUpdate(
2498 [weakRenderList = AceType::WeakClaim(this), context = context_](const GestureEvent& info) {
2499 auto pipelineContext = context.Upgrade();
2500 if (!pipelineContext) {
2501 LOGE("Context is null.");
2502 return;
2503 }
2504
2505 auto renderList = weakRenderList.Upgrade();
2506 if (!renderList) {
2507 LOGE("RenderList is null.");
2508 return;
2509 }
2510
2511 ItemDragInfo dragInfo;
2512 dragInfo.SetX(info.GetGlobalPoint().GetX());
2513 dragInfo.SetY(info.GetGlobalPoint().GetY());
2514
2515 Point point = info.GetGlobalPoint() - renderList->GetBetweenItemAndBuilder();
2516 if (renderList->GetUpdateBuilderFuncId()) {
2517 renderList->GetUpdateBuilderFuncId()(Dimension(point.GetX()), Dimension(point.GetY()));
2518 }
2519
2520 auto targetRenderlist = renderList->FindTargetRenderNode<V2::RenderList>(pipelineContext, info);
2521 auto preTargetRenderlist = renderList->GetPreTargetRenderList();
2522
2523 if (preTargetRenderlist == targetRenderlist) {
2524 if (targetRenderlist && targetRenderlist->GetOnItemDragMove()) {
2525 Point point = info.GetGlobalPoint() - targetRenderlist->GetGlobalOffset();
2526 auto newListItem = targetRenderlist->FindCurrentListItem(point);
2527 if (static_cast<int32_t>(targetRenderlist->GetIndexByListItem(newListItem)) > -1) {
2528 renderList->insertItemIndex_ = targetRenderlist->GetIndexByListItem(newListItem);
2529 }
2530 if (targetRenderlist == renderList) {
2531 (targetRenderlist->GetOnItemDragMove())(dragInfo,
2532 static_cast<int32_t>(renderList->selectedItemIndex_), renderList->insertItemIndex_);
2533 } else {
2534 (targetRenderlist->GetOnItemDragMove())(dragInfo, -1, renderList->insertItemIndex_);
2535 }
2536 }
2537 return;
2538 }
2539 if (preTargetRenderlist) {
2540 if (preTargetRenderlist->GetOnItemDragLeave()) {
2541 (preTargetRenderlist->GetOnItemDragLeave())(
2542 dragInfo, static_cast<int32_t>(renderList->selectedItemIndex_));
2543 }
2544 }
2545 if (targetRenderlist) {
2546 if (targetRenderlist->GetOnItemDragEnter()) {
2547 (targetRenderlist->GetOnItemDragEnter())(dragInfo);
2548 }
2549 }
2550 renderList->SetPreTargetRenderList(targetRenderlist);
2551 });
2552 panRecognizer->SetOnActionEnd([weakRenderList = AceType::WeakClaim(this), context = context_](
2553 const GestureEvent& info) {
2554 auto pipelineContext = context.Upgrade();
2555 if (!pipelineContext) {
2556 LOGE("Context is null.");
2557 return;
2558 }
2559
2560 auto renderList = weakRenderList.Upgrade();
2561 if (!renderList) {
2562 LOGE("RenderList is null.");
2563 return;
2564 }
2565 if (!renderList->selectedDragItem_ || !renderList->selectedDragItem_->IsMovable()) {
2566 return;
2567 }
2568
2569 ItemDragInfo dragInfo;
2570 dragInfo.SetX(info.GetGlobalPoint().GetX());
2571 dragInfo.SetY(info.GetGlobalPoint().GetY());
2572 if (renderList->hasDragItem_) {
2573 auto stackElement = pipelineContext->GetLastStack();
2574 stackElement->PopComponent();
2575 renderList->hasDragItem_ = false;
2576 }
2577
2578 ACE_DCHECK(renderList->GetPreTargetRenderList() ==
2579 renderList->FindTargetRenderNode<V2::RenderList>(pipelineContext, info));
2580 auto targetRenderlist = renderList->GetPreTargetRenderList();
2581
2582 if (!targetRenderlist) {
2583 (renderList->GetOnItemDrop())(dragInfo, static_cast<int32_t>(renderList->selectedItemIndex_), -1, true);
2584 renderList->SetPreTargetRenderList(nullptr);
2585 renderList->selectedDragItem_->SetHidden(false);
2586 renderList->MarkNeedLayout();
2587 return;
2588 }
2589
2590 renderList->selectedDragItem_->SetHidden(false);
2591 if (targetRenderlist->GetOnItemDrop()) {
2592 Point point = info.GetGlobalPoint() - targetRenderlist->GetGlobalOffset();
2593 auto newListItem = targetRenderlist->FindCurrentListItem(point);
2594 if (static_cast<int32_t>(targetRenderlist->GetIndexByListItem(newListItem)) > -1) {
2595 renderList->insertItemIndex_ = static_cast<size_t>(targetRenderlist->GetIndexByListItem(newListItem));
2596 }
2597 if (targetRenderlist == renderList) {
2598 int32_t from = static_cast<int32_t>(renderList->selectedItemIndex_);
2599 int32_t to = static_cast<int32_t>(renderList->insertItemIndex_);
2600 auto moveRes =
2601 ResumeEventCallback(renderList->component_, &ListComponent::GetOnItemMove, true, from, to);
2602 (targetRenderlist->GetOnItemDrop())(dragInfo, from, to, moveRes);
2603 renderList->MarkNeedLayout();
2604 } else {
2605 (targetRenderlist->GetOnItemDrop())(dragInfo, -1, renderList->insertItemIndex_, true);
2606 targetRenderlist->MarkNeedLayout();
2607 }
2608 }
2609 renderList->SetPreTargetRenderList(nullptr);
2610 renderList->scrollable_->MarkAvailable(true);
2611 });
2612 panRecognizer->SetOnActionCancel([weakRenderList = AceType::WeakClaim(this), context = context_]() {
2613 auto pipelineContext = context.Upgrade();
2614 if (!pipelineContext) {
2615 LOGE("Context is null.");
2616 return;
2617 }
2618
2619 auto renderList = weakRenderList.Upgrade();
2620 if (!renderList) {
2621 LOGE("RenderList is null.");
2622 return;
2623 }
2624 if (!renderList->selectedDragItem_ || !renderList->selectedDragItem_->IsMovable()) {
2625 return;
2626 }
2627
2628 if (renderList->hasDragItem_) {
2629 auto stackElement = pipelineContext->GetLastStack();
2630 stackElement->PopComponent();
2631 renderList->hasDragItem_ = false;
2632 }
2633
2634 renderList->SetPreTargetRenderList(nullptr);
2635 renderList->selectedDragItem_->SetHidden(false);
2636 renderList->scrollable_->MarkAvailable(true);
2637 renderList->MarkNeedLayout();
2638 });
2639 std::vector<RefPtr<GestureRecognizer>> recognizers { longPressRecognizer, panRecognizer };
2640 dragDropGesture_ = AceType::MakeRefPtr<OHOS::Ace::SequencedRecognizer>(GetContext(), recognizers);
2641 }
2642
FindCurrentListItem(const Point & point)2643 RefPtr<RenderListItem> RenderList::FindCurrentListItem(const Point& point)
2644 {
2645 const auto& children = GetChildren();
2646 for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
2647 auto& child = *iter;
2648 for (auto& rect : child->GetTouchRectList()) {
2649 if (rect.IsInRegion(point)) {
2650 return AceType::DynamicCast<RenderListItem>(child);
2651 }
2652 }
2653 }
2654 return nullptr;
2655 }
2656
CalculateSelectedIndex(const RefPtr<RenderList> targetRenderlist,const GestureEvent & info,Size & selectedItemSize)2657 size_t RenderList::CalculateSelectedIndex(
2658 const RefPtr<RenderList> targetRenderlist, const GestureEvent& info, Size& selectedItemSize)
2659 {
2660 auto listItem = targetRenderlist->FindTargetRenderNode<RenderListItem>(context_.Upgrade(), info);
2661 if (listItem) {
2662 selectedItemSize = listItem->GetLayoutSize();
2663 return targetRenderlist->GetIndexByListItem(listItem);
2664 }
2665
2666 return DEFAULT_INDEX;
2667 }
2668
CalculateInsertIndex(const RefPtr<RenderList> targetRenderlist,const GestureEvent & info,Size selectedItemSize)2669 int32_t RenderList::CalculateInsertIndex(
2670 const RefPtr<RenderList> targetRenderlist, const GestureEvent& info, Size selectedItemSize)
2671 {
2672 if (targetRenderlist->TotalCount() == 0) {
2673 return 0;
2674 }
2675
2676 auto listItem = targetRenderlist->FindTargetRenderNode<RenderListItem>(context_.Upgrade(), info);
2677 if (!listItem) {
2678 GestureEvent newEvent = info;
2679 while (!listItem) {
2680 if (FindTargetRenderNode<V2::RenderList>(context_.Upgrade(), newEvent) != targetRenderlist) {
2681 break;
2682 }
2683 double newX = vertical_ ? newEvent.GetGlobalPoint().GetX()
2684 : newEvent.GetGlobalPoint().GetX() - selectedItemSize.Width();
2685 double newY = vertical_ ? newEvent.GetGlobalPoint().GetY() - selectedItemSize.Height()
2686 : newEvent.GetGlobalPoint().GetY();
2687 newEvent.SetGlobalPoint(Point(newX, newY));
2688 listItem = targetRenderlist->FindTargetRenderNode<RenderListItem>(context_.Upgrade(), newEvent);
2689 }
2690 if (!listItem) {
2691 return 0;
2692 }
2693 if (static_cast<int32_t>(targetRenderlist->GetIndexByListItem(listItem)) > -1) {
2694 return static_cast<int32_t>(targetRenderlist->GetIndexByListItem(listItem)) + 1;
2695 }
2696 return DEFAULT_INDEX_VALUE;
2697 }
2698
2699 if (static_cast<int32_t>(targetRenderlist->GetIndexByListItem(listItem)) > -1) {
2700 return static_cast<int32_t>(targetRenderlist->GetIndexByListItem(listItem));
2701 }
2702
2703 return DEFAULT_INDEX_VALUE;
2704 }
2705
IsAxisScrollable(AxisDirection direction)2706 bool RenderList::IsAxisScrollable(AxisDirection direction)
2707 {
2708 return (((AxisEvent::IsDirectionUp(direction) || AxisEvent::IsDirectionLeft(direction)) && !reachStart_) ||
2709 ((AxisEvent::IsDirectionDown(direction) || AxisEvent::IsDirectionRight(direction)) && !reachEnd_));
2710 }
2711
HandleAxisEvent(const AxisEvent & event)2712 void RenderList::HandleAxisEvent(const AxisEvent& event) {}
2713
HandleMouseEvent(const MouseEvent & event)2714 bool RenderList::HandleMouseEvent(const MouseEvent& event)
2715 {
2716 if (!isMultiSelectable_) {
2717 return false;
2718 }
2719 scrollable_->MarkAvailable(false);
2720
2721 if (event.button == MouseButton::LEFT_BUTTON) {
2722 if (event.action == MouseAction::PRESS) {
2723 Point mousePoint(event.GetOffset().GetX(), event.GetOffset().GetY());
2724 auto listItem = FindChildNodeOfClass<RenderListItem>(mousePoint, mousePoint);
2725 if (listItem && listItem->IsDragStart()) {
2726 forbidMultiSelect_ = true;
2727 }
2728 } else if (event.action == MouseAction::RELEASE) {
2729 forbidMultiSelect_ = false;
2730 }
2731 }
2732
2733 if (forbidMultiSelect_) {
2734 return false;
2735 }
2736
2737 if (event.action == MouseAction::HOVER_EXIT) {
2738 mouseIsHover_ = false;
2739 } else {
2740 mouseIsHover_ = true;
2741 }
2742
2743 auto context = context_.Upgrade();
2744 if (context) {
2745 context->SubscribeCtrlA([wp = AceType::WeakClaim(this)]() {
2746 auto sp = wp.Upgrade();
2747 if (sp) {
2748 if (sp->mouseIsHover_ == true) {
2749 sp->MultiSelectAllWhenCtrlA();
2750 } else {
2751 sp->ClearMultiSelect();
2752 sp->MarkNeedRender();
2753 }
2754 }
2755 });
2756 } else {
2757 LOGE("context is null");
2758 return false;
2759 }
2760
2761 if (context->IsCtrlDown()) {
2762 if (context->IsKeyboardA()) {
2763 MultiSelectAllWhenCtrlA();
2764 return true;
2765 }
2766 HandleMouseEventWhenCtrlDown(event);
2767 return true;
2768 }
2769 selectedItemsWithCtrl_.clear();
2770
2771 if (context->IsShiftDown()) {
2772 HandleMouseEventWhenShiftDown(event);
2773 return true;
2774 }
2775 firstItemWithShift_ = nullptr;
2776
2777 HandleMouseEventWithoutKeyboard(event);
2778 return true;
2779 }
2780
ClearMultiSelect()2781 void RenderList::ClearMultiSelect()
2782 {
2783 for (const auto& listItem : items_) {
2784 if (!listItem) {
2785 continue;
2786 }
2787 listItem->MarkIsSelected(false);
2788 }
2789 }
2790
MultiSelectWithoutKeyboard(const Rect & selectedZone)2791 void RenderList::MultiSelectWithoutKeyboard(const Rect& selectedZone)
2792 {
2793 if (!selectedZone.IsValid()) {
2794 Point mousePoint(selectedZone.GetOffset().GetX(), selectedZone.GetOffset().GetY());
2795 auto listItem = FindChildNodeOfClass<RenderListItem>(mousePoint, mousePoint);
2796 if (!listItem) {
2797 return;
2798 }
2799 if (!listItem->GetSelectable()) {
2800 return;
2801 }
2802 listItem->MarkIsSelected(true);
2803 if (listItem->GetOnSelectId()) {
2804 (listItem->GetOnSelectId())(listItem->IsSelected());
2805 }
2806 return;
2807 }
2808
2809 for (const auto& listItem : items_) {
2810 if (!listItem) {
2811 continue;
2812 }
2813 if (!listItem->GetSelectable()) {
2814 continue;
2815 }
2816 if (!selectedZone.IsIntersectWith(listItem->GetPaintRect())) {
2817 listItem->MarkIsSelected(false);
2818 if (listItem->GetOnSelectId()) {
2819 (listItem->GetOnSelectId())(listItem->IsSelected());
2820 }
2821 continue;
2822 }
2823 listItem->MarkIsSelected(true);
2824 if (listItem->GetOnSelectId()) {
2825 (listItem->GetOnSelectId())(listItem->IsSelected());
2826 }
2827 }
2828 }
2829
HandleMouseEventWithoutKeyboard(const MouseEvent & event)2830 void RenderList::HandleMouseEventWithoutKeyboard(const MouseEvent& event)
2831 {
2832 if (event.button == MouseButton::LEFT_BUTTON) {
2833 if (event.action == MouseAction::PRESS) {
2834 ClearMultiSelect();
2835 mouseStartOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2836 mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2837 auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
2838 MultiSelectWithoutKeyboard(selectedZone);
2839 MarkNeedRender();
2840 } else if (event.action == MouseAction::MOVE) {
2841 mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2842 auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
2843 MultiSelectWithoutKeyboard(selectedZone);
2844 MarkNeedRender();
2845 } else if (event.action == MouseAction::RELEASE) {
2846 mouseStartOffset_ = Offset(0.0, 0.0);
2847 mouseEndOffset_ = Offset(0.0, 0.0);
2848 MarkNeedRender();
2849 }
2850 }
2851 }
2852
GetPressItemWhenShiftDown(const Rect & selectedZone)2853 RefPtr<RenderListItem> RenderList::GetPressItemWhenShiftDown(const Rect& selectedZone)
2854 {
2855 if (!selectedZone.IsValid()) {
2856 Point mousePoint(selectedZone.GetOffset().GetX(), selectedZone.GetOffset().GetY());
2857 auto listItem = FindChildNodeOfClass<RenderListItem>(mousePoint, mousePoint);
2858 if (!listItem) {
2859 return nullptr;
2860 }
2861 if (!listItem->GetSelectable()) {
2862 return nullptr;
2863 }
2864 return listItem;
2865 }
2866 return nullptr;
2867 }
2868
MultiSelectWhenShiftDown(const Rect & selectedZone)2869 void RenderList::MultiSelectWhenShiftDown(const Rect& selectedZone)
2870 {
2871 for (const auto& listItem : items_) {
2872 if (!listItem) {
2873 continue;
2874 }
2875 if (!listItem->GetSelectable()) {
2876 continue;
2877 }
2878 if (!selectedZone.IsIntersectWith(listItem->GetPaintRect())) {
2879 continue;
2880 }
2881 listItem->MarkIsSelected(true);
2882 if (listItem->GetOnSelectId()) {
2883 (listItem->GetOnSelectId())(listItem->IsSelected());
2884 }
2885 }
2886 }
2887
HandleMouseEventWhenShiftDown(const MouseEvent & event)2888 void RenderList::HandleMouseEventWhenShiftDown(const MouseEvent& event)
2889 {
2890 if (event.button == MouseButton::LEFT_BUTTON) {
2891 if (event.action == MouseAction::PRESS) {
2892 mouseStartOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2893 mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2894 auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
2895 if (firstItemWithShift_ == nullptr) {
2896 firstItemWithShift_ = GetPressItemWhenShiftDown(selectedZone);
2897 }
2898 secondItemWithShift_ = GetPressItemWhenShiftDown(selectedZone);
2899 MultiSelectAllInRange(firstItemWithShift_, secondItemWithShift_);
2900 MarkNeedRender();
2901 } else if (event.action == MouseAction::MOVE) {
2902 mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2903 auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
2904 MultiSelectWhenShiftDown(selectedZone);
2905 MarkNeedRender();
2906 } else if (event.action == MouseAction::RELEASE) {
2907 mouseStartOffset_ = Offset(0.0, 0.0);
2908 mouseEndOffset_ = Offset(0.0, 0.0);
2909 MarkNeedRender();
2910 }
2911 }
2912 }
2913
MultiSelectAllInRange(const RefPtr<RenderListItem> & firstItem,const RefPtr<RenderListItem> & secondItem)2914 void RenderList::MultiSelectAllInRange(
2915 const RefPtr<RenderListItem>& firstItem, const RefPtr<RenderListItem>& secondItem)
2916 {
2917 ClearMultiSelect();
2918 if (!firstItem) {
2919 return;
2920 }
2921
2922 if (!secondItem) {
2923 firstItem->MarkIsSelected(true);
2924 if (firstItem->GetOnSelectId()) {
2925 (firstItem->GetOnSelectId())(firstItem->IsSelected());
2926 }
2927 return;
2928 }
2929
2930 auto fromItemIndex = std::min(GetIndexByListItem(firstItem), GetIndexByListItem(secondItem));
2931 auto toItemIndex = std::max(GetIndexByListItem(firstItem), GetIndexByListItem(secondItem));
2932
2933 for (const auto& listItem : items_) {
2934 if (!listItem) {
2935 continue;
2936 }
2937 if (!listItem->GetSelectable()) {
2938 continue;
2939 }
2940
2941 auto nowIndex = GetIndexByListItem(listItem);
2942 if (nowIndex <= toItemIndex && nowIndex >= fromItemIndex) {
2943 listItem->MarkIsSelected(true);
2944 if (listItem->GetOnSelectId()) {
2945 (listItem->GetOnSelectId())(listItem->IsSelected());
2946 }
2947 }
2948 }
2949 }
2950
MultiSelectWhenCtrlDown(const Rect & selectedZone)2951 void RenderList::MultiSelectWhenCtrlDown(const Rect& selectedZone)
2952 {
2953 if (!selectedZone.IsValid()) {
2954 Point mousePoint(selectedZone.GetOffset().GetX(), selectedZone.GetOffset().GetY());
2955 auto listItem = FindChildNodeOfClass<RenderListItem>(mousePoint, mousePoint);
2956 if (!listItem) {
2957 return;
2958 }
2959 if (!listItem->GetSelectable()) {
2960 return;
2961 }
2962
2963 if (selectedItemsWithCtrl_.find(listItem) != selectedItemsWithCtrl_.end()) {
2964 listItem->MarkIsSelected(false);
2965 } else {
2966 listItem->MarkIsSelected(true);
2967 }
2968
2969 if (listItem->GetOnSelectId()) {
2970 (listItem->GetOnSelectId())(listItem->IsSelected());
2971 }
2972 return;
2973 }
2974
2975 for (const auto& listItem : items_) {
2976 if (!listItem) {
2977 continue;
2978 }
2979 if (!listItem->GetSelectable()) {
2980 continue;
2981 }
2982 if (!selectedZone.IsIntersectWith(listItem->GetPaintRect())) {
2983 if (selectedItemsWithCtrl_.find(listItem) != selectedItemsWithCtrl_.end()) {
2984 listItem->MarkIsSelected(true);
2985 } else {
2986 listItem->MarkIsSelected(false);
2987 }
2988 if (listItem->GetOnSelectId()) {
2989 (listItem->GetOnSelectId())(listItem->IsSelected());
2990 }
2991 continue;
2992 }
2993
2994 if (selectedItemsWithCtrl_.find(listItem) != selectedItemsWithCtrl_.end()) {
2995 listItem->MarkIsSelected(false);
2996 } else {
2997 listItem->MarkIsSelected(true);
2998 }
2999
3000 if (listItem->GetOnSelectId()) {
3001 (listItem->GetOnSelectId())(listItem->IsSelected());
3002 }
3003 }
3004 }
3005
HandleMouseEventWhenCtrlDown(const MouseEvent & event)3006 void RenderList::HandleMouseEventWhenCtrlDown(const MouseEvent& event)
3007 {
3008 if (event.button == MouseButton::LEFT_BUTTON) {
3009 if (event.action == MouseAction::PRESS) {
3010 mouseStartOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
3011 mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
3012 auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
3013 MultiSelectWhenCtrlDown(selectedZone);
3014 MarkNeedRender();
3015 } else if (event.action == MouseAction::MOVE) {
3016 mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
3017 auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
3018 MultiSelectWhenCtrlDown(selectedZone);
3019 MarkNeedRender();
3020 } else if (event.action == MouseAction::RELEASE) {
3021 mouseStartOffset_ = Offset(0.0, 0.0);
3022 mouseEndOffset_ = Offset(0.0, 0.0);
3023 MarkNeedRender();
3024 CollectSelectedItems();
3025 }
3026 }
3027 }
3028
CollectSelectedItems()3029 void RenderList::CollectSelectedItems()
3030 {
3031 selectedItemsWithCtrl_.clear();
3032 for (const auto& listItem : items_) {
3033 if (!listItem) {
3034 continue;
3035 }
3036 if (!listItem->GetSelectable()) {
3037 continue;
3038 }
3039 if (listItem->IsSelected()) {
3040 selectedItemsWithCtrl_.insert(listItem);
3041 }
3042 }
3043 }
3044
MultiSelectAllWhenCtrlA()3045 void RenderList::MultiSelectAllWhenCtrlA()
3046 {
3047 for (const auto& listItem : items_) {
3048 if (!listItem) {
3049 continue;
3050 }
3051 if (!listItem->GetSelectable()) {
3052 continue;
3053 }
3054 listItem->MarkIsSelected(true);
3055 if (listItem->GetOnSelectId()) {
3056 (listItem->GetOnSelectId())(listItem->IsSelected());
3057 }
3058 }
3059 MarkNeedRender();
3060 }
3061
RequestNextFocus(bool vertical,bool reverse)3062 int32_t RenderList::RequestNextFocus(bool vertical, bool reverse)
3063 {
3064 bool rightToLeft_ = false;
3065 int32_t moveStep = DIRECTION_MAP.at(rightToLeft_).at(vertical_).at(vertical).at(reverse);
3066 if (moveStep == STEP_INVALID) {
3067 return -1;
3068 }
3069 focusIndex_ += moveStep;
3070 return focusIndex_;
3071 }
3072
ProvideRestoreInfo()3073 std::string RenderList::ProvideRestoreInfo()
3074 {
3075 if (firstDisplayIndex_ > 0) {
3076 return std::to_string(firstDisplayIndex_);
3077 }
3078 return "";
3079 }
3080
ApplyRestoreInfo()3081 void RenderList::ApplyRestoreInfo()
3082 {
3083 if (GetRestoreInfo().empty()) {
3084 return;
3085 }
3086 startIndex_ = static_cast<size_t>(StringUtils::StringToInt(GetRestoreInfo()));
3087 SetRestoreInfo("");
3088 }
3089
LayoutChild(RefPtr<RenderNode> child,double referencePos,bool forward)3090 void RenderList::LayoutChild(RefPtr<RenderNode> child, double referencePos, bool forward)
3091 {
3092 auto innerLayout = MakeInnerLayout();
3093 auto renderNode = child;
3094 RefPtr<RenderListItemGroup> listItemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
3095 if (listItemGroup) {
3096 renderNode = listItemGroup->GetRenderNode();
3097 ListItemLayoutParam param = {
3098 .startCacheCount = (cachedCount_ > 0 && !isLaneList_) ? cachedCount_ - startCachedCount_ : 0,
3099 .endCacheCount = (cachedCount_ > 0 && !isLaneList_) ? cachedCount_ - endCachedCount_ : 0,
3100 .startMainPos = (cachedCount_ == 0 || isLaneList_) ? startMainPos_ : 0,
3101 .endMainPos = (cachedCount_ == 0 || isLaneList_) ? endMainPos_ : mainSize_,
3102 .listMainSize = mainSize_,
3103 .referencePos = referencePos,
3104 .maxLaneLength = isLaneList_ ? maxLaneLength_ : 0.0,
3105 .forwardLayout = forward,
3106 .isVertical = vertical_,
3107 .sticky = sticky_,
3108 .lanes = isLaneList_ ? lanes_ : 1,
3109 .align = component_->GetAlignListItemAlign(),
3110 };
3111 listItemGroup->SetItemGroupLayoutParam(param);
3112 listItemGroup->SetNeedLayoutDeep();
3113 } else if (isLaneList_) {
3114 innerLayout = MakeInnerLayoutForLane();
3115 }
3116 if (renderNode) {
3117 renderNode->Layout(innerLayout);
3118 }
3119 }
3120
PaintChild(const RefPtr<RenderNode> & child,RenderContext & context,const Offset & offset)3121 void RenderList::PaintChild(const RefPtr<RenderNode>& child, RenderContext& context, const Offset& offset)
3122 {
3123 RefPtr<RenderListItemGroup> listItemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
3124 if (listItemGroup) {
3125 auto renderNode = listItemGroup->GetRenderNode();
3126 RenderNode::PaintChild(renderNode, context, offset);
3127 } else {
3128 RenderNode::PaintChild(child, context, offset);
3129 }
3130 }
3131
SetChildPosition(RefPtr<RenderNode> child,const Offset & offset)3132 void RenderList::SetChildPosition(RefPtr<RenderNode> child, const Offset& offset)
3133 {
3134 auto renderNode = child;
3135 RefPtr<RenderListItemGroup> listItemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
3136 if (listItemGroup) {
3137 renderNode = listItemGroup->GetRenderNode();
3138 }
3139 if (renderNode) {
3140 renderNode->SetPosition(offset);
3141 }
3142 }
3143
AddChildItem(RefPtr<RenderNode> child)3144 void RenderList::AddChildItem(RefPtr<RenderNode> child)
3145 {
3146 auto renderNode = child;
3147 RefPtr<RenderListItemGroup> listItemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
3148 if (listItemGroup) {
3149 renderNode = listItemGroup->GetRenderNode();
3150 }
3151 AddChild(renderNode);
3152 }
3153
SizeChangeOffset(double newWindowHeight)3154 void RenderList::SizeChangeOffset(double newWindowHeight)
3155 {
3156 LOGD("list newWindowHeight = %{public}f", newWindowHeight);
3157 auto context = context_.Upgrade();
3158 if (!context) {
3159 return;
3160 }
3161 auto textFieldManager = AceType::DynamicCast<TextFieldManager>(context->GetTextFieldManager());
3162 // only need to offset vertical lists
3163 if (textFieldManager && vertical_) {
3164 // only when textField is onFocus
3165 if (!textFieldManager->GetOnFocusTextField().Upgrade()) {
3166 return;
3167 }
3168 auto position = textFieldManager->GetClickPosition().GetY();
3169 double offset = newWindowHeight - position;
3170 if (LessOrEqual(offset, 0.0)) {
3171 // negative offset to scroll down
3172 currentOffset_ += offset;
3173 startIndexOffset_ += offset;
3174 }
3175 LOGD("size change offset applied, %{public}f", offset);
3176 }
3177 }
3178
AdjustForReachEnd(double mainSize,double curMainPos)3179 void RenderList::AdjustForReachEnd(double mainSize, double curMainPos)
3180 {
3181 double delta = mainSize - curMainPos;
3182 for (auto rit = items_.rbegin(); rit != items_.rend(); rit++) {
3183 auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(*rit);
3184 if (itemGroup) {
3185 double size = GetMainSize(itemGroup->GetLayoutSize());
3186 LayoutChild(itemGroup, itemGroup->GetReferencePos() + delta, itemGroup->IsForwardLayout());
3187 double newSize = GetMainSize(itemGroup->GetLayoutSize());
3188 delta -= (newSize - size);
3189 }
3190 }
3191 currentOffset_ += delta;
3192 }
3193
AdjustForReachStart(double & curMainPos)3194 void RenderList::AdjustForReachStart(double& curMainPos)
3195 {
3196 double delta = currentOffset_;
3197 for (const auto& child : items_) {
3198 auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
3199 if (itemGroup) {
3200 double size = GetMainSize(itemGroup->GetLayoutSize());
3201 LayoutChild(itemGroup, itemGroup->GetReferencePos() - delta, itemGroup->IsForwardLayout());
3202 double newSize = GetMainSize(itemGroup->GetLayoutSize());
3203 delta -= (newSize - size);
3204 }
3205 }
3206 curMainPos -= delta;
3207 }
3208
3209 } // namespace OHOS::Ace::V2
3210