1 /*
2 * Copyright (c) 2021-2024 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 float SCROLL_MAX_TIME = 300.0f; // Scroll Animate max time 0.3 second
46 constexpr int32_t SCROLL_FROM_JUMP = 3;
47 constexpr int32_t DEFAULT_FINGERS = 1;
48 constexpr int32_t DEFAULT_DURATION = 200;
49 constexpr int32_t DEFAULT_DISTANCE = 0;
50
51 constexpr bool DIR_HORIZONTAL = false;
52 constexpr bool DIR_VERTICAL = true;
53 constexpr bool DIR_FORWARD = false;
54 constexpr bool DIR_REVERSE = true;
55 constexpr int32_t STEP_FORWARD = 1;
56 constexpr int32_t STEP_BACK = -1;
57 constexpr int32_t STEP_INVALID = 10;
58 constexpr int32_t CENTER_ALIGN_DIVIDER = 2;
59
60 // IsRightToLeft | IsListVertical | IsDirectionVertical | IsDirectionReverse
61 const std::map<bool, std::map<bool, std::map<bool, std::map<bool, int32_t>>>> DIRECTION_MAP = {
62 { false, // RTL is false
63 { { false, { { DIR_HORIZONTAL, { { DIR_FORWARD, STEP_FORWARD }, { DIR_REVERSE, STEP_BACK } } },
64 { DIR_VERTICAL, { { DIR_FORWARD, STEP_INVALID }, { DIR_REVERSE, STEP_INVALID } } } } },
65 { true, { { DIR_HORIZONTAL, { { DIR_FORWARD, STEP_INVALID }, { DIR_REVERSE, STEP_INVALID } } },
66 { DIR_VERTICAL, { { DIR_FORWARD, STEP_FORWARD }, { DIR_REVERSE, STEP_BACK } } } } } } },
67 { true, // RTL is true
68 { { false, { { DIR_HORIZONTAL, { { DIR_FORWARD, STEP_BACK }, { DIR_REVERSE, STEP_FORWARD } } },
69 { DIR_VERTICAL, { { DIR_FORWARD, STEP_INVALID }, { DIR_REVERSE, STEP_INVALID } } } } },
70 { true, { { DIR_HORIZONTAL, { { DIR_FORWARD, STEP_INVALID }, { DIR_REVERSE, STEP_INVALID } } },
71 { DIR_VERTICAL, { { DIR_FORWARD, STEP_BACK }, { DIR_REVERSE, STEP_FORWARD } } } } } } }
72 };
73 } // namespace
74
~RenderList()75 RenderList::~RenderList()
76 {
77 if (scrollBarProxy_) {
78 scrollBarProxy_->UnRegisterScrollableNode(AceType::WeakClaim(this));
79 }
80 }
81
Update(const RefPtr<Component> & component)82 void RenderList::Update(const RefPtr<Component>& component)
83 {
84 component_ = AceType::DynamicCast<ListComponent>(component);
85 ACE_DCHECK(component_);
86
87 isRightToLeft_ = component_->GetTextDirection() == TextDirection::RTL ? true : false;
88 RemoveAllItems();
89
90 auto axis = component_->GetDirection();
91 vertical_ = axis == Axis::VERTICAL;
92
93 InitScrollBar();
94
95 // Start index should be updated only for the first time
96 if (startIndex_ == INITIAL_CHILD_INDEX) {
97 initialIndex_ = static_cast<size_t>(component_->GetInitialIndex());
98 startIndex_ = initialIndex_ > 0 ? initialIndex_ : 0;
99 useEstimateCurrentOffset_ = true;
100 }
101 // maybe change startIndex
102 ApplyRestoreInfo();
103
104 const auto& divider = component_->GetItemDivider();
105 listSpace_ = component_->GetSpace();
106 cachedCount_ = static_cast<size_t>(component_->GetCachedCount());
107
108 LOGI("cached count: %{public}zu", cachedCount_);
109 spaceWidth_ = std::max(NormalizePercentToPx(component_->GetSpace(), vertical_),
110 divider ? NormalizePercentToPx(divider->strokeWidth, vertical_) : 0.0);
111 InitScrollable(axis);
112 // now only support spring
113 if (component_->GetEdgeEffect() == EdgeEffect::SPRING) {
114 if (!scrollEffect_ || scrollEffect_->GetEdgeEffect() != EdgeEffect::SPRING) {
115 scrollEffect_ = AceType::MakeRefPtr<ScrollSpringEffect>();
116 ResetEdgeEffect();
117 }
118 } else if (component_->GetEdgeEffect() == EdgeEffect::FADE) {
119 if (!scrollEffect_ || scrollEffect_->GetEdgeEffect() != EdgeEffect::FADE) {
120 scrollEffect_ = AceType::MakeRefPtr<ScrollFadeEffect>();
121 ResetEdgeEffect();
122 }
123 } else {
124 scrollEffect_ = nullptr;
125 }
126
127 auto controller = component_->GetScrollController();
128 if (controller) {
129 controller->SetScrollNode(AceType::WeakClaim(this));
130 }
131 if (!animator_) {
132 animator_ = CREATE_ANIMATOR(GetContext());
133 }
134
135 // chainAnimation
136 if (chainAnimation_ != component_->GetChainAnimation()) {
137 chainAnimation_ = component_->GetChainAnimation();
138 if (chainAnimation_) {
139 InitChainAnimation(CHAIN_ANIMATION_NODE_COUNT);
140 overSpringProperty_ = SpringChainProperty::GetDefaultOverSpringProperty();
141 } else {
142 overSpringProperty_ = nullptr;
143 chain_ = nullptr;
144 chainAdapter_ = nullptr;
145 }
146 }
147
148 if (chainAnimation_) {
149 // add chain interval length
150 spaceWidth_ += NormalizeToPx(chainProperty_.Interval());
151 }
152
153 scrollBarProxy_ = component_->GetScrollBarProxy();
154 InitScrollBarProxy();
155
156 onItemDragStart_ = component_->GetOnItemDragStartId();
157 onItemDragEnter_ = component_->GetOnItemDragEnterId();
158 onItemDragMove_ = component_->GetOnItemDragMoveId();
159 onItemDragLeave_ = component_->GetOnItemDragLeaveId();
160 onItemDrop_ = component_->GetOnItemDropId();
161
162 if (onItemDragStart_) {
163 CreateDragDropRecognizer();
164 }
165 FindRefreshParent(AceType::WeakClaim(this));
166
167 isMultiSelectable_ = component_->GetMultiSelectable();
168 hasHeight_ = component_->GetHasHeight();
169 hasWidth_ = component_->GetHasWidth();
170 isLaneList_ = (component_->GetLanes() != -1) || (component_->GetLaneConstrain() != std::nullopt);
171 sticky_ = component_->GetSticky();
172 if (!vertical_ || scrollBar_->GetDisplayMode() != DisplayMode::OFF || chainAnimation_ ||
173 sticky_ != StickyStyle::NONE || isLaneList_ || !scrollable_->Available()) {
174 drivenRender_ = false;
175 } else {
176 drivenRender_ = true;
177 }
178 MarkNeedLayout();
179 }
180
InitScrollable(Axis axis)181 void RenderList::InitScrollable(Axis axis)
182 {
183 if (scrollable_) {
184 scrollable_->SetAxis(axis);
185 scrollable_->SetOnScrollBegin(component_->GetOnScrollBegin());
186 return;
187 }
188
189 auto callback = [weak = AceType::WeakClaim(this)](double offset, int32_t source) {
190 auto renderList = weak.Upgrade();
191
192 if (!renderList) {
193 return false;
194 }
195
196 if (source == SCROLL_FROM_START) {
197 renderList->ProcessDragStart(offset);
198 return true;
199 }
200
201 Offset delta;
202 if (renderList->vertical_) {
203 delta.SetX(0.0);
204 delta.SetY(offset);
205 } else {
206 delta.SetX(offset);
207 delta.SetY(0.0);
208 }
209 renderList->AdjustOffset(delta, source);
210 if ((source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_ANIMATION_SPRING) &&
211 renderList->currentOffset_ >= 0.0) {
212 if (renderList->scrollable_->RelatedScrollEventDoing(Offset(0.0, -offset))) {
213 return false;
214 }
215 }
216 renderList->ProcessDragUpdate(renderList->GetMainAxis(delta));
217
218 // Stop animator of scroll bar.
219 auto scrollBarProxy = renderList->scrollBarProxy_;
220 if (scrollBarProxy) {
221 scrollBarProxy->StopScrollBarAnimator();
222 }
223 return renderList->UpdateScrollPosition(renderList->GetMainAxis(delta), source);
224 };
225 scrollable_ = AceType::MakeRefPtr<Scrollable>(callback, axis);
226 scrollable_->SetNotifyScrollOverCallBack([weak = AceType::WeakClaim(this)](double velocity) {
227 auto list = weak.Upgrade();
228 if (!list) {
229 return;
230 }
231 list->ProcessScrollOverCallback(velocity);
232 });
233 scrollable_->SetScrollEndCallback([weak = AceType::WeakClaim(this)]() {
234 auto list = weak.Upgrade();
235 if (!list) {
236 LOGW("render list Upgrade fail in scroll end callback");
237 return;
238 }
239 auto proxy = list->scrollBarProxy_;
240 if (proxy) {
241 proxy->StartScrollBarAnimator();
242 }
243 auto scrollBar = list->scrollBar_;
244 if (scrollBar) {
245 scrollBar->HandleScrollBarEnd();
246 }
247 list->listEventFlags_[ListEvents::SCROLL_STOP] = true;
248 list->HandleListEvent();
249 });
250 InitializeScrollable(scrollable_);
251 scrollable_->SetOnScrollBegin(component_->GetOnScrollBegin());
252 if (vertical_) {
253 scrollable_->InitRelatedParent(GetParent());
254 }
255 scrollable_->Initialize(context_);
256 scrollable_->SetNodeId(GetAccessibilityNodeId());
257 }
258
InitScrollBarProxy()259 void RenderList::InitScrollBarProxy()
260 {
261 if (!scrollBarProxy_) {
262 return;
263 }
264 auto callback = [weak = AceType::WeakClaim(this)](double value, int32_t source) {
265 auto renderList = weak.Upgrade();
266 if (!renderList) {
267 LOGE("render list is released");
268 return false;
269 }
270 return renderList->UpdateScrollPosition(value, source);
271 };
272 scrollBarProxy_->UnRegisterScrollableNode(AceType::WeakClaim(this));
273 scrollBarProxy_->RegisterScrollableNode({ AceType::WeakClaim(this), callback });
274 }
275
IsReachStart()276 bool RenderList::IsReachStart()
277 {
278 bool scrollUpToReachStart = GreatNotEqual(prevOffset_, 0.0) && LessOrEqual(currentOffset_, 0.0);
279 bool scrollDownToReachStart = LessNotEqual(prevOffset_, 0.0) && GreatOrEqual(currentOffset_, 0.0);
280 return scrollUpToReachStart || scrollDownToReachStart;
281 }
282
InitScrollBar()283 void RenderList::InitScrollBar()
284 {
285 if (!component_) {
286 LOGE("InitScrollBar failed, component_ is null.");
287 return;
288 }
289 if (scrollBar_) {
290 scrollBar_->SetDisplayMode(component_->GetScrollBar());
291 scrollBar_->Reset();
292 return;
293 }
294 const RefPtr<ScrollBarTheme> theme = GetTheme<ScrollBarTheme>();
295 if (!theme) {
296 return;
297 }
298
299 scrollBar_ = AceType::MakeRefPtr<ScrollBar>(component_->GetScrollBar(), theme->GetShapeMode());
300 RefPtr<ListScrollBarController> controller = AceType::MakeRefPtr<ListScrollBarController>();
301 scrollBar_->SetScrollBarController(controller);
302
303 // set the scroll bar style
304 scrollBar_->SetReservedHeight(theme->GetReservedHeight());
305 scrollBar_->SetMinHeight(theme->GetMinHeight());
306 scrollBar_->SetMinDynamicHeight(theme->GetMinDynamicHeight());
307 scrollBar_->SetForegroundColor(theme->GetForegroundColor());
308 scrollBar_->SetBackgroundColor(theme->GetBackgroundColor());
309 scrollBar_->SetPadding(theme->GetPadding());
310 scrollBar_->SetScrollable(true);
311 scrollBar_->SetInactiveWidth(theme->GetNormalWidth());
312 scrollBar_->SetNormalWidth(theme->GetNormalWidth());
313 scrollBar_->SetActiveWidth(theme->GetActiveWidth());
314 scrollBar_->SetTouchWidth(theme->GetTouchWidth());
315 if (!vertical_) {
316 scrollBar_->SetPositionMode(PositionMode::BOTTOM);
317 } else {
318 if (isRightToLeft_) {
319 scrollBar_->SetPositionMode(PositionMode::LEFT);
320 }
321 }
322 scrollBar_->InitScrollBar(AceType::WeakClaim(this), GetContext());
323 SetScrollBarCallback();
324 }
325
SetScrollBarCallback()326 void RenderList::SetScrollBarCallback()
327 {
328 if (!scrollBar_ || !scrollBar_->NeedScrollBar()) {
329 return;
330 }
331 auto&& scrollCallback = [weakList = AceType::WeakClaim(this)](double value, int32_t source) {
332 auto list = weakList.Upgrade();
333 if (!list) {
334 LOGE("render list is released");
335 return false;
336 }
337 return list->UpdateScrollPosition(value, source);
338 };
339 auto&& barEndCallback = [weakList = AceType::WeakClaim(this)](int32_t value) {
340 auto list = weakList.Upgrade();
341 if (!list) {
342 LOGE("render list is released.");
343 return;
344 }
345 list->scrollBarOpacity_ = value;
346 list->MarkNeedRender();
347 };
348 auto&& scrollEndCallback = []() {
349 // nothing to do
350 };
351 scrollBar_->SetCallBack(scrollCallback, barEndCallback, scrollEndCallback);
352 }
353
GetLaneLengthInPx(const Dimension & length)354 double RenderList::GetLaneLengthInPx(const Dimension& length)
355 {
356 if (length.Unit() == DimensionUnit::PERCENT) {
357 return NormalizePercentToPx(length, !vertical_, true);
358 }
359 return NormalizeToPx(length);
360 }
361
ModifyLaneLength(const std::optional<std::pair<Dimension,Dimension>> & laneConstrain)362 void RenderList::ModifyLaneLength(const std::optional<std::pair<Dimension, Dimension>>& laneConstrain)
363 {
364 minLaneLength_ = GetLaneLengthInPx(laneConstrain.value().first);
365 maxLaneLength_ = GetLaneLengthInPx(laneConstrain.value().second);
366 if (LessOrEqual(maxLaneLength_, 0.0)) {
367 maxLaneLength_ = GetCrossSize(GetLayoutSize());
368 }
369 if (LessOrEqual(minLaneLength_, 0.0)) {
370 minLaneLength_ = std::min(GetCrossSize(GetLayoutSize()), maxLaneLength_);
371 }
372 if (GreatNotEqual(minLaneLength_, maxLaneLength_)) {
373 LOGI("minLaneLength: %{public}f is greater than maxLaneLength: %{public}f, assign minLaneLength to"
374 " maxLaneLength",
375 minLaneLength_, maxLaneLength_);
376 maxLaneLength_ = minLaneLength_;
377 }
378 }
379
CalculateLanes()380 void RenderList::CalculateLanes()
381 {
382 auto lanes = component_->GetLanes();
383 do {
384 auto laneConstrain = component_->GetLaneConstrain();
385 // Case 1: lane length constrain is not set
386 // 1.1: use [lanes_] set by user if [lanes_] is set
387 // 1.2: set [lanes_] to 1 if [lanes_] is not set
388 if (!laneConstrain) {
389 if (lanes <= 0) {
390 lanes = 1;
391 }
392 maxLaneLength_ = GetCrossSize(GetLayoutParam().GetMaxSize()) / lanes;
393 minLaneLength_ = GetCrossSize(GetLayoutParam().GetMinSize()) / lanes;
394 break;
395 }
396 // Case 2: lane length constrain is set --> need to calculate [lanes_] according to contraint.
397 // We agreed on such rules (assuming we have a vertical list here):
398 // rule 1: [minLaneLength_] has a higher priority than [maxLaneLength_] when decide [lanes_], for e.g.,
399 // if [minLaneLength_] is 40, [maxLaneLength_] is 60, list's width is 120,
400 // the [lanes_] is 3 rather than 2.
401 // rule 2: after [lanes_] is determined by rule 1, the width of lane will be as large as it can be, for e.g.,
402 // if [minLaneLength_] is 40, [maxLaneLength_] is 60, list's width is 132, the [lanes_] is 3
403 // according to rule 1, then the width of lane will be 132 / 3 = 44 rather than 40,
404 // its [minLaneLength_].
405
406 // set layout size temporarily to calculate percent unit of constrain
407 SetLayoutSize(GetLayoutParam().GetMaxSize());
408 ModifyLaneLength(laneConstrain);
409
410 // if minLaneLength is 40, maxLaneLength is 60
411 // when list's width is 120, lanes_ = 3
412 // when list's width is 80, lanes_ = 2
413 // when list's width is 70, lanes_ = 1
414 auto maxCrossSize = GetCrossSize(GetLayoutSize());
415 double maxLanes = maxCrossSize / minLaneLength_;
416 double minLanes = maxCrossSize / maxLaneLength_;
417 // let's considerate scenarios when maxCrossSize > 0
418 // now it's guaranteed that [minLaneLength_] <= [maxLaneLength_], i.e., maxLanes >= minLanes > 0
419 // there are 3 scenarios:
420 // 1. 1 > maxLanes >= minLanes > 0
421 // 2. maxLanes >= 1 >= minLanes > 0
422 // 3. maxLanes >= minLanes > 1
423
424 // 1. 1 > maxLanes >= minLanes > 0 ---> maxCrossSize < minLaneLength_ =< maxLaneLength
425 if (GreatNotEqual(1, maxLanes) && GreatOrEqual(maxLanes, minLanes)) {
426 lanes = 1;
427 minLaneLength_ = maxCrossSize;
428 maxLaneLength_ = maxCrossSize;
429 break;
430 }
431 // 2. maxLanes >= 1 >= minLanes > 0 ---> minLaneLength_ = maxCrossSize < maxLaneLength
432 if (GreatOrEqual(maxLanes, 1) && LessOrEqual(minLanes, 1)) {
433 lanes = std::floor(maxLanes);
434 maxLaneLength_ = maxCrossSize;
435 break;
436 }
437 // 3. maxLanes >= minLanes > 1 ---> minLaneLength_ <= maxLaneLength < maxCrossSize
438 if (GreatOrEqual(maxLanes, minLanes) && GreatNotEqual(minLanes, 1)) {
439 lanes = std::floor(maxLanes);
440 break;
441 }
442 lanes = 1;
443 LOGE("unexpected situation, set lanes to 1, maxLanes: %{public}f, minLanes: %{public}f, minLaneLength_: "
444 "%{public}f, maxLaneLength_: %{public}f",
445 maxLanes, minLanes, minLaneLength_, maxLaneLength_);
446 } while (0);
447 if (lanes != lanes_) { // if lanes changes, adjust startIndex_
448 lanes_ = lanes;
449 if (lanes > 1) {
450 size_t startIndex = startIndex_ - GetItemRelativeIndex(startIndex_) % static_cast<size_t>(lanes);
451 if (startIndex_ != startIndex) {
452 RemoveAllItems();
453 }
454 }
455 }
456 }
457
RequestNewItemsAtEndForLaneList(double & curMainPos,double mainSize)458 void RenderList::RequestNewItemsAtEndForLaneList(double& curMainPos, double mainSize)
459 {
460 int newItemCntInLine = 0;
461 double lineMainSize = 0;
462 for (size_t newIndex = startIndex_ + items_.size();; ++newIndex) {
463 bool breakWhenRequestNewItem = false;
464 RefPtr<RenderListItemGroup> itemGroup;
465 do {
466 if (GreatOrEqual(curMainPos, endMainPos_)) {
467 breakWhenRequestNewItem = true;
468 break;
469 }
470 auto child = RequestAndLayoutNewItem(newIndex, curMainPos);
471 if (!child) {
472 startIndex_ = std::min(startIndex_, TotalCount());
473 breakWhenRequestNewItem = true;
474 break;
475 }
476 if (GreatOrEqual(curMainPos, mainSize)) {
477 ++endCachedCount_;
478 }
479 itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
480 if (itemGroup) {
481 break;
482 }
483 lineMainSize = std::max(lineMainSize, GetMainSize(child->GetLayoutSize()));
484 ++newItemCntInLine;
485 } while (0);
486 bool singleLaneDoneAddItem = (lanes_ == 1) && !breakWhenRequestNewItem;
487 bool multiLaneDoneSupplyOneLine = (lanes_ > 1) && (newItemCntInLine == lanes_);
488 bool multiLaneStartSupplyLine = (itemGroup || breakWhenRequestNewItem) && (newItemCntInLine >= 1);
489 if (singleLaneDoneAddItem || multiLaneDoneSupplyOneLine || multiLaneStartSupplyLine) {
490 curMainPos += lineMainSize + spaceWidth_;
491 newItemCntInLine = 0;
492 lineMainSize = 0;
493 }
494 if (itemGroup) {
495 double size = GetMainSize(itemGroup->GetLayoutSize());
496 curMainPos += size + spaceWidth_;
497 }
498 if (breakWhenRequestNewItem) {
499 break;
500 }
501 }
502 }
503
RequestNewItemsAtEnd(double & curMainPos,double mainSize)504 void RenderList::RequestNewItemsAtEnd(double& curMainPos, double mainSize)
505 {
506 for (size_t newIndex = startIndex_ + items_.size();; ++newIndex) {
507 if (cachedCount_ != 0) {
508 if (endCachedCount_ >= cachedCount_ && GreatOrEqual(curMainPos, mainSize)) {
509 break;
510 }
511 } else {
512 if (GreatOrEqual(curMainPos, endMainPos_)) {
513 break;
514 }
515 }
516 auto child = RequestAndLayoutNewItem(newIndex, curMainPos);
517 if (!child) {
518 startIndex_ = std::min(startIndex_, TotalCount());
519 break;
520 }
521 if (GreatOrEqual(curMainPos, mainSize)) {
522 auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
523 if (itemGroup) {
524 endCachedCount_ += itemGroup->GetCurrEndCacheCount();
525 } else {
526 ++endCachedCount_;
527 }
528 }
529 curMainPos += GetMainSize(child->GetLayoutSize()) + spaceWidth_;
530 }
531
532 if (selectedItem_ && selectedItemIndex_ < startIndex_) {
533 curMainPos += GetMainSize(selectedItem_->GetLayoutSize()) + spaceWidth_;
534 }
535 }
536
RequestNewItemsAtStartForLaneList()537 void RenderList::RequestNewItemsAtStartForLaneList()
538 {
539 int newItemCntInLine = 0;
540 double lineMainSize = 0;
541 for (; startIndex_ > 0; --startIndex_) {
542 bool breakWhenRequestNewItem = false;
543 RefPtr<RenderListItemGroup> itemGroup;
544 do {
545 if (LessOrEqual(currentOffset_, startMainPos_)) {
546 breakWhenRequestNewItem = true;
547 break;
548 }
549 auto child = RequestAndLayoutNewItem(startIndex_ - 1, currentOffset_ - spaceWidth_, false);
550 if (!child) {
551 breakWhenRequestNewItem = true;
552 break;
553 }
554 if (selectedItemIndex_ == startIndex_) {
555 continue;
556 }
557 if (LessOrEqual(currentOffset_, 0.0)) {
558 ++startCachedCount_;
559 }
560 itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
561 if (itemGroup) {
562 break;
563 }
564 lineMainSize = std::max(lineMainSize, GetMainSize(child->GetLayoutSize()));
565 ++newItemCntInLine;
566 } while (0);
567 bool singleLaneDoneAddItem = (lanes_ == 1) && !breakWhenRequestNewItem;
568 bool isLaneStart =
569 !itemGroup && (lanes_ > 1) && (GetItemRelativeIndex(startIndex_ - 1) % static_cast<size_t>(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 event.second = false; \
751 } \
752 break;
753
HandleListEvent()754 void RenderList::HandleListEvent()
755 {
756 for (auto& event : listEventFlags_) {
757 switch (event.first) {
758 CASE_OF_LIST_EVENT_WITH_NO_PARAM(SCROLL_STOP, GetOnScrollStop);
759 CASE_OF_LIST_EVENT_WITH_NO_PARAM(REACH_START, GetOnReachStart);
760 CASE_OF_LIST_EVENT_WITH_NO_PARAM(REACH_END, GetOnReachEnd);
761 default:
762 LOGW("This event does not handle in here, please check. event number: %{public}d", event.first);
763 break;
764 }
765 }
766 }
767
CalculateLaneCrossOffset(double crossSize,double childCrossSize)768 double RenderList::CalculateLaneCrossOffset(double crossSize, double childCrossSize)
769 {
770 double delta = crossSize - childCrossSize;
771 // TODO: modify in rtl scenario
772 switch (component_->GetAlignListItemAlign()) {
773 case ListItemAlign::START:
774 return 0.0;
775 case ListItemAlign::CENTER:
776 return delta / CENTER_ALIGN_DIVIDER;
777 case ListItemAlign::END:
778 return delta;
779 default:
780 LOGW("Invalid ListItemAlign: %{public}d", component_->GetAlignListItemAlign());
781 return 0.0;
782 }
783 }
784
SetItemsPositionForLaneList(double mainSize)785 Size RenderList::SetItemsPositionForLaneList(double mainSize)
786 {
787 double crossSize = fixedCrossSize_ ? GetCrossSize(GetLayoutParam().GetMaxSize()) : 0.0;
788 if (items_.empty()) {
789 return MakeValue<Size>(fixedMainSize_ ? mainSize : 0.0, crossSize);
790 }
791
792 double curMainPos = currentOffset_;
793 size_t index = startIndex_;
794 size_t newStickyIndex = 0;
795 RefPtr<RenderListItem> newStickyItem;
796 RefPtr<RenderListItem> nextStickyItem;
797 double nextStickyMainAxis = Size::INFINITE_SIZE;
798 size_t firstIdx = INITIAL_CHILD_INDEX;
799 size_t lastIdx = 0;
800 double selectedItemMainSize = selectedItem_ ? GetMainSize(selectedItem_->GetLayoutSize()) : 0.0;
801
802 for (auto iter = items_.begin(); iter != items_.end();) {
803 RefPtr<RenderListItem> child;
804 double childMainSize = 0.0;
805 double childCrossSize = 0.0;
806 std::vector<RefPtr<RenderListItem>> itemSet;
807 // start set child position in a row
808 for (int32_t rowIndex = 0; rowIndex < lanes_; rowIndex++) {
809 child = *iter;
810 auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
811 if (itemGroup && rowIndex > 0) {
812 break;
813 }
814 double singleChildSize = GetMainSize(child->GetLayoutSize());
815 childCrossSize += GetCrossSize(child->GetLayoutSize());
816 childMainSize = std::max(childMainSize, singleChildSize); // get max item height in a row as row height
817 // store items in a row, set position of each item after done getting [childMainSize]
818 itemSet.emplace_back(child);
819 if ((++iter) == items_.end() || itemGroup) {
820 break;
821 }
822 }
823 if (!fixedCrossSize_) {
824 crossSize = std::max(crossSize, childCrossSize);
825 }
826 auto offset = MakeValue<Offset>(curMainPos, 0.0);
827 if (chainAnimation_) {
828 double chainDelta = GetChainDelta(index);
829 auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
830 if (itemGroup) {
831 itemGroup->SetChainOffset(-chainDelta);
832 }
833 offset += MakeValue<Offset>(-chainDelta, 0.0);
834 }
835 // set item position for one row
836 for (size_t i = 0; i < itemSet.size(); i++) {
837 auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(itemSet[i]);
838 double itemCrossSize = itemGroup ? crossSize : crossSize / lanes_;
839 auto offsetCross = CalculateLaneCrossOffset(itemCrossSize, GetCrossSize(itemSet[i]->GetLayoutSize()));
840 auto position = offset + MakeValue<Offset>(0.0, itemCrossSize * i + offsetCross);
841 if (isRightToLeft_) {
842 if (IsVertical()) {
843 position = MakeValue<Offset>(
844 GetMainAxis(position), crossSize - childCrossSize / itemSet.size() - GetCrossAxis(position));
845 } else {
846 position =
847 MakeValue<Offset>(mainSize - childMainSize - GetMainAxis(position), GetCrossAxis(position));
848 }
849 }
850 SetChildPosition(itemSet[i], position);
851 }
852
853 if (lanes_ == 1) {
854 if (selectedItem_) {
855 double range = std::min(selectedItemMainSize, childMainSize) / 2.0;
856 bool beforeSelectedItem = index <= selectedItemIndex_;
857 if (beforeSelectedItem && targetIndex_ == index) {
858 targetMainAxis_ = curMainPos;
859 curMainPos += selectedItemMainSize + spaceWidth_;
860 }
861
862 if (movingForward_) {
863 double axis = selectedItemMainAxis_;
864 if (GreatOrEqual(axis, curMainPos) && LessNotEqual(axis, curMainPos + range)) {
865 targetIndex_ = beforeSelectedItem ? index : index - 1;
866 targetMainAxis_ = curMainPos;
867 curMainPos += selectedItemMainSize + spaceWidth_;
868 }
869 } else {
870 double axis = selectedItemMainAxis_ + selectedItemMainSize;
871 double limit = curMainPos + childMainSize;
872 if (GreatNotEqual(axis, limit - range) && LessOrEqual(axis, limit)) {
873 targetIndex_ = beforeSelectedItem ? index + 1 : index;
874 targetMainAxis_ = curMainPos;
875 curMainPos -= selectedItemMainSize + spaceWidth_;
876 }
877 }
878 }
879
880 // Disable sticky mode while expand all items
881 if (fixedMainSize_ && itemSet[0]->GetSticky() != StickyMode::NONE) {
882 if (LessOrEqual(curMainPos, 0.0)) {
883 newStickyItem = itemSet[0];
884 newStickyIndex = index;
885 } else if (!nextStickyItem) {
886 nextStickyItem = itemSet[0];
887 nextStickyMainAxis = curMainPos;
888 }
889 }
890 }
891 itemSet.clear();
892
893 childMainSize += spaceWidth_;
894 if (LessNotEqual(curMainPos, mainSize) && GreatNotEqual(curMainPos + childMainSize, 0.0)) {
895 firstIdx = std::min(firstIdx, index);
896 lastIdx = std::max(lastIdx, index);
897 }
898
899 if (child != selectedItem_) {
900 curMainPos += childMainSize;
901 }
902
903 if (selectedItem_ && index > selectedItemIndex_ && targetIndex_ == index) {
904 targetMainAxis_ = curMainPos;
905 curMainPos += selectedItemMainSize + spaceWidth_;
906 }
907
908 ++index;
909 }
910 if (firstIdx != firstDisplayIndex_ || lastIdx != lastDisplayIndex_) {
911 firstDisplayIndex_ = firstIdx;
912 lastDisplayIndex_ = lastIdx;
913 ResumeEventCallback(component_, &ListComponent::GetOnScrollIndex, static_cast<int32_t>(firstDisplayIndex_),
914 static_cast<int32_t>(lastDisplayIndex_), static_cast<int32_t>(midDisplayIndex_));
915 }
916
917 // Disable sticky mode while expand all items
918 if (!fixedMainSize_) {
919 return MakeValue<Size>(curMainPos - spaceWidth_, crossSize);
920 }
921
922 if (lanes_ == 1) {
923 UpdateStickyListItem(newStickyItem, newStickyIndex, nextStickyItem);
924 if (currentStickyItem_) {
925 const auto& stickyItemLayoutSize = currentStickyItem_->GetLayoutSize();
926 const double mainStickySize = GetMainSize(stickyItemLayoutSize) + spaceWidth_;
927 if (nextStickyItem && LessNotEqual(nextStickyMainAxis, mainStickySize)) {
928 auto position = MakeValue<Offset>(nextStickyMainAxis - mainStickySize, 0.0);
929 if (isRightToLeft_) {
930 if (IsVertical()) {
931 position = MakeValue<Offset>(GetMainAxis(position),
932 crossSize - GetCrossSize(stickyItemLayoutSize) - GetCrossAxis(position));
933 } else {
934 position = MakeValue<Offset>(
935 mainSize - mainStickySize - GetMainAxis(position), GetCrossAxis(position));
936 }
937 }
938 currentStickyItem_->SetPosition(position);
939 } else {
940 currentStickyItem_->SetPosition(MakeValue<Offset>(0.0, 0.0));
941 }
942
943 if (!fixedCrossSize_) {
944 crossSize = std::max(crossSize, GetCrossSize(stickyItemLayoutSize));
945 }
946 }
947 }
948
949 return MakeValue<Size>(mainSize, crossSize);
950 }
951
SetItemsPosition(double mainSize)952 Size RenderList::SetItemsPosition(double mainSize)
953 {
954 double crossSize = fixedCrossSize_ ? GetCrossSize(GetLayoutParam().GetMaxSize()) : 0.0;
955 if (items_.empty()) {
956 return MakeValue<Size>(fixedMainSize_ ? mainSize : 0.0, crossSize);
957 }
958
959 double curMainPos = currentOffset_;
960 size_t index = startIndex_;
961 size_t newStickyIndex = 0;
962 RefPtr<RenderListItem> newStickyItem;
963 RefPtr<RenderListItem> nextStickyItem;
964 double nextStickyMainAxis = Size::INFINITE_SIZE;
965 size_t firstIdx = INITIAL_CHILD_INDEX;
966 size_t lastIdx = 0;
967 double selectedItemMainSize = selectedItem_ ? GetMainSize(selectedItem_->GetLayoutSize()) : 0.0;
968
969 for (const auto& child : items_) {
970 const auto& childLayoutSize = child->GetLayoutSize();
971 double childMainSize = GetMainSize(childLayoutSize);
972
973 if (selectedItem_) {
974 double range = std::min(selectedItemMainSize, childMainSize) / 2.0;
975 bool beforeSelectedItem = index <= selectedItemIndex_;
976 if (beforeSelectedItem && targetIndex_ == index) {
977 targetMainAxis_ = curMainPos;
978 curMainPos += selectedItemMainSize + spaceWidth_;
979 }
980
981 if (movingForward_) {
982 double axis = selectedItemMainAxis_;
983 if (GreatOrEqual(axis, curMainPos) && LessNotEqual(axis, curMainPos + range)) {
984 targetIndex_ = beforeSelectedItem ? index : index - 1;
985 targetMainAxis_ = curMainPos;
986 curMainPos += selectedItemMainSize + spaceWidth_;
987 }
988 } else {
989 double axis = selectedItemMainAxis_ + selectedItemMainSize;
990 double limit = curMainPos + childMainSize;
991 if (GreatNotEqual(axis, limit - range) && LessOrEqual(axis, limit)) {
992 targetIndex_ = beforeSelectedItem ? index + 1 : index;
993 targetMainAxis_ = curMainPos;
994 curMainPos -= selectedItemMainSize + spaceWidth_;
995 }
996 }
997 }
998
999 auto offsetCross = CalculateLaneCrossOffset(crossSize, GetCrossSize(childLayoutSize));
1000 auto offset = MakeValue<Offset>(curMainPos, offsetCross);
1001 if (chainAnimation_) {
1002 double chainDelta = GetChainDelta(index);
1003 auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
1004 if (itemGroup) {
1005 itemGroup->SetChainOffset(-chainDelta);
1006 }
1007 offset += MakeValue<Offset>(-chainDelta, 0.0);
1008 }
1009
1010 if (isRightToLeft_) {
1011 if (IsVertical()) {
1012 offset = MakeValue<Offset>(
1013 GetMainAxis(offset), crossSize - GetCrossSize(childLayoutSize) - GetCrossAxis(offset));
1014 } else {
1015 offset = MakeValue<Offset>(
1016 mainSize - GetMainSize(childLayoutSize) - GetMainAxis(offset), GetCrossAxis(offset));
1017 }
1018 }
1019 SetChildPosition(child, offset);
1020 // Disable sticky mode while expand all items
1021 if (fixedMainSize_ && child->GetSticky() != StickyMode::NONE) {
1022 if (LessOrEqual(curMainPos, 0.0)) {
1023 newStickyItem = child;
1024 newStickyIndex = index;
1025 } else if (!nextStickyItem) {
1026 nextStickyItem = child;
1027 nextStickyMainAxis = curMainPos;
1028 }
1029 }
1030
1031 childMainSize += spaceWidth_;
1032 if (LessNotEqual(curMainPos, mainSize) && GreatNotEqual(curMainPos + childMainSize, 0.0)) {
1033 if (!fixedCrossSize_) {
1034 crossSize = std::max(crossSize, GetCrossSize(childLayoutSize));
1035 }
1036 firstIdx = std::min(firstIdx, index);
1037 lastIdx = std::max(lastIdx, index);
1038 }
1039
1040 if (child != selectedItem_) {
1041 curMainPos += childMainSize;
1042 }
1043
1044 if (selectedItem_ && index > selectedItemIndex_ && targetIndex_ == index) {
1045 targetMainAxis_ = curMainPos;
1046 curMainPos += selectedItemMainSize + spaceWidth_;
1047 }
1048
1049 ++index;
1050 }
1051 if (firstIdx != firstDisplayIndex_ || lastIdx != lastDisplayIndex_) {
1052 firstDisplayIndex_ = firstIdx;
1053 lastDisplayIndex_ = lastIdx;
1054 ResumeEventCallback(component_, &ListComponent::GetOnScrollIndex, static_cast<int32_t>(firstDisplayIndex_),
1055 static_cast<int32_t>(lastDisplayIndex_), static_cast<int32_t>(midDisplayIndex_));
1056 }
1057
1058 // Disable sticky mode while expand all items
1059 if (!fixedMainSize_) {
1060 return MakeValue<Size>(curMainPos - spaceWidth_, crossSize);
1061 }
1062
1063 UpdateStickyListItem(newStickyItem, newStickyIndex, nextStickyItem);
1064 if (currentStickyItem_) {
1065 const auto& stickyItemLayoutSize = currentStickyItem_->GetLayoutSize();
1066 const double mainStickySize = GetMainSize(stickyItemLayoutSize) + spaceWidth_;
1067 auto offsetCross = CalculateLaneCrossOffset(crossSize, GetCrossSize(currentStickyItem_->GetLayoutSize()));
1068 if (nextStickyItem && LessNotEqual(nextStickyMainAxis, mainStickySize)) {
1069 auto position = MakeValue<Offset>(nextStickyMainAxis - mainStickySize, offsetCross);
1070 if (isRightToLeft_) {
1071 if (IsVertical()) {
1072 position = MakeValue<Offset>(
1073 GetMainAxis(position), crossSize - GetCrossSize(stickyItemLayoutSize) - GetCrossAxis(position));
1074 } else {
1075 position =
1076 MakeValue<Offset>(mainSize - mainStickySize - GetMainAxis(position), GetCrossAxis(position));
1077 }
1078 }
1079 currentStickyItem_->SetPosition(position);
1080 } else {
1081 currentStickyItem_->SetPosition(MakeValue<Offset>(0.0, offsetCross));
1082 }
1083
1084 if (!fixedCrossSize_) {
1085 crossSize = std::max(crossSize, GetCrossSize(stickyItemLayoutSize));
1086 }
1087 }
1088
1089 return MakeValue<Size>(mainSize, crossSize);
1090 }
1091
UpdateStickyListItem(const RefPtr<RenderListItem> & newStickyItem,size_t newStickyItemIndex,const RefPtr<RenderListItem> & nextStickyItem)1092 void RenderList::UpdateStickyListItem(const RefPtr<RenderListItem>& newStickyItem, size_t newStickyItemIndex,
1093 const RefPtr<RenderListItem>& nextStickyItem)
1094 {
1095 if (newStickyItem) {
1096 if (newStickyItem == currentStickyItem_) {
1097 return;
1098 }
1099
1100 if (currentStickyItem_ && currentStickyIndex_ < startIndex_) {
1101 RecycleListItem(currentStickyIndex_);
1102 }
1103
1104 currentStickyItem_ = newStickyItem;
1105 currentStickyIndex_ = newStickyItemIndex;
1106 return;
1107 }
1108
1109 if (nextStickyItem && nextStickyItem == currentStickyItem_) {
1110 size_t index = currentStickyIndex_ != 0 ? currentStickyIndex_ - 1 : INVALID_CHILD_INDEX;
1111 ApplyPreviousStickyListItem(index, 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_ != 0 ? startIndex_ - 1 : INVALID_CHILD_INDEX;
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 = static_cast<size_t>(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)1751 void RenderList::JumpToIndex(int32_t idx)
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);
1804 } else if (scrollEdgeType == ScrollEdgeType::SCROLL_BOTTOM) {
1805 JumpToIndex(TotalCount());
1806 }
1807 } else {
1808 if (scrollEdgeType == ScrollEdgeType::SCROLL_LEFT) {
1809 JumpToIndex(0);
1810 } else if (scrollEdgeType == ScrollEdgeType::SCROLL_RIGHT) {
1811 JumpToIndex(TotalCount());
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 return;
2009 }
2010
2011 if (NearZero(velocity)) {
2012 return;
2013 }
2014
2015 if (reachStart_) {
2016 dragStartIndexPending_ = startIndex_;
2017 } else if (reachEnd_) {
2018 dragStartIndexPending_ = startIndex_;
2019 if (!items_.empty()) {
2020 dragStartIndexPending_ += items_.size() - 1;
2021 }
2022 }
2023
2024 double delta = FlushChainAnimation();
2025 currentOffset_ += delta;
2026 if (!NearZero(delta)) {
2027 LOGE("ProcessScrollOverCallback delta = %lf currentOffset_ = %lf", delta, currentOffset_);
2028 }
2029 }
2030
InitChainAnimation(int32_t nodeCount)2031 void RenderList::InitChainAnimation(int32_t nodeCount)
2032 {
2033 auto context = GetContext().Upgrade();
2034 if (!context) {
2035 LOGE("Init chain animation failed. context is null");
2036 return;
2037 }
2038
2039 if (chainAdapter_ && chain_) {
2040 return;
2041 }
2042 chainAdapter_ = AceType::MakeRefPtr<BilateralSpringAdapter>();
2043 chain_ = AceType::MakeRefPtr<SimpleSpringChain>(chainAdapter_);
2044 const auto& property = GetChainProperty();
2045 chain_->SetFrameDelta(property.FrameDelay());
2046 if (property.StiffnessTransfer()) {
2047 chain_->SetStiffnessTransfer(AceType::MakeRefPtr<ExpParamTransfer>(property.StiffnessCoefficient()));
2048 } else {
2049 chain_->SetStiffnessTransfer(AceType::MakeRefPtr<LinearParamTransfer>(property.StiffnessCoefficient()));
2050 }
2051 if (property.DampingTransfer()) {
2052 chain_->SetDampingTransfer(AceType::MakeRefPtr<ExpParamTransfer>(property.DampingCoefficient()));
2053 } else {
2054 chain_->SetDampingTransfer(AceType::MakeRefPtr<LinearParamTransfer>(property.DampingCoefficient()));
2055 }
2056 chain_->SetControlDamping(property.ControlDamping());
2057 chain_->SetControlStiffness(property.ControlStiffness());
2058 chain_->SetDecoration(context->NormalizeToPx(property.Interval()));
2059 chain_->SetMinDecoration(context->NormalizeToPx(property.MinInterval()));
2060 chain_->SetMaxDecoration(context->NormalizeToPx(property.MaxInterval()));
2061 for (int32_t index = 0; index < nodeCount; index++) {
2062 auto node = AceType::MakeRefPtr<BilateralSpringNode>(GetContext(), index, 0.0);
2063 WeakPtr<BilateralSpringNode> nodeWeak(node);
2064 WeakPtr<SimpleSpringAdapter> adapterWeak(chainAdapter_);
2065 node->AddUpdateListener(
2066 [weak = AceType::WeakClaim(this), nodeWeak, adapterWeak](double value, double velocity) {
2067 auto renderList = weak.Upgrade();
2068 auto node = nodeWeak.Upgrade();
2069 auto adapter = adapterWeak.Upgrade();
2070 if (!renderList || !node || !adapter) {
2071 return;
2072 }
2073 renderList->MarkNeedLayout();
2074 });
2075 chainAdapter_->AddNode(node);
2076 }
2077 chainAdapter_->NotifyControlIndexChange();
2078 }
2079
GetChainDelta(int32_t index) const2080 double RenderList::GetChainDelta(int32_t index) const
2081 {
2082 if (!chainAdapter_) {
2083 return 0.0;
2084 }
2085 double value = 0.0;
2086 RefPtr<BilateralSpringNode> node;
2087 int32_t controlIndex = dragStartIndex_;
2088 int32_t baseIndex = controlIndex - chainAdapter_->GetControlIndex();
2089 auto targetIndex = std::clamp(index - baseIndex, 0, CHAIN_ANIMATION_NODE_COUNT - 1);
2090 node = AceType::DynamicCast<BilateralSpringNode>(chainAdapter_->GetNode(targetIndex));
2091 if (node) {
2092 value = node->GetValue();
2093 }
2094 return value;
2095 }
2096
GetNearChildByPosition(double mainOffset) const2097 size_t RenderList::GetNearChildByPosition(double mainOffset) const
2098 {
2099 size_t index = startIndex_;
2100 size_t prevIndex = INVALID_CHILD_INDEX;
2101
2102 for (auto& child : items_) {
2103 auto childMainOffset = GetMainAxis(child->GetPosition());
2104 if (childMainOffset > mainOffset) {
2105 return prevIndex;
2106 }
2107 prevIndex = index++;
2108 }
2109 return prevIndex;
2110 }
2111
FlushChainAnimation()2112 double RenderList::FlushChainAnimation()
2113 {
2114 if (!chainAnimation_ || !chain_ || !chainAdapter_) {
2115 return 0.0;
2116 }
2117 double deltaDistance = 0.0;
2118 bool needSetValue = false;
2119 bool overScroll = scrollable_ && scrollable_->IsSpringMotionRunning();
2120 if (chainOverScroll_ != overScroll) {
2121 if (overScroll) {
2122 const auto& springProperty = GetOverSpringProperty();
2123 if (springProperty && springProperty->IsValid()) {
2124 chain_->SetControlStiffness(springProperty->Stiffness());
2125 chain_->SetControlDamping(springProperty->Damping());
2126 }
2127 } else {
2128 chain_->SetControlStiffness(GetChainProperty().ControlStiffness());
2129 chain_->SetControlDamping(GetChainProperty().ControlDamping());
2130 }
2131 chain_->OnControlNodeChange();
2132 chainOverScroll_ = overScroll;
2133 }
2134 chain_->FlushAnimation();
2135 if (dragStartIndexPending_ != dragStartIndex_) {
2136 deltaDistance = chainAdapter_->ResetControl(dragStartIndexPending_ - dragStartIndex_);
2137 dragStartIndex_ = dragStartIndexPending_;
2138 chainAdapter_->SetDeltaValue(-deltaDistance);
2139 needSetValue = true;
2140 }
2141 if (!NearZero(currentDelta_)) {
2142 chainAdapter_->SetDeltaValue(currentDelta_);
2143 currentDelta_ = 0.0;
2144 needSetValue = true;
2145 }
2146 if (needSetValue) {
2147 chain_->SetValue(0.0);
2148 }
2149 return deltaDistance;
2150 }
2151
UpdateAccessibilityAttr()2152 void RenderList::UpdateAccessibilityAttr()
2153 {
2154 if (!component_) {
2155 LOGE("RenderList: component is null.");
2156 return;
2157 }
2158
2159 auto accessibilityNode = GetAccessibilityNode().Upgrade();
2160 if (!accessibilityNode) {
2161 return;
2162 }
2163
2164 auto collectionInfo = accessibilityNode->GetCollectionInfo();
2165 size_t count = TotalCount() > 0 ? TotalCount() : 1;
2166 if (vertical_) {
2167 collectionInfo.rows = static_cast<int32_t>(count);
2168 collectionInfo.columns = 1;
2169 } else {
2170 collectionInfo.rows = 1;
2171 collectionInfo.columns = static_cast<int32_t>(count);
2172 }
2173 accessibilityNode->SetCollectionInfo(collectionInfo);
2174 accessibilityNode->SetScrollableState(true);
2175 accessibilityNode->SetActionScrollForward([weakList = AceType::WeakClaim(this)]() {
2176 auto list = weakList.Upgrade();
2177 if (list) {
2178 LOGI("Trigger ScrollForward by Accessibility.");
2179 return list->HandleActionScroll(true);
2180 }
2181 return false;
2182 });
2183 accessibilityNode->SetActionScrollBackward([weakList = AceType::WeakClaim(this)]() {
2184 auto list = weakList.Upgrade();
2185 if (list) {
2186 LOGI("Trigger ScrollBackward by Accessibility.");
2187 return list->HandleActionScroll(false);
2188 }
2189 return false;
2190 });
2191
2192 accessibilityNode->AddSupportAction(AceAction::ACTION_SCROLL_FORWARD);
2193 accessibilityNode->AddSupportAction(AceAction::ACTION_SCROLL_BACKWARD);
2194
2195 scrollFinishEventBack_ = [weakList = AceType::WeakClaim(this)] {
2196 auto list = weakList.Upgrade();
2197 if (list) {
2198 list->ModifyActionScroll();
2199 }
2200 };
2201 }
2202
UpdateAccessibilityScrollAttr()2203 void RenderList::UpdateAccessibilityScrollAttr()
2204 {
2205 auto accessibilityNode = GetAccessibilityNode().Upgrade();
2206 if (accessibilityNode) {
2207 accessibilityNode->SetListBeginIndex(firstDisplayIndex_);
2208 accessibilityNode->SetListEndIndex(lastDisplayIndex_);
2209 accessibilityNode->SetListItemCounts(items_.size());
2210 }
2211 }
2212
UpdateAccessibilityVisible()2213 void RenderList::UpdateAccessibilityVisible()
2214 {
2215 auto accessibilityNode = GetAccessibilityNode().Upgrade();
2216 if (!accessibilityNode) {
2217 return;
2218 }
2219 Offset globalOffset = GetGlobalOffset();
2220 Rect listItemRect;
2221 Rect viewPortRect = Rect(globalOffset, GetLayoutSize());
2222 for (const auto& listItem : items_) {
2223 if (!listItem || listItem->GetChildren().empty()) {
2224 continue;
2225 }
2226 // RenderListItem's accessibility node is List's in v2, see ViewStackProcessor::WrapComponents() and
2227 // RenderElement::SetAccessibilityNode
2228 auto listItemWithAccessibilityNode = listItem->GetFirstChild();
2229 auto node = listItemWithAccessibilityNode->GetAccessibilityNode().Upgrade();
2230 if (!node) {
2231 continue;
2232 }
2233 bool visible = GetVisible();
2234 if (visible) {
2235 listItemRect.SetSize(listItem->GetLayoutSize());
2236 listItemRect.SetOffset(globalOffset + listItem->GetPosition());
2237 visible = listItemRect.IsIntersectWith(viewPortRect);
2238 }
2239 listItemWithAccessibilityNode->SetAccessibilityVisible(visible);
2240 if (visible) {
2241 Rect clampRect = listItemRect.Constrain(viewPortRect);
2242 listItemWithAccessibilityNode->SetAccessibilityRect(clampRect);
2243 } else {
2244 listItem->NotifyPaintFinish();
2245 }
2246 }
2247 }
2248
ActionByScroll(bool forward,ScrollEventBack scrollEventBack)2249 bool RenderList::ActionByScroll(bool forward, ScrollEventBack scrollEventBack)
2250 {
2251 LOGI("Handle action by Scroll.");
2252 auto node = GetParent().Upgrade();
2253 while (node) {
2254 auto scroll = AceType::DynamicCast<RenderSingleChildScroll>(node);
2255 if (!scroll) {
2256 node = node->GetParent().Upgrade();
2257 continue;
2258 }
2259
2260 scroll->ScrollPage(!forward, true, scrollEventBack);
2261 return true;
2262 }
2263 return false;
2264 }
2265
HandleActionScroll(bool forward)2266 bool RenderList::HandleActionScroll(bool forward)
2267 {
2268 if (isActionByScroll_) {
2269 return ActionByScroll(forward, scrollFinishEventBack_);
2270 }
2271
2272 if (forward) {
2273 JumpToIndex(lastDisplayIndex_);
2274 } else {
2275 JumpToIndex(startIndex_);
2276 }
2277 if (scrollFinishEventBack_) {
2278 scrollFinishEventBack_();
2279 }
2280 return true;
2281 }
2282
ModifyActionScroll()2283 void RenderList::ModifyActionScroll()
2284 {
2285 hasActionScroll_ = true;
2286 }
2287
OnPaintFinish()2288 void RenderList::OnPaintFinish()
2289 {
2290 if (!hasActionScroll_) {
2291 return;
2292 }
2293
2294 hasActionScroll_ = false;
2295 auto context = context_.Upgrade();
2296 if (!context) {
2297 LOGE("RenderList: context is null.");
2298 return;
2299 }
2300
2301 AccessibilityEvent scrollEvent;
2302 scrollEvent.nodeId = GetAccessibilityNodeId();
2303 scrollEvent.eventType = "scrollend";
2304 context->SendEventToAccessibility(scrollEvent);
2305 }
2306
IsUseOnly()2307 bool RenderList::IsUseOnly()
2308 {
2309 return true;
2310 }
2311
PrepareRawRecognizer()2312 bool RenderList::PrepareRawRecognizer()
2313 {
2314 if (rawRecognizer_) {
2315 return true;
2316 }
2317
2318 rawRecognizer_ = AceType::MakeRefPtr<RawRecognizer>();
2319 auto weak = AceType::WeakClaim(this);
2320 rawRecognizer_->SetOnTouchDown([weak](const TouchEventInfo& info) {
2321 if (info.GetTouches().empty()) {
2322 return;
2323 }
2324 auto spThis = weak.Upgrade();
2325 if (spThis) {
2326 spThis->lastPos_ = spThis->GetMainAxis(info.GetTouches().front().GetLocalLocation());
2327 }
2328 });
2329 rawRecognizer_->SetOnTouchMove([weak](const TouchEventInfo& info) {
2330 if (info.GetTouches().empty()) {
2331 return;
2332 }
2333
2334 auto spThis = weak.Upgrade();
2335 if (!spThis || !spThis->selectedItem_) {
2336 return;
2337 }
2338 double currentPos = spThis->GetMainAxis(info.GetTouches().front().GetLocalLocation());
2339 spThis->OnSelectedItemMove(currentPos);
2340 });
2341 rawRecognizer_->SetOnTouchUp([weak](const TouchEventInfo& info) {
2342 auto spThis = weak.Upgrade();
2343 if (spThis) {
2344 spThis->OnSelectedItemStopMoving(false);
2345 }
2346 });
2347 rawRecognizer_->SetOnTouchCancel([weak](const TouchEventInfo& info) {
2348 auto spThis = weak.Upgrade();
2349 if (spThis) {
2350 spThis->OnSelectedItemStopMoving(true);
2351 }
2352 });
2353
2354 return true;
2355 }
2356
OnSelectedItemMove(double position)2357 void RenderList::OnSelectedItemMove(double position)
2358 {
2359 double deltaPos = position - lastPos_;
2360
2361 movingForward_ = LessOrEqual(deltaPos, 0.0);
2362 selectedItemMainAxis_ += deltaPos;
2363 deltaPos = -deltaPos;
2364 if (LessOrEqual(selectedItemMainAxis_, 0.0)) {
2365 selectedItemMainAxis_ = 0.0;
2366 } else {
2367 double maxMainSize = GetMainSize(GetLayoutSize());
2368 double mainSize = GetMainSize(selectedItem_->GetLayoutSize());
2369 if (GreatOrEqual(selectedItemMainAxis_ + mainSize, maxMainSize)) {
2370 selectedItemMainAxis_ = maxMainSize - mainSize;
2371 } else {
2372 deltaPos = 0.0;
2373 lastPos_ = position;
2374 }
2375 }
2376
2377 if (!NearZero(deltaPos)) {
2378 currentOffset_ += deltaPos;
2379 autoScrollingForItemMove_ = true;
2380 }
2381
2382 MarkNeedLayout();
2383 }
2384
OnSelectedItemStopMoving(bool canceled)2385 void RenderList::OnSelectedItemStopMoving(bool canceled)
2386 {
2387 if (!canceled && targetIndex_ != selectedItemIndex_) {
2388 auto from = static_cast<int32_t>(selectedItemIndex_);
2389 auto to = static_cast<int32_t>(targetIndex_);
2390 LOGI("Moving item from %{private}d to %{private}d", from, to);
2391 if (!ResumeEventCallback(component_, &ListComponent::GetOnItemMove, false, from, to)) {
2392 LOGI("User canceled, stop moving item");
2393 }
2394 }
2395
2396 if (selectedItemIndex_ < startIndex_ || selectedItemIndex_ >= startIndex_ + items_.size()) {
2397 RecycleListItem(selectedItemIndex_);
2398 }
2399
2400 targetIndex_ = INITIAL_CHILD_INDEX;
2401 selectedItemIndex_ = INITIAL_CHILD_INDEX;
2402 selectedItem_ = nullptr;
2403 MarkNeedLayout();
2404 }
2405
CreateDragDropRecognizer()2406 void RenderList::CreateDragDropRecognizer()
2407 {
2408 if (dragDropGesture_) {
2409 return;
2410 }
2411
2412 auto longPressRecognizer =
2413 AceType::MakeRefPtr<OHOS::Ace::LongPressRecognizer>(context_, DEFAULT_DURATION, DEFAULT_FINGERS, false);
2414 longPressRecognizer->SetOnAction([weakRenderList = AceType::WeakClaim(this)](const GestureEvent& info) {
2415 auto renderList = weakRenderList.Upgrade();
2416 if (!renderList) {
2417 LOGE("LongPress action RenderList is null.");
2418 return;
2419 }
2420 renderList->scrollable_->MarkAvailable(false);
2421 });
2422 PanDirection panDirection;
2423 auto panRecognizer =
2424 AceType::MakeRefPtr<OHOS::Ace::PanRecognizer>(context_, DEFAULT_FINGERS, panDirection, DEFAULT_DISTANCE);
2425 panRecognizer->SetOnActionStart([weakRenderList = AceType::WeakClaim(this), context = context_,
2426 onItemDragStart = onItemDragStart_](const GestureEvent& info) {
2427 if (onItemDragStart) {
2428 auto pipelineContext = context.Upgrade();
2429 if (!pipelineContext) {
2430 LOGE("Context is null.");
2431 return;
2432 }
2433
2434 auto renderList = weakRenderList.Upgrade();
2435 if (!renderList) {
2436 LOGE("RenderList is null.");
2437 return;
2438 }
2439
2440 ItemDragInfo dragInfo;
2441 dragInfo.SetX(info.GetGlobalPoint().GetX());
2442 dragInfo.SetY(info.GetGlobalPoint().GetY());
2443
2444 Point point = info.GetGlobalPoint() - renderList->GetGlobalOffset();
2445 auto listItem = renderList->FindCurrentListItem(point);
2446 if (!listItem) {
2447 LOGW("There is no listitem at the point.");
2448 return;
2449 }
2450
2451 if (!listItem->IsMovable()) {
2452 LOGI("This list item is not movable.");
2453 return;
2454 }
2455 renderList->selectedDragItem_ = listItem;
2456 renderList->selectedItemIndex_ = renderList->GetIndexByListItem(listItem);
2457 renderList->selectedDragItem_->SetHidden(true);
2458 renderList->MarkNeedLayout();
2459
2460 auto customComponent =
2461 DynamicCast<Component>(onItemDragStart(dragInfo, int32_t(renderList->selectedItemIndex_)));
2462 if (!customComponent) {
2463 LOGE("Custom component is null.");
2464 return;
2465 }
2466 auto stackElement = pipelineContext->GetLastStack();
2467 auto positionedComponent = AceType::MakeRefPtr<PositionedComponent>(customComponent);
2468 positionedComponent->SetTop(Dimension(listItem->GetGlobalOffset().GetY()));
2469 positionedComponent->SetLeft(Dimension(listItem->GetGlobalOffset().GetX()));
2470 renderList->SetBetweenItemAndBuilder(
2471 Offset(info.GetGlobalPoint().GetX() - listItem->GetGlobalOffset().GetX(),
2472 info.GetGlobalPoint().GetY() - listItem->GetGlobalOffset().GetY()));
2473
2474 auto updatePosition = [weak = weakRenderList](
2475 const std::function<void(const Dimension&, const Dimension&)>& func) {
2476 auto renderList = weak.Upgrade();
2477 if (!renderList) {
2478 return;
2479 }
2480 renderList->SetUpdateBuilderFuncId(func);
2481 };
2482
2483 positionedComponent->SetUpdatePositionFuncId(updatePosition);
2484 stackElement->PushComponent(positionedComponent);
2485 renderList->hasDragItem_ = true;
2486 }
2487 });
2488 panRecognizer->SetOnActionUpdate(
2489 [weakRenderList = AceType::WeakClaim(this), context = context_](const GestureEvent& info) {
2490 auto pipelineContext = context.Upgrade();
2491 if (!pipelineContext) {
2492 LOGE("Context is null.");
2493 return;
2494 }
2495
2496 auto renderList = weakRenderList.Upgrade();
2497 if (!renderList) {
2498 LOGE("RenderList is null.");
2499 return;
2500 }
2501
2502 ItemDragInfo dragInfo;
2503 dragInfo.SetX(info.GetGlobalPoint().GetX());
2504 dragInfo.SetY(info.GetGlobalPoint().GetY());
2505
2506 Point point = info.GetGlobalPoint() - renderList->GetBetweenItemAndBuilder();
2507 if (renderList->GetUpdateBuilderFuncId()) {
2508 renderList->GetUpdateBuilderFuncId()(Dimension(point.GetX()), Dimension(point.GetY()));
2509 }
2510
2511 auto targetRenderlist = renderList->FindTargetRenderNode<V2::RenderList>(pipelineContext, info);
2512 auto preTargetRenderlist = renderList->GetPreTargetRenderList();
2513 if (preTargetRenderlist == targetRenderlist) {
2514 if (targetRenderlist && targetRenderlist->GetOnItemDragMove()) {
2515 Point point = info.GetGlobalPoint() - targetRenderlist->GetGlobalOffset();
2516 auto newListItem = targetRenderlist->FindCurrentListItem(point);
2517 if (static_cast<int32_t>(targetRenderlist->GetIndexByListItem(newListItem)) > -1) {
2518 renderList->insertItemIndex_ = targetRenderlist->GetIndexByListItem(newListItem);
2519 }
2520 if (targetRenderlist == renderList) {
2521 (targetRenderlist->GetOnItemDragMove())(dragInfo,
2522 static_cast<int32_t>(renderList->selectedItemIndex_), renderList->insertItemIndex_);
2523 } else {
2524 (targetRenderlist->GetOnItemDragMove())(dragInfo, -1, renderList->insertItemIndex_);
2525 }
2526 }
2527 return;
2528 }
2529 if (preTargetRenderlist) {
2530 if (preTargetRenderlist->GetOnItemDragLeave()) {
2531 (preTargetRenderlist->GetOnItemDragLeave())(
2532 dragInfo, static_cast<int32_t>(renderList->selectedItemIndex_));
2533 }
2534 }
2535 if (targetRenderlist) {
2536 if (targetRenderlist->GetOnItemDragEnter()) {
2537 (targetRenderlist->GetOnItemDragEnter())(dragInfo);
2538 }
2539 }
2540 renderList->SetPreTargetRenderList(targetRenderlist);
2541 });
2542 panRecognizer->SetOnActionEnd([weakRenderList = AceType::WeakClaim(this), context = context_](
2543 const GestureEvent& info) {
2544 auto pipelineContext = context.Upgrade();
2545 if (!pipelineContext) {
2546 LOGE("Context is null.");
2547 return;
2548 }
2549
2550 auto renderList = weakRenderList.Upgrade();
2551 if (!renderList) {
2552 LOGE("RenderList is null.");
2553 return;
2554 }
2555 if (!renderList->selectedDragItem_ || !renderList->selectedDragItem_->IsMovable()) {
2556 return;
2557 }
2558
2559 ItemDragInfo dragInfo;
2560 dragInfo.SetX(info.GetGlobalPoint().GetX());
2561 dragInfo.SetY(info.GetGlobalPoint().GetY());
2562 if (renderList->hasDragItem_) {
2563 auto stackElement = pipelineContext->GetLastStack();
2564 stackElement->PopComponent();
2565 renderList->hasDragItem_ = false;
2566 }
2567
2568 ACE_DCHECK(renderList->GetPreTargetRenderList() ==
2569 renderList->FindTargetRenderNode<V2::RenderList>(pipelineContext, info));
2570 auto targetRenderlist = renderList->GetPreTargetRenderList();
2571 if (!targetRenderlist) {
2572 (renderList->GetOnItemDrop())(dragInfo, static_cast<int32_t>(renderList->selectedItemIndex_), -1, true);
2573 renderList->SetPreTargetRenderList(nullptr);
2574 renderList->selectedDragItem_->SetHidden(false);
2575 renderList->MarkNeedLayout();
2576 return;
2577 }
2578
2579 renderList->selectedDragItem_->SetHidden(false);
2580 if (targetRenderlist->GetOnItemDrop()) {
2581 Point point = info.GetGlobalPoint() - targetRenderlist->GetGlobalOffset();
2582 auto newListItem = targetRenderlist->FindCurrentListItem(point);
2583 if (static_cast<int32_t>(targetRenderlist->GetIndexByListItem(newListItem)) > -1) {
2584 renderList->insertItemIndex_ = static_cast<size_t>(targetRenderlist->GetIndexByListItem(newListItem));
2585 }
2586 if (targetRenderlist == renderList) {
2587 int32_t from = static_cast<int32_t>(renderList->selectedItemIndex_);
2588 int32_t to = static_cast<int32_t>(renderList->insertItemIndex_);
2589 auto moveRes =
2590 ResumeEventCallback(renderList->component_, &ListComponent::GetOnItemMove, true, from, to);
2591 (targetRenderlist->GetOnItemDrop())(dragInfo, from, to, moveRes);
2592 renderList->MarkNeedLayout();
2593 } else {
2594 (targetRenderlist->GetOnItemDrop())(dragInfo, -1, renderList->insertItemIndex_, true);
2595 targetRenderlist->MarkNeedLayout();
2596 }
2597 }
2598 renderList->SetPreTargetRenderList(nullptr);
2599 renderList->scrollable_->MarkAvailable(true);
2600 });
2601 panRecognizer->SetOnActionCancel([weakRenderList = AceType::WeakClaim(this), context = context_]() {
2602 auto pipelineContext = context.Upgrade();
2603 if (!pipelineContext) {
2604 LOGE("Context is null.");
2605 return;
2606 }
2607
2608 auto renderList = weakRenderList.Upgrade();
2609 if (!renderList) {
2610 LOGE("RenderList is null.");
2611 return;
2612 }
2613 if (!renderList->selectedDragItem_ || !renderList->selectedDragItem_->IsMovable()) {
2614 return;
2615 }
2616
2617 if (renderList->hasDragItem_) {
2618 auto stackElement = pipelineContext->GetLastStack();
2619 stackElement->PopComponent();
2620 renderList->hasDragItem_ = false;
2621 }
2622
2623 renderList->SetPreTargetRenderList(nullptr);
2624 renderList->selectedDragItem_->SetHidden(false);
2625 renderList->scrollable_->MarkAvailable(true);
2626 renderList->MarkNeedLayout();
2627 });
2628 std::vector<RefPtr<GestureRecognizer>> recognizers { longPressRecognizer, panRecognizer };
2629 dragDropGesture_ = AceType::MakeRefPtr<OHOS::Ace::SequencedRecognizer>(GetContext(), recognizers);
2630 }
2631
FindCurrentListItem(const Point & point)2632 RefPtr<RenderListItem> RenderList::FindCurrentListItem(const Point& point)
2633 {
2634 const auto& children = GetChildren();
2635 for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
2636 auto& child = *iter;
2637 for (auto& rect : child->GetTouchRectList()) {
2638 if (rect.IsInRegion(point)) {
2639 return AceType::DynamicCast<RenderListItem>(child);
2640 }
2641 }
2642 }
2643 return nullptr;
2644 }
2645
CalculateSelectedIndex(const RefPtr<RenderList> targetRenderlist,const GestureEvent & info,Size & selectedItemSize)2646 size_t RenderList::CalculateSelectedIndex(
2647 const RefPtr<RenderList> targetRenderlist, const GestureEvent& info, Size& selectedItemSize)
2648 {
2649 auto listItem = targetRenderlist->FindTargetRenderNode<RenderListItem>(context_.Upgrade(), info);
2650 if (listItem) {
2651 selectedItemSize = listItem->GetLayoutSize();
2652 return targetRenderlist->GetIndexByListItem(listItem);
2653 }
2654
2655 return DEFAULT_INDEX;
2656 }
2657
CalculateInsertIndex(const RefPtr<RenderList> targetRenderlist,const GestureEvent & info,Size selectedItemSize)2658 int32_t RenderList::CalculateInsertIndex(
2659 const RefPtr<RenderList> targetRenderlist, const GestureEvent& info, Size selectedItemSize)
2660 {
2661 if (targetRenderlist->TotalCount() == 0) {
2662 return 0;
2663 }
2664
2665 auto listItem = targetRenderlist->FindTargetRenderNode<RenderListItem>(context_.Upgrade(), info);
2666 if (!listItem) {
2667 GestureEvent newEvent = info;
2668 while (!listItem) {
2669 if (FindTargetRenderNode<V2::RenderList>(context_.Upgrade(), newEvent) != targetRenderlist) {
2670 break;
2671 }
2672 double newX = vertical_ ? newEvent.GetGlobalPoint().GetX()
2673 : newEvent.GetGlobalPoint().GetX() - selectedItemSize.Width();
2674 double newY = vertical_ ? newEvent.GetGlobalPoint().GetY() - selectedItemSize.Height()
2675 : newEvent.GetGlobalPoint().GetY();
2676 newEvent.SetGlobalPoint(Point(newX, newY));
2677 listItem = targetRenderlist->FindTargetRenderNode<RenderListItem>(context_.Upgrade(), newEvent);
2678 }
2679 if (!listItem) {
2680 return 0;
2681 }
2682 if (static_cast<int32_t>(targetRenderlist->GetIndexByListItem(listItem)) > -1) {
2683 return static_cast<int32_t>(targetRenderlist->GetIndexByListItem(listItem)) + 1;
2684 }
2685 return DEFAULT_INDEX_VALUE;
2686 }
2687
2688 if (static_cast<int32_t>(targetRenderlist->GetIndexByListItem(listItem)) > -1) {
2689 return static_cast<int32_t>(targetRenderlist->GetIndexByListItem(listItem));
2690 }
2691
2692 return DEFAULT_INDEX_VALUE;
2693 }
2694
IsAxisScrollable(AxisDirection direction)2695 bool RenderList::IsAxisScrollable(AxisDirection direction)
2696 {
2697 return (((AxisEvent::IsDirectionUp(direction) || AxisEvent::IsDirectionLeft(direction)) && !reachStart_) ||
2698 ((AxisEvent::IsDirectionDown(direction) || AxisEvent::IsDirectionRight(direction)) && !reachEnd_));
2699 }
2700
HandleAxisEvent(const AxisEvent & event)2701 void RenderList::HandleAxisEvent(const AxisEvent& event) {}
2702
HandleMouseEvent(const MouseEvent & event)2703 bool RenderList::HandleMouseEvent(const MouseEvent& event)
2704 {
2705 if (!isMultiSelectable_) {
2706 return false;
2707 }
2708 scrollable_->MarkAvailable(false);
2709
2710 if (event.button == MouseButton::LEFT_BUTTON) {
2711 if (event.action == MouseAction::PRESS) {
2712 Point mousePoint(event.GetOffset().GetX(), event.GetOffset().GetY());
2713 auto listItem = FindChildNodeOfClass<RenderListItem>(mousePoint, mousePoint);
2714 if (listItem && listItem->IsDragStart()) {
2715 forbidMultiSelect_ = true;
2716 }
2717 } else if (event.action == MouseAction::RELEASE) {
2718 forbidMultiSelect_ = false;
2719 }
2720 }
2721
2722 if (forbidMultiSelect_) {
2723 return false;
2724 }
2725
2726 if (event.action == MouseAction::HOVER_EXIT) {
2727 mouseIsHover_ = false;
2728 } else {
2729 mouseIsHover_ = true;
2730 }
2731
2732 auto context = context_.Upgrade();
2733 if (context) {
2734 context->SubscribeCtrlA([wp = AceType::WeakClaim(this)]() {
2735 auto sp = wp.Upgrade();
2736 if (sp) {
2737 if (sp->mouseIsHover_ == true) {
2738 sp->MultiSelectAllWhenCtrlA();
2739 } else {
2740 sp->ClearMultiSelect();
2741 sp->MarkNeedRender();
2742 }
2743 }
2744 });
2745 } else {
2746 LOGE("context is null");
2747 return false;
2748 }
2749
2750 if (context->IsCtrlDown()) {
2751 if (context->IsKeyboardA()) {
2752 MultiSelectAllWhenCtrlA();
2753 return true;
2754 }
2755 HandleMouseEventWhenCtrlDown(event);
2756 return true;
2757 }
2758 selectedItemsWithCtrl_.clear();
2759
2760 if (context->IsShiftDown()) {
2761 HandleMouseEventWhenShiftDown(event);
2762 return true;
2763 }
2764 firstItemWithShift_ = nullptr;
2765
2766 HandleMouseEventWithoutKeyboard(event);
2767 return true;
2768 }
2769
ClearMultiSelect()2770 void RenderList::ClearMultiSelect()
2771 {
2772 for (const auto& listItem : items_) {
2773 if (!listItem) {
2774 continue;
2775 }
2776 listItem->MarkIsSelected(false);
2777 }
2778 }
2779
MultiSelectWithoutKeyboard(const Rect & selectedZone)2780 void RenderList::MultiSelectWithoutKeyboard(const Rect& selectedZone)
2781 {
2782 if (!selectedZone.IsValid()) {
2783 Point mousePoint(selectedZone.GetOffset().GetX(), selectedZone.GetOffset().GetY());
2784 auto listItem = FindChildNodeOfClass<RenderListItem>(mousePoint, mousePoint);
2785 if (!listItem) {
2786 return;
2787 }
2788 if (!listItem->GetSelectable()) {
2789 return;
2790 }
2791 listItem->MarkIsSelected(true);
2792 if (listItem->GetOnSelectId()) {
2793 (listItem->GetOnSelectId())(listItem->IsSelected());
2794 }
2795 return;
2796 }
2797
2798 for (const auto& listItem : items_) {
2799 if (!listItem) {
2800 continue;
2801 }
2802 if (!listItem->GetSelectable()) {
2803 continue;
2804 }
2805 if (!selectedZone.IsIntersectWith(listItem->GetPaintRect())) {
2806 listItem->MarkIsSelected(false);
2807 if (listItem->GetOnSelectId()) {
2808 (listItem->GetOnSelectId())(listItem->IsSelected());
2809 }
2810 continue;
2811 }
2812 listItem->MarkIsSelected(true);
2813 if (listItem->GetOnSelectId()) {
2814 (listItem->GetOnSelectId())(listItem->IsSelected());
2815 }
2816 }
2817 }
2818
HandleMouseEventWithoutKeyboard(const MouseEvent & event)2819 void RenderList::HandleMouseEventWithoutKeyboard(const MouseEvent& event)
2820 {
2821 if (event.button == MouseButton::LEFT_BUTTON) {
2822 if (event.action == MouseAction::PRESS) {
2823 ClearMultiSelect();
2824 mouseStartOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2825 mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2826 auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
2827 MultiSelectWithoutKeyboard(selectedZone);
2828 MarkNeedRender();
2829 } else if (event.action == MouseAction::MOVE) {
2830 mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2831 auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
2832 MultiSelectWithoutKeyboard(selectedZone);
2833 MarkNeedRender();
2834 } else if (event.action == MouseAction::RELEASE) {
2835 mouseStartOffset_ = Offset(0.0, 0.0);
2836 mouseEndOffset_ = Offset(0.0, 0.0);
2837 MarkNeedRender();
2838 }
2839 }
2840 }
2841
GetPressItemWhenShiftDown(const Rect & selectedZone)2842 RefPtr<RenderListItem> RenderList::GetPressItemWhenShiftDown(const Rect& selectedZone)
2843 {
2844 if (!selectedZone.IsValid()) {
2845 Point mousePoint(selectedZone.GetOffset().GetX(), selectedZone.GetOffset().GetY());
2846 auto listItem = FindChildNodeOfClass<RenderListItem>(mousePoint, mousePoint);
2847 if (!listItem) {
2848 return nullptr;
2849 }
2850 if (!listItem->GetSelectable()) {
2851 return nullptr;
2852 }
2853 return listItem;
2854 }
2855 return nullptr;
2856 }
2857
MultiSelectWhenShiftDown(const Rect & selectedZone)2858 void RenderList::MultiSelectWhenShiftDown(const Rect& selectedZone)
2859 {
2860 for (const auto& listItem : items_) {
2861 if (!listItem) {
2862 continue;
2863 }
2864 if (!listItem->GetSelectable()) {
2865 continue;
2866 }
2867 if (!selectedZone.IsIntersectWith(listItem->GetPaintRect())) {
2868 continue;
2869 }
2870 listItem->MarkIsSelected(true);
2871 if (listItem->GetOnSelectId()) {
2872 (listItem->GetOnSelectId())(listItem->IsSelected());
2873 }
2874 }
2875 }
2876
HandleMouseEventWhenShiftDown(const MouseEvent & event)2877 void RenderList::HandleMouseEventWhenShiftDown(const MouseEvent& event)
2878 {
2879 if (event.button == MouseButton::LEFT_BUTTON) {
2880 if (event.action == MouseAction::PRESS) {
2881 mouseStartOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2882 mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2883 auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
2884 if (firstItemWithShift_ == nullptr) {
2885 firstItemWithShift_ = GetPressItemWhenShiftDown(selectedZone);
2886 }
2887 secondItemWithShift_ = GetPressItemWhenShiftDown(selectedZone);
2888 MultiSelectAllInRange(firstItemWithShift_, secondItemWithShift_);
2889 MarkNeedRender();
2890 } else if (event.action == MouseAction::MOVE) {
2891 mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2892 auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
2893 MultiSelectWhenShiftDown(selectedZone);
2894 MarkNeedRender();
2895 } else if (event.action == MouseAction::RELEASE) {
2896 mouseStartOffset_ = Offset(0.0, 0.0);
2897 mouseEndOffset_ = Offset(0.0, 0.0);
2898 MarkNeedRender();
2899 }
2900 }
2901 }
2902
MultiSelectAllInRange(const RefPtr<RenderListItem> & firstItem,const RefPtr<RenderListItem> & secondItem)2903 void RenderList::MultiSelectAllInRange(
2904 const RefPtr<RenderListItem>& firstItem, const RefPtr<RenderListItem>& secondItem)
2905 {
2906 ClearMultiSelect();
2907 if (!firstItem) {
2908 return;
2909 }
2910
2911 if (!secondItem) {
2912 firstItem->MarkIsSelected(true);
2913 if (firstItem->GetOnSelectId()) {
2914 (firstItem->GetOnSelectId())(firstItem->IsSelected());
2915 }
2916 return;
2917 }
2918
2919 auto fromItemIndex = std::min(GetIndexByListItem(firstItem), GetIndexByListItem(secondItem));
2920 auto toItemIndex = std::max(GetIndexByListItem(firstItem), GetIndexByListItem(secondItem));
2921
2922 for (const auto& listItem : items_) {
2923 if (!listItem) {
2924 continue;
2925 }
2926 if (!listItem->GetSelectable()) {
2927 continue;
2928 }
2929
2930 auto nowIndex = GetIndexByListItem(listItem);
2931 if (nowIndex <= toItemIndex && nowIndex >= fromItemIndex) {
2932 listItem->MarkIsSelected(true);
2933 if (listItem->GetOnSelectId()) {
2934 (listItem->GetOnSelectId())(listItem->IsSelected());
2935 }
2936 }
2937 }
2938 }
2939
MultiSelectWhenCtrlDown(const Rect & selectedZone)2940 void RenderList::MultiSelectWhenCtrlDown(const Rect& selectedZone)
2941 {
2942 if (!selectedZone.IsValid()) {
2943 Point mousePoint(selectedZone.GetOffset().GetX(), selectedZone.GetOffset().GetY());
2944 auto listItem = FindChildNodeOfClass<RenderListItem>(mousePoint, mousePoint);
2945 if (!listItem) {
2946 return;
2947 }
2948 if (!listItem->GetSelectable()) {
2949 return;
2950 }
2951
2952 if (selectedItemsWithCtrl_.find(listItem) != selectedItemsWithCtrl_.end()) {
2953 listItem->MarkIsSelected(false);
2954 } else {
2955 listItem->MarkIsSelected(true);
2956 }
2957
2958 if (listItem->GetOnSelectId()) {
2959 (listItem->GetOnSelectId())(listItem->IsSelected());
2960 }
2961 return;
2962 }
2963
2964 for (const auto& listItem : items_) {
2965 if (!listItem) {
2966 continue;
2967 }
2968 if (!listItem->GetSelectable()) {
2969 continue;
2970 }
2971 if (!selectedZone.IsIntersectWith(listItem->GetPaintRect())) {
2972 if (selectedItemsWithCtrl_.find(listItem) != selectedItemsWithCtrl_.end()) {
2973 listItem->MarkIsSelected(true);
2974 } else {
2975 listItem->MarkIsSelected(false);
2976 }
2977 if (listItem->GetOnSelectId()) {
2978 (listItem->GetOnSelectId())(listItem->IsSelected());
2979 }
2980 continue;
2981 }
2982
2983 if (selectedItemsWithCtrl_.find(listItem) != selectedItemsWithCtrl_.end()) {
2984 listItem->MarkIsSelected(false);
2985 } else {
2986 listItem->MarkIsSelected(true);
2987 }
2988
2989 if (listItem->GetOnSelectId()) {
2990 (listItem->GetOnSelectId())(listItem->IsSelected());
2991 }
2992 }
2993 }
2994
HandleMouseEventWhenCtrlDown(const MouseEvent & event)2995 void RenderList::HandleMouseEventWhenCtrlDown(const MouseEvent& event)
2996 {
2997 if (event.button == MouseButton::LEFT_BUTTON) {
2998 if (event.action == MouseAction::PRESS) {
2999 mouseStartOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
3000 mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
3001 auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
3002 MultiSelectWhenCtrlDown(selectedZone);
3003 MarkNeedRender();
3004 } else if (event.action == MouseAction::MOVE) {
3005 mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
3006 auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
3007 MultiSelectWhenCtrlDown(selectedZone);
3008 MarkNeedRender();
3009 } else if (event.action == MouseAction::RELEASE) {
3010 mouseStartOffset_ = Offset(0.0, 0.0);
3011 mouseEndOffset_ = Offset(0.0, 0.0);
3012 MarkNeedRender();
3013 CollectSelectedItems();
3014 }
3015 }
3016 }
3017
CollectSelectedItems()3018 void RenderList::CollectSelectedItems()
3019 {
3020 selectedItemsWithCtrl_.clear();
3021 for (const auto& listItem : items_) {
3022 if (!listItem) {
3023 continue;
3024 }
3025 if (!listItem->GetSelectable()) {
3026 continue;
3027 }
3028 if (listItem->IsSelected()) {
3029 selectedItemsWithCtrl_.insert(listItem);
3030 }
3031 }
3032 }
3033
MultiSelectAllWhenCtrlA()3034 void RenderList::MultiSelectAllWhenCtrlA()
3035 {
3036 for (const auto& listItem : items_) {
3037 if (!listItem) {
3038 continue;
3039 }
3040 if (!listItem->GetSelectable()) {
3041 continue;
3042 }
3043 listItem->MarkIsSelected(true);
3044 if (listItem->GetOnSelectId()) {
3045 (listItem->GetOnSelectId())(listItem->IsSelected());
3046 }
3047 }
3048 MarkNeedRender();
3049 }
3050
RequestNextFocus(bool vertical,bool reverse)3051 int32_t RenderList::RequestNextFocus(bool vertical, bool reverse)
3052 {
3053 bool rightToLeft_ = false;
3054 int32_t moveStep = DIRECTION_MAP.at(rightToLeft_).at(vertical_).at(vertical).at(reverse);
3055 if (moveStep == STEP_INVALID) {
3056 return -1;
3057 }
3058 focusIndex_ += moveStep;
3059 return focusIndex_;
3060 }
3061
ProvideRestoreInfo()3062 std::string RenderList::ProvideRestoreInfo()
3063 {
3064 if (firstDisplayIndex_ > 0) {
3065 return std::to_string(firstDisplayIndex_);
3066 }
3067 return "";
3068 }
3069
ApplyRestoreInfo()3070 void RenderList::ApplyRestoreInfo()
3071 {
3072 if (GetRestoreInfo().empty()) {
3073 return;
3074 }
3075 startIndex_ = static_cast<size_t>(StringUtils::StringToInt(GetRestoreInfo()));
3076 SetRestoreInfo("");
3077 }
3078
LayoutChild(RefPtr<RenderNode> child,double referencePos,bool forward)3079 void RenderList::LayoutChild(RefPtr<RenderNode> child, double referencePos, bool forward)
3080 {
3081 auto innerLayout = MakeInnerLayout();
3082 auto renderNode = child;
3083 RefPtr<RenderListItemGroup> listItemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
3084 if (listItemGroup) {
3085 renderNode = listItemGroup->GetRenderNode();
3086 ListItemLayoutParam param = {
3087 .startCacheCount = (cachedCount_ > 0 && !isLaneList_) ? cachedCount_ - startCachedCount_ : 0,
3088 .endCacheCount = (cachedCount_ > 0 && !isLaneList_) ? cachedCount_ - endCachedCount_ : 0,
3089 .startMainPos = (cachedCount_ == 0 || isLaneList_) ? startMainPos_ : 0,
3090 .endMainPos = (cachedCount_ == 0 || isLaneList_) ? endMainPos_ : mainSize_,
3091 .listMainSize = mainSize_,
3092 .referencePos = referencePos,
3093 .maxLaneLength = isLaneList_ ? maxLaneLength_ : 0.0,
3094 .forwardLayout = forward,
3095 .isVertical = vertical_,
3096 .sticky = sticky_,
3097 .lanes = isLaneList_ ? lanes_ : 1,
3098 .align = component_->GetAlignListItemAlign(),
3099 };
3100 listItemGroup->SetItemGroupLayoutParam(param);
3101 listItemGroup->SetNeedLayoutDeep();
3102 } else if (isLaneList_) {
3103 innerLayout = MakeInnerLayoutForLane();
3104 }
3105 if (renderNode) {
3106 renderNode->Layout(innerLayout);
3107 }
3108 }
3109
PaintChild(const RefPtr<RenderNode> & child,RenderContext & context,const Offset & offset)3110 void RenderList::PaintChild(const RefPtr<RenderNode>& child, RenderContext& context, const Offset& offset)
3111 {
3112 RefPtr<RenderListItemGroup> listItemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
3113 if (listItemGroup) {
3114 auto renderNode = listItemGroup->GetRenderNode();
3115 RenderNode::PaintChild(renderNode, context, offset);
3116 } else {
3117 RenderNode::PaintChild(child, context, offset);
3118 }
3119 }
3120
SetChildPosition(RefPtr<RenderNode> child,const Offset & offset)3121 void RenderList::SetChildPosition(RefPtr<RenderNode> child, const Offset& offset)
3122 {
3123 auto renderNode = child;
3124 RefPtr<RenderListItemGroup> listItemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
3125 if (listItemGroup) {
3126 renderNode = listItemGroup->GetRenderNode();
3127 }
3128 if (renderNode) {
3129 renderNode->SetPosition(offset);
3130 }
3131 }
3132
AddChildItem(RefPtr<RenderNode> child)3133 void RenderList::AddChildItem(RefPtr<RenderNode> child)
3134 {
3135 auto renderNode = child;
3136 RefPtr<RenderListItemGroup> listItemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
3137 if (listItemGroup) {
3138 renderNode = listItemGroup->GetRenderNode();
3139 }
3140 AddChild(renderNode);
3141 }
3142
SizeChangeOffset(double newWindowHeight)3143 void RenderList::SizeChangeOffset(double newWindowHeight)
3144 {
3145 auto context = context_.Upgrade();
3146 if (!context) {
3147 return;
3148 }
3149 auto textFieldManager = AceType::DynamicCast<TextFieldManager>(context->GetTextFieldManager());
3150 // only need to offset vertical lists
3151 if (textFieldManager && vertical_) {
3152 // only when textField is onFocus
3153 if (!textFieldManager->GetOnFocusTextField().Upgrade()) {
3154 return;
3155 }
3156 auto position = textFieldManager->GetClickPosition().GetY();
3157 double offset = newWindowHeight - position;
3158 if (LessOrEqual(offset, 0.0)) {
3159 // negative offset to scroll down
3160 currentOffset_ += offset;
3161 startIndexOffset_ += offset;
3162 }
3163 }
3164 }
3165
AdjustForReachEnd(double mainSize,double curMainPos)3166 void RenderList::AdjustForReachEnd(double mainSize, double curMainPos)
3167 {
3168 double delta = mainSize - curMainPos;
3169 for (auto rit = items_.rbegin(); rit != items_.rend(); rit++) {
3170 auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(*rit);
3171 if (itemGroup) {
3172 double size = GetMainSize(itemGroup->GetLayoutSize());
3173 LayoutChild(itemGroup, itemGroup->GetReferencePos() + delta, itemGroup->IsForwardLayout());
3174 double newSize = GetMainSize(itemGroup->GetLayoutSize());
3175 delta -= (newSize - size);
3176 }
3177 }
3178 currentOffset_ += delta;
3179 }
3180
AdjustForReachStart(double & curMainPos)3181 void RenderList::AdjustForReachStart(double& curMainPos)
3182 {
3183 double delta = currentOffset_;
3184 for (const auto& child : items_) {
3185 auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
3186 if (itemGroup) {
3187 double size = GetMainSize(itemGroup->GetLayoutSize());
3188 LayoutChild(itemGroup, itemGroup->GetReferencePos() - delta, itemGroup->IsForwardLayout());
3189 double newSize = GetMainSize(itemGroup->GetLayoutSize());
3190 delta -= (newSize - size);
3191 }
3192 }
3193 curMainPos -= delta;
3194 }
3195
3196 } // namespace OHOS::Ace::V2
3197