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/grid/render_grid_scroll.h"
17
18 #include "base/log/event_report.h"
19 #include "core/common/text_field_manager.h"
20 #include "core/components_v2/grid/grid_scroll_controller.h"
21 #include "core/event/ace_event_helper.h"
22
23 namespace OHOS::Ace::V2 {
24
25 namespace {
26
27 const char UNIT_PERCENT[] = "%";
28 const char UNIT_RATIO[] = "fr";
29 constexpr int32_t TIME_THRESHOLD = 3 * 1000000; // 3 millisecond
30 constexpr double JUMP_INDEX_THRESHOLD = 2.0;
31
32 } // namespace
33
~RenderGridScroll()34 RenderGridScroll::~RenderGridScroll()
35 {
36 if (scrollBarProxy_) {
37 scrollBarProxy_->UnRegisterScrollableNode(AceType::WeakClaim(this));
38 }
39 }
40
Update(const RefPtr<Component> & component)41 void RenderGridScroll::Update(const RefPtr<Component>& component)
42 {
43 component_ = AceType::DynamicCast<GridLayoutComponent>(component);
44 ACE_DCHECK(component_);
45 if (!NeedUpdate(component)) {
46 InitScrollBar();
47 return;
48 }
49 useScrollable_ = SCROLLABLE::NO_SCROLL;
50 mainSize_ = &rowSize_;
51 crossSize_ = &colSize_;
52 mainCount_ = &rowCount_;
53 crossCount_ = &colCount_;
54 crossGap_ = &colGap_;
55 mainGap_ = &rowGap_;
56 startRankItemIndex_ = 0;
57 currentItemIndex_ = 0;
58 // maybe change ItemIndex
59 ApplyRestoreInfo();
60 RenderGridLayout::Update(component);
61 FindRefreshParent(AceType::WeakClaim(this));
62 InitScrollBar();
63 TakeBoundary();
64 const RefPtr<GridLayoutComponent> grid = AceType::DynamicCast<GridLayoutComponent>(component);
65 if (!grid) {
66 LOGE("RenderGridLayout update failed.");
67 EventReport::SendRenderException(RenderExcepType::RENDER_COMPONENT_ERR);
68 return;
69 }
70 scrolledEventFun_ =
71 AceAsyncEvent<void(const std::shared_ptr<GridEventInfo>&)>::Create(grid->GetScrolledEvent(), context_);
72
73 scrollBarProxy_ = grid->GetScrollBarProxy();
74 InitScrollBarProxy();
75 }
76
NeedUpdate(const RefPtr<Component> & component)77 bool RenderGridScroll::NeedUpdate(const RefPtr<Component>& component)
78 {
79 const RefPtr<GridLayoutComponent> grid = AceType::DynamicCast<GridLayoutComponent>(component);
80 if (!grid) {
81 LOGE("RenderGridLayout update failed.");
82 EventReport::SendRenderException(RenderExcepType::RENDER_COMPONENT_ERR);
83 return false;
84 }
85 auto controller = grid->GetController();
86 if (controller) {
87 controller->SetScrollNode(WeakClaim(this));
88 }
89 if (!animator_) {
90 animator_ = CREATE_ANIMATOR(GetContext());
91 }
92 cacheCount_ = grid->GetCacheCount();
93
94 if (direction_ != grid->GetDirection() || crossAxisAlign_ != grid->GetFlexAlign() ||
95 gridWidth_ != grid->GetWidth() || gridHeight_ != grid->GetHeight() || colsArgs_ != grid->GetColumnsArgs() ||
96 rowsArgs_ != grid->GetRowsArgs() || userColGap_ != grid->GetColumnGap() || userRowGap_ != grid->GetRowGap() ||
97 rightToLeft_ != grid->GetRightToLeft()) {
98 return true;
99 }
100 return false;
101 }
102
AddChildByIndex(int32_t index,const RefPtr<RenderNode> & renderNode)103 void RenderGridScroll::AddChildByIndex(int32_t index, const RefPtr<RenderNode>& renderNode)
104 {
105 auto iter = items_.try_emplace(index, renderNode);
106 if (iter.second) {
107 AddChild(renderNode);
108 RefPtr<RenderGridLayoutItem> node = AceType::DynamicCast<RenderGridLayoutItem>(renderNode);
109 if (node) {
110 node->SetBoundary();
111 node->SetIndex(index);
112 node->SetHidden(false);
113 }
114 }
115 }
116
CreateScrollable()117 void RenderGridScroll::CreateScrollable()
118 {
119 scrollable_ = nullptr;
120 if (useScrollable_ == SCROLLABLE::NO_SCROLL) {
121 return;
122 }
123
124 auto callback = [weak = AceType::WeakClaim(this)](double offset, int32_t source) {
125 auto grid = weak.Upgrade();
126 if (!grid) {
127 return false;
128 }
129 // Stop animator of scroll bar.
130 auto scrollBarProxy = grid->scrollBarProxy_;
131 if (scrollBarProxy) {
132 scrollBarProxy->StopScrollBarAnimator();
133 }
134 return grid->UpdateScrollPosition(offset, source);
135 };
136 scrollable_ = AceType::MakeRefPtr<Scrollable>(
137 callback, useScrollable_ == SCROLLABLE::HORIZONTAL ? Axis::HORIZONTAL : Axis::VERTICAL);
138 scrollable_->SetScrollEndCallback([weak = AceType::WeakClaim(this)]() {
139 auto grid = weak.Upgrade();
140 if (grid) {
141 auto proxy = grid->scrollBarProxy_;
142 if (proxy) {
143 proxy->StartScrollBarAnimator();
144 }
145 auto scrollBar = grid->scrollBar_;
146 if (scrollBar) {
147 scrollBar->HandleScrollBarEnd();
148 }
149 }
150 });
151 InitializeScrollable(scrollable_);
152 scrollable_->Initialize(context_);
153 }
154
CheckJumpToIndex(double offset)155 void RenderGridScroll::CheckJumpToIndex(double offset)
156 {
157 int32_t index = startShowItemIndex_;
158 double dtIndex = -offset / estimateAverageHeight_;
159 double remainOffset = 0.0;
160 if (dtIndex >= 0) {
161 auto idx = static_cast<int32_t>(dtIndex);
162 index = endShowItemIndex_ + idx;
163 if (index >= GetItemTotalCount()) {
164 index = GetItemTotalCount() - 1;
165 } else {
166 remainOffset = -offset - idx * estimateAverageHeight_;
167 }
168 } else {
169 auto idx = static_cast<int32_t>(-dtIndex);
170 index = startShowItemIndex_ - idx;
171 if (index < 0) {
172 index = 0;
173 } else {
174 remainOffset = -offset + idx * estimateAverageHeight_;
175 }
176 }
177 int32_t rankIndex = GetStartingItem(index);
178 if (((index - rankIndex) * estimateAverageHeight_) > (colSize_* JUMP_INDEX_THRESHOLD)) {
179 currentOffset_ += offset;
180 return;
181 }
182 ScrollToIndex(rankIndex);
183 currentOffset_ = remainOffset + (index - rankIndex) * estimateAverageHeight_;
184 }
185
UpdateScrollPosition(double offset,int32_t source)186 bool RenderGridScroll::UpdateScrollPosition(double offset, int32_t source)
187 {
188 if (source == SCROLL_FROM_START) {
189 return true;
190 }
191
192 if (NearZero(offset)) {
193 return true;
194 }
195 if (scrollBar_ && scrollBar_->NeedScrollBar()) {
196 scrollBar_->SetActive(SCROLL_FROM_CHILD != source);
197 }
198 if (reachHead_ && HandleRefreshEffect(offset, source, currentOffset_)) {
199 return false;
200 }
201 if (reachHead_ && reachTail_) {
202 return false;
203 }
204
205 if (rightToLeft_ && useScrollable_ == SCROLLABLE::HORIZONTAL) {
206 offset = -offset;
207 }
208 if (offset > 0.0) {
209 if (reachHead_) {
210 return false;
211 }
212 reachTail_ = false;
213 } else {
214 if (reachTail_) {
215 return false;
216 }
217 reachHead_ = false;
218 }
219 // If the offset is from the scroll bar and is large, use scroll to index to improve performance
220 if (source == SCROLL_FROM_BAR && std::abs(offset) > colSize_ * JUMP_INDEX_THRESHOLD) {
221 CheckJumpToIndex(offset);
222 } else {
223 currentOffset_ += offset;
224 }
225 MarkNeedLayout(true);
226 return true;
227 }
228
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)229 void RenderGridScroll::OnTouchTestHit(
230 const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
231 {
232 if (!GetVisible()) {
233 return;
234 }
235 if (!scrollable_) {
236 return;
237 }
238 if (scrollable_->Available() && scrollBar_ && scrollBar_->InBarRegion(globalPoint_ - coordinateOffset)) {
239 scrollBar_->AddScrollBarController(coordinateOffset, result);
240 } else {
241 scrollable_->SetCoordinateOffset(coordinateOffset);
242 scrollable_->SetDragTouchRestrict(touchRestrict);
243 }
244 result.emplace_back(scrollable_);
245 }
246
IsChildrenTouchEnable()247 bool RenderGridScroll::IsChildrenTouchEnable()
248 {
249 return scrollable_->IsMotionStop();
250 }
251
SetChildPosition(const RefPtr<RenderNode> & child,int32_t main,int32_t cross,int32_t mainSpan,int32_t crossSpan)252 void RenderGridScroll::SetChildPosition(
253 const RefPtr<RenderNode>& child, int32_t main, int32_t cross, int32_t mainSpan, int32_t crossSpan)
254 {
255 // Calculate the position for current child.
256 double positionMain = 0.0;
257 double positionCross = 0.0;
258 if (main < startIndex_) {
259 positionMain -= GetSize(gridCells_.at(main).at(0));
260 positionMain += (main - startIndex_) * (*mainGap_);
261 } else {
262 for (int32_t i = startIndex_; i < main; ++i) {
263 positionMain += GetSize(gridCells_.at(i).at(0));
264 }
265 positionMain += (main - startIndex_) * (*mainGap_);
266 }
267
268 for (int32_t i = 0; i < cross; ++i) {
269 positionCross += GetSize(gridCells_.at(main).at(i), false);
270 }
271 positionCross += cross * (*crossGap_);
272
273 // Calculate the size for current child.
274 double mainLen = 0.0;
275 double crossLen = 0.0;
276 for (int32_t i = 0; i < mainSpan; ++i) {
277 mainLen += GetSize(gridCells_.at(main + i).at(0));
278 }
279
280 mainLen += (mainSpan - 1) * (*mainGap_);
281 for (int32_t i = 0; i < crossSpan; ++i) {
282 crossLen += GetSize(gridCells_.at(main).at(cross + i), false);
283 }
284 crossLen += (crossSpan - 1) * (*crossGap_);
285
286 // If RTL, place the item from right.
287 if (rightToLeft_) {
288 if (useScrollable_ != SCROLLABLE::HORIZONTAL) {
289 positionCross = colSize_ - positionCross - crossLen;
290 } else {
291 positionMain = colSize_ - positionMain - mainLen;
292 }
293 }
294
295 double mainOffset = (mainLen - GetSize(child->GetLayoutSize())) / 2.0;
296 double crossOffset = (crossLen - GetSize(child->GetLayoutSize(), false)) / 2.0;
297
298 Offset offset;
299 if (useScrollable_ != SCROLLABLE::HORIZONTAL) {
300 offset = Offset(positionCross + crossOffset, positionMain + mainOffset - firstItemOffset_);
301 } else {
302 if (rightToLeft_) {
303 offset = Offset(positionMain + mainOffset + firstItemOffset_, positionCross + crossOffset);
304 } else {
305 offset = Offset(positionMain + mainOffset - firstItemOffset_, positionCross + crossOffset);
306 }
307 }
308
309 child->SetPosition(offset);
310 }
311
GetItemMainIndex(const RefPtr<RenderNode> & child,bool isMain) const312 int32_t RenderGridScroll::GetItemMainIndex(const RefPtr<RenderNode>& child, bool isMain) const
313 {
314 if (useScrollable_ == SCROLLABLE::HORIZONTAL) {
315 if (isMain) {
316 return GetItemColumnIndex(child);
317 } else {
318 return GetItemRowIndex(child);
319 }
320 } else {
321 if (isMain) {
322 return GetItemRowIndex(child);
323 } else {
324 return GetItemColumnIndex(child);
325 }
326 }
327 }
328
SetMainSize(Size & dst,const Size & src)329 void RenderGridScroll::SetMainSize(Size& dst, const Size& src)
330 {
331 if (useScrollable_ == SCROLLABLE::HORIZONTAL) {
332 dst.SetWidth(src.Width());
333 } else {
334 dst.SetHeight(src.Height());
335 }
336 }
337
GetSize(const Size & src,bool isMain) const338 double RenderGridScroll::GetSize(const Size& src, bool isMain) const
339 {
340 if (useScrollable_ == SCROLLABLE::HORIZONTAL) {
341 return isMain ? src.Width() : src.Height();
342 }
343
344 return isMain ? src.Height() : src.Width();
345 }
346
SizeChangeOffset(double newWindowHeight)347 void RenderGridScroll::SizeChangeOffset(double newWindowHeight)
348 {
349 auto context = context_.Upgrade();
350 if (!context) {
351 return;
352 }
353 auto textFieldManager = AceType::DynamicCast<TextFieldManager>(context->GetTextFieldManager());
354 // only need to offset vertical lists
355 if (textFieldManager && (useScrollable_ == SCROLLABLE::VERTICAL)) {
356 // only when textField is onFocus
357 if (!textFieldManager->GetOnFocusTextField().Upgrade()) {
358 return;
359 }
360 auto position = textFieldManager->GetClickPosition().GetY();
361 double offset = newWindowHeight + GetGlobalOffset().GetY() - position;
362 if (LessOrEqual(offset, 0.0)) {
363 // negative offset to scroll down
364 textFieldOffset_ = offset;
365 }
366 }
367 }
368
GetGridSize()369 bool RenderGridScroll::GetGridSize()
370 {
371 double rowSize = ((gridHeight_ > 0.0) && (gridHeight_ < GetLayoutParam().GetMaxSize().Height()))
372 ? gridHeight_
373 : GetLayoutParam().GetMaxSize().Height();
374 double colSize = ((gridWidth_ > 0.0) && (gridWidth_ < GetLayoutParam().GetMaxSize().Width()))
375 ? gridWidth_
376 : GetLayoutParam().GetMaxSize().Width();
377 if (NearEqual(rowSize_, Size::INFINITE_SIZE)) {
378 if ((rowsArgs_.find(UNIT_PERCENT) != std::string::npos || rowsArgs_.find(UNIT_RATIO) != std::string::npos)) {
379 rowSize = viewPort_.Height();
380 }
381 } else if (rowsArgs_.empty()) {
382 useScrollable_ = SCROLLABLE::VERTICAL;
383 }
384 if (NearEqual(colSize_, Size::INFINITE_SIZE)) {
385 if ((colsArgs_.find(UNIT_PERCENT) != std::string::npos || colsArgs_.find(UNIT_RATIO) != std::string::npos)) {
386 colSize = viewPort_.Width();
387 }
388 } else if (colsArgs_.empty()) {
389 useScrollable_ = SCROLLABLE::HORIZONTAL;
390 mainSize_ = &colSize_;
391 crossSize_ = &rowSize_;
392 mainCount_ = &colCount_;
393 crossCount_ = &rowCount_;
394 crossGap_ = &rowGap_;
395 mainGap_ = &colGap_;
396 }
397 if (rowSize != rowSize_ || colSize != colSize_) {
398 rowSize_ = rowSize;
399 colSize_ = colSize;
400 CreateScrollable();
401 SizeChangeOffset(rowSize);
402 return true;
403 }
404 return false;
405 }
406
BuildGrid(std::vector<double> & main,std::vector<double> & cross)407 void RenderGridScroll::BuildGrid(std::vector<double>& main, std::vector<double>& cross)
408 {
409 if (useScrollable_ == SCROLLABLE::NO_SCROLL) {
410 main = ParseArgs(GetRowTemplate(), rowSize_, rowGap_);
411 cross = ParseArgs(GetColumnsTemplate(), colSize_, colGap_);
412 } else if (useScrollable_ == SCROLLABLE::VERTICAL) {
413 cross = ParseArgs(GetColumnsTemplate(), colSize_, colGap_);
414 int32_t col = 0;
415 for (auto width : cross) {
416 metaData_[col] = Size(width, Size::INFINITE_SIZE);
417 ++col;
418 }
419 } else if (useScrollable_ == SCROLLABLE::HORIZONTAL) {
420 cross = ParseArgs(GetRowTemplate(), rowSize_, rowGap_);
421 int32_t row = 0;
422 for (auto height : cross) {
423 metaData_[row] = Size(Size::INFINITE_SIZE, height);
424 ++row;
425 }
426 }
427 }
428
InitialGridProp()429 void RenderGridScroll::InitialGridProp()
430 {
431 // Not first time layout after update, no need to initial.
432 if (!GetGridSize() && !updateFlag_) {
433 return;
434 }
435 ACE_SCOPED_TRACE("InitialGridProp");
436 OnDataSourceUpdated(0);
437 rowGap_ = NormalizePercentToPx(userRowGap_, true);
438 colGap_ = NormalizePercentToPx(userColGap_, false);
439 std::vector<double> main;
440 std::vector<double> cross;
441 BuildGrid(main, cross);
442
443 // Initialize the columnCount and rowCount, default is 1
444 *crossCount_ = cross.size();
445 *mainCount_ = main.size();
446 gridCells_.clear();
447 items_.clear();
448 UpdateAccessibilityAttr();
449 if (buildChildByIndex_) {
450 int32_t endIndex = -1;
451 while (endIndex < currentItemIndex_) {
452 if (!Rank(*mainCount_, *mainCount_ == 0 ? startRankItemIndex_ : -1)) {
453 // When [firstLineToBottom_] does not equal to [std::nullopt], it indicates that this [InitialGridProp]
454 // is called after [ScrollToIndex].
455 // This is the case when it scrolls to the last line and the last line is not full.
456 // So we need to add a line to [*mainCount_].
457 (*mainCount_) += firstLineToBottom_ ? 1 : 0;
458 break;
459 }
460 (*mainCount_)++;
461 auto mainIter = gridMatrix_.find(*mainCount_ - 1);
462 if (mainIter == gridMatrix_.end()) {
463 break;
464 }
465 for (int32_t crossIndex = *crossCount_ - 1; crossIndex >= 0; crossIndex--) {
466 auto iter = mainIter->second.find(crossIndex);
467 if (iter != mainIter->second.end()) {
468 endIndex = iter->second;
469 break;
470 }
471 }
472 }
473 currentItemIndex_ = 0;
474
475 SupplyItems(*mainCount_ > 0 ? *mainCount_ - 1 : 0);
476 startIndex_ = *mainCount_ > 0 ? *mainCount_ - 1 : 0;
477
478 if (NearZero(currentOffset_)) {
479 needCalculateViewPort_ = true;
480 }
481 }
482 updateFlag_ = false;
483 if (firstLineToBottom_ && firstLineToBottom_.value()) {
484 // calculate the distance from the first line to the last line
485 currentOffset_ = *mainSize_ - GetSize(gridCells_.at(0).at(0));
486 needCalculateViewPort_ = false;
487 }
488 firstLineToBottom_ = std::nullopt;
489 }
490
BuildLazyGridLayout(int32_t index,double sizeNeed)491 double RenderGridScroll::BuildLazyGridLayout(int32_t index, double sizeNeed)
492 {
493 if (!buildChildByIndex_ || index < 0 || NearZero(sizeNeed)) {
494 return 0.0;
495 }
496 double size = 0.0;
497 int32_t startIndex = index;
498 while (size < sizeNeed) {
499 auto suppleSize = SupplyItems(startIndex);
500 if (NearZero(suppleSize)) {
501 break;
502 }
503 *mainCount_ = ++startIndex;
504 size += suppleSize + *mainGap_;
505 }
506 return size;
507 }
508
CheckGridPlaced(int32_t index,int32_t main,int32_t cross,int32_t & mainSpan,int32_t & crossSpan)509 bool RenderGridScroll::CheckGridPlaced(
510 int32_t index, int32_t main, int32_t cross, int32_t& mainSpan, int32_t& crossSpan)
511 {
512 auto mainIter = gridMatrix_.find(main);
513 if (mainIter != gridMatrix_.end()) {
514 auto crossIter = mainIter->second.find(cross);
515 if (crossIter != mainIter->second.end()) {
516 return false;
517 }
518 }
519 if (cross + crossSpan > *crossCount_) {
520 return false;
521 }
522
523 for (int32_t i = 0; i < mainSpan; i++) {
524 mainIter = gridMatrix_.find(i + main);
525 if (mainIter != gridMatrix_.end()) {
526 for (int32_t j = 0; j < crossSpan; j++) {
527 if (mainIter->second.find(j + cross) != mainIter->second.end()) {
528 return false;
529 }
530 }
531 }
532 }
533
534 for (int32_t i = main; i < main + mainSpan; ++i) {
535 std::map<int32_t, int32_t> mainMap;
536 auto iter = gridMatrix_.find(i);
537 if (iter != gridMatrix_.end()) {
538 mainMap = iter->second;
539 }
540 for (int32_t j = cross; j < cross + crossSpan; ++j) {
541 mainMap.emplace(std::make_pair(j, index));
542 }
543 gridMatrix_[i] = mainMap;
544 }
545 return true;
546 }
547
LayoutChild(const RefPtr<RenderNode> & child,int32_t main,int32_t cross,int32_t mainSpan,int32_t crossSpan,bool needPosition)548 void RenderGridScroll::LayoutChild(const RefPtr<RenderNode>& child, int32_t main, int32_t cross, int32_t mainSpan,
549 int32_t crossSpan, bool needPosition)
550 {
551 auto gridLayoutItem = AceType::DynamicCast<RenderGridLayoutItem>(child);
552 if (!gridLayoutItem) {
553 LOGE("child of GridScroll is not GridLayoutItem!");
554 return;
555 }
556 Dimension itemMainSize;
557 if (useScrollable_ == SCROLLABLE::HORIZONTAL) {
558 itemMainSize = gridLayoutItem->GetGridItemWidth();
559 } else {
560 itemMainSize = gridLayoutItem->GetGridItemHeight();
561 }
562 bool itemMainIsPercent = itemMainSize.Unit() == DimensionUnit::PERCENT;
563 child->Layout(MakeInnerLayoutParam(main, cross, mainSpan, crossSpan, itemMainIsPercent));
564 SetMainSize(gridCells_.at(main).at(cross), child->GetLayoutSize());
565 if (GetSize(gridCells_.at(main).at(0)) < GetSize(gridCells_.at(main).at(cross))) {
566 SetMainSize(gridCells_.at(main).at(0), gridCells_.at(main).at(cross));
567 }
568 if (!needPosition) {
569 return;
570 }
571 if (useScrollable_ != SCROLLABLE::HORIZONTAL) {
572 child->SetPosition(Offset(0, *mainSize_ + *mainGap_));
573 } else {
574 child->SetPosition(Offset(*mainSize_ + *mainGap_, 0));
575 }
576 }
577
GetNextGrid(int32_t & curMain,int32_t & curCross) const578 void RenderGridScroll::GetNextGrid(int32_t& curMain, int32_t& curCross) const
579 {
580 ++curCross;
581 if (curCross >= *crossCount_) {
582 curCross = 0;
583 ++curMain;
584 }
585 }
586
GetPreviousGrid(int32_t & curMain,int32_t & curCross)587 void RenderGridScroll::GetPreviousGrid(int32_t& curMain, int32_t& curCross)
588 {
589 --curCross;
590 if (curCross < 0) {
591 curCross = *crossCount_;
592 --curMain;
593 }
594 }
595
MakeInnerLayoutParam(int32_t main,int32_t cross,int32_t mainSpan,int32_t crossSpan,bool itemIsPercentUnit) const596 LayoutParam RenderGridScroll::MakeInnerLayoutParam(
597 int32_t main, int32_t cross, int32_t mainSpan, int32_t crossSpan, bool itemIsPercentUnit) const
598 {
599 LayoutParam innerLayout;
600 double mainLen = 0.0;
601 double crossLen = 0.0;
602 if (itemIsPercentUnit) {
603 auto maxMainSize = GetSize(GetLayoutParam().GetMaxSize());
604 mainLen += Size::IsValueInfinite(maxMainSize) ? GetSize(viewPort_) : maxMainSize;
605 } else {
606 for (int32_t i = 0; i < mainSpan; ++i) {
607 if (gridCells_.find(main + i) != gridCells_.end() &&
608 gridCells_.at(main + i).find(cross) != gridCells_.at(main + i).end()) {
609 mainLen += GetSize(gridCells_.at(main + i).at(cross));
610 }
611 }
612 mainLen += (mainSpan - 1) * (*mainGap_);
613 }
614 for (int32_t i = 0; i < crossSpan; ++i) {
615 if (gridCells_.find(main) != gridCells_.end() &&
616 gridCells_.at(main).find(cross + i) != gridCells_.at(main).end()) {
617 crossLen += GetSize(gridCells_.at(main).at(cross + i), false);
618 }
619 }
620 crossLen += (crossSpan - 1) * (*crossGap_);
621
622 Size size;
623 if (useScrollable_ == SCROLLABLE::HORIZONTAL) {
624 size = Size(mainLen, crossLen);
625 } else {
626 size = Size(crossLen, mainLen);
627 }
628 if (crossAxisAlign_ == FlexAlign::STRETCH) {
629 innerLayout.SetMinSize(size);
630 innerLayout.SetMaxSize(size);
631 } else {
632 innerLayout.SetMaxSize(size);
633 }
634 return innerLayout;
635 }
636
LoadForward()637 void RenderGridScroll::LoadForward()
638 {
639 auto firstItem = GetStartingItem(startRankItemIndex_ - 1);
640
641 decltype(gridCells_) gridCells(std::move(gridCells_));
642 decltype(gridMatrix_) gridMatrix(std::move(gridMatrix_));
643
644 int32_t count = 0;
645 int32_t endIndex = -1;
646 while (endIndex < startRankItemIndex_ - 1) {
647 if (!Rank(count, count == 0 ? firstItem : -1)) {
648 break;
649 }
650 count++;
651 auto mainIter = gridMatrix_.find(count - 1);
652 if (mainIter == gridMatrix_.end()) {
653 break;
654 }
655 for (int32_t cross = *crossCount_ - 1; cross >= 0; cross--) {
656 auto iter = mainIter->second.find(cross);
657 if (iter != mainIter->second.end()) {
658 endIndex = iter->second;
659 break;
660 }
661 }
662 }
663 startRankItemIndex_ = firstItem;
664 if (count == 0) {
665 return;
666 }
667 for (const auto& item : gridMatrix) {
668 gridMatrix_[item.first + count] = item.second;
669 }
670 for (const auto& item : gridCells) {
671 gridCells_[item.first + count] = item.second;
672 }
673
674 decltype(inCache_) inCache(std::move(inCache_));
675 for (const auto& item : inCache) {
676 inCache_.insert(item + count);
677 }
678
679 *mainCount_ += count;
680 startIndex_ += count;
681 }
682
CalculateViewPort()683 void RenderGridScroll::CalculateViewPort()
684 {
685 if (textFieldOffset_) {
686 currentOffset_ += textFieldOffset_.value();
687 textFieldOffset_.reset();
688 }
689 while (!NearZero(currentOffset_) || needCalculateViewPort_) {
690 if (currentOffset_ > 0) {
691 // [currentOffset_] > 0 means grid items are going to move down
692 // move to top/left of first row/column
693 if (!NearZero(firstItemOffset_)) {
694 if (gridCells_.find(startIndex_ + 1) != gridCells_.end()) {
695 currentOffset_ += GetSize(gridCells_.at(startIndex_++).at(0)) + *mainGap_ - firstItemOffset_;
696 }
697 firstItemOffset_ = 0.0;
698 }
699 while (currentOffset_ > 0) {
700 if (startIndex_ > 0) {
701 if (gridCells_.find(startIndex_ - 1) == gridCells_.end()) {
702 SupplyItems(startIndex_ - 1);
703 }
704 currentOffset_ -= GetSize(gridCells_.at(startIndex_-- - 1).at(0)) + *mainGap_;
705 }
706 if (startIndex_ == 0 && startRankItemIndex_ > 0 && currentOffset_ > 0) {
707 LoadForward();
708 }
709 if (startIndex_ == 0) {
710 break;
711 }
712 }
713 if (currentOffset_ < 0) {
714 firstItemOffset_ -= currentOffset_;
715 } else {
716 if (startIndex_ == 0) {
717 reachHead_ = true;
718 }
719 }
720 currentOffset_ = 0.0;
721
722 auto blank = CalculateBlankOfEnd();
723 if (GreatOrEqual(0.0, blank)) {
724 return;
725 }
726 // request new item until the blank is filled up
727 blank -= BuildLazyGridLayout(*mainCount_, blank);
728 if (LessOrEqual(blank, 0)) {
729 return;
730 }
731 } else {
732 // [currentOffset_] <= 0 means grid items are going to move up.
733 if (!NearZero(firstItemOffset_)) {
734 currentOffset_ -= firstItemOffset_;
735 firstItemOffset_ = 0.0;
736 }
737 // step1: move [currentOffset_] to the last one of [gridCells_]
738 while (startIndex_ < *mainCount_ && (currentOffset_ < 0 || needCalculateViewPort_)) {
739 currentOffset_ += GetSize(gridCells_.at(startIndex_++).at(0)) + *mainGap_;
740 }
741 needCalculateViewPort_ = false;
742 // step2: if [currentOffset_] is positive, it means that we've had enough grid items
743 if (currentOffset_ > 0) {
744 firstItemOffset_ = GetSize(gridCells_.at(--startIndex_).at(0)) + *mainGap_ - currentOffset_;
745 currentOffset_ = 0.0;
746 } else {
747 // step3: if [currentOffset_] is non-positive, it means we need to build more grid items
748 if (!GreatOrEqual(0.0, BuildLazyGridLayout(*mainCount_, -currentOffset_))) {
749 continue;
750 }
751 }
752 currentOffset_ = 0.0;
753 auto blank = CalculateBlankOfEnd();
754 if (GreatOrEqual(0.0, blank)) {
755 return;
756 }
757 // request new item until the blank is filled up
758 blank -= BuildLazyGridLayout(*mainCount_, blank);
759 if (LessOrEqual(blank, 0)) {
760 return;
761 }
762 blank = blank - firstItemOffset_;
763 firstItemOffset_ = 0;
764 // Move up
765 while (blank > 0) {
766 if (startIndex_ == 0 && startRankItemIndex_ > 0) {
767 LoadForward();
768 }
769 if (startIndex_ == 0) {
770 break;
771 }
772 if (gridCells_.find(startIndex_ - 1) == gridCells_.end()) {
773 SupplyItems(startIndex_ - 1);
774 }
775 blank -= GetSize(gridCells_.at(--startIndex_).at(0)) + *mainGap_;
776 }
777 firstItemOffset_ -= blank;
778 if (firstItemOffset_ < 0) {
779 firstItemOffset_ = 0;
780 }
781 reachTail_ = true;
782 }
783 }
784 }
785
CalculateBlankOfEnd()786 double RenderGridScroll::CalculateBlankOfEnd()
787 {
788 double drawLength = 0.0 - firstItemOffset_;
789 for (int32_t main = startIndex_; main < *mainCount_; main++) {
790 drawLength += GetSize(gridCells_.at(main).at(0)) + *mainGap_;
791 if (GreatOrEqual(drawLength, *mainSize_)) {
792 break;
793 }
794 }
795 return *mainSize_ - drawLength;
796 }
797
SupplyItems(int32_t mainIndex,int32_t itemIndex,bool needPosition)798 double RenderGridScroll::SupplyItems(int32_t mainIndex, int32_t itemIndex, bool needPosition)
799 {
800 ACE_SCOPED_TRACE("SupplyItems %d", mainIndex);
801 if (loadingIndex_ == mainIndex) {
802 loadingIndex_ = -1;
803 }
804 if (gridMatrix_.find(mainIndex) == gridMatrix_.end()) {
805 Rank(mainIndex, itemIndex);
806 }
807 gridCells_.try_emplace(mainIndex, metaData_);
808 auto iter = gridMatrix_.find(mainIndex);
809 if (iter != gridMatrix_.end()) {
810 int32_t frontIndex = -1;
811 for (const auto& item : iter->second) {
812 if (item.second != frontIndex) {
813 if (items_.find(item.second) != items_.end() || buildChildByIndex_(item.second)) {
814 int32_t itemRowSpan = GetItemSpan(items_[item.second], true);
815 int32_t itemColSpan = GetItemSpan(items_[item.second], false);
816 LayoutChild(items_[item.second], mainIndex, item.first, itemRowSpan, itemColSpan, needPosition);
817 }
818 }
819 frontIndex = item.second;
820 }
821 inCache_.insert(mainIndex);
822 return NearEqual(GetSize(gridCells_[mainIndex][0]), Size::INFINITE_SIZE) ? 0.0
823 : GetSize(gridCells_[mainIndex][0]);
824 }
825 return 0.0;
826 }
827
Rank(int32_t mainIndex,int32_t itemIndex)828 bool RenderGridScroll::Rank(int32_t mainIndex, int32_t itemIndex)
829 {
830 if (gridMatrix_.find(mainIndex) != gridMatrix_.end()) {
831 return true;
832 }
833 ACE_SCOPED_TRACE("Rank [%d]", mainIndex);
834 if (itemIndex == -1) {
835 auto mainIter = gridMatrix_.find(mainIndex - 1);
836 if (mainIter != gridMatrix_.end()) {
837 for (int32_t cross = *crossCount_ - 1; cross >= 0; cross--) {
838 auto iter = mainIter->second.find(cross);
839 if (iter != mainIter->second.end()) {
840 itemIndex = iter->second + 1;
841 break;
842 }
843 }
844 }
845 }
846 if (itemIndex == -1) {
847 LOGE("failed, itemIndex = -1, mainIndex = %d", mainIndex);
848 return false;
849 }
850
851 bool isFilled = false;
852 int32_t index = mainIndex;
853 int32_t crossIndex = 0;
854 while (!isFilled) {
855 int32_t itemMain = -1;
856 int32_t itemCross = -1;
857 int32_t itemMainSpan = -1;
858 int32_t itemCrossSpan = -1;
859 auto item = items_.find(itemIndex);
860 if (item != items_.end()) {
861 itemMain = GetItemMainIndex(item->second, true);
862 itemCross = GetItemMainIndex(item->second, false);
863 itemMainSpan = GetItemSpan(item->second, useScrollable_ != SCROLLABLE::HORIZONTAL);
864 itemCrossSpan = GetItemSpan(item->second, useScrollable_ == SCROLLABLE::HORIZONTAL);
865 } else {
866 if (!getChildSpanByIndex_(itemIndex, useScrollable_ == SCROLLABLE::HORIZONTAL, itemMain, itemCross,
867 itemMainSpan, itemCrossSpan)) {
868 return false;
869 }
870 }
871
872 if (itemCrossSpan > *crossCount_) {
873 itemIndex++;
874 continue;
875 }
876 if (itemMain >= 0 && itemCross >= 0 && itemCross < *crossCount_ &&
877 CheckGridPlaced(itemIndex, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
878 } else {
879 while (!CheckGridPlaced(itemIndex, mainIndex, crossIndex, itemMainSpan, itemCrossSpan)) {
880 GetNextGrid(mainIndex, crossIndex);
881 if (mainIndex > index) {
882 isFilled = true;
883 break;
884 }
885 }
886 }
887 itemIndex++;
888 }
889 return true;
890 }
891
PerformLayout()892 void RenderGridScroll::PerformLayout()
893 {
894 if (rowsArgs_.empty() && colsArgs_.empty()) {
895 return;
896 }
897 if (RenderGridLayout::GetChildren().empty() && !buildChildByIndex_) {
898 return;
899 }
900 InitialGridProp();
901 CalculateViewPort();
902 showItem_.clear();
903 childrenInRect_.clear();
904 double drawLength = 0.0 - firstItemOffset_;
905 int32_t main = startIndex_ > 0 ? startIndex_ - 1 : startIndex_;
906 for (; main < *mainCount_; main++) {
907 if (gridCells_.find(main) == gridCells_.end()) {
908 continue;
909 }
910 for (int32_t cross = 0; cross < *crossCount_; cross++) {
911 auto mainIter = gridMatrix_.find(main);
912 if (mainIter == gridMatrix_.end()) {
913 continue;
914 }
915 auto crossIter = mainIter->second.find(cross);
916 if (crossIter == mainIter->second.end()) {
917 continue;
918 }
919 if (buildChildByIndex_ && inCache_.count(main) == 0) {
920 SupplyItems(main);
921 }
922 if (showItem_.count(crossIter->second) == 0) {
923 showItem_.insert(crossIter->second);
924 auto item = items_.find(crossIter->second);
925 if (item != items_.end()) {
926 childrenInRect_.push_back(item->second);
927 int32_t itemMainSpan = GetItemSpan(item->second, useScrollable_ != SCROLLABLE::HORIZONTAL);
928 int32_t itemCrossSpan = GetItemSpan(item->second, useScrollable_ == SCROLLABLE::HORIZONTAL);
929 SetChildPosition(item->second, main, cross, itemMainSpan, itemCrossSpan);
930 }
931 }
932 }
933 if (main >= startIndex_) {
934 drawLength += GetSize(gridCells_.at(main).at(0)) + *mainGap_;
935 }
936 if (GreatOrEqual(drawLength, *mainSize_)) {
937 break;
938 }
939 }
940 SetLayoutSize(GetLayoutParam().Constrain(Size(colSize_, rowSize_)));
941 endIndex_ = main;
942 MarkNeedPredictLayout();
943 CalculateWholeSize(drawLength);
944
945 if (rightToLeft_ && useScrollable_ == SCROLLABLE::HORIZONTAL) {
946 lastOffset_ = estimateHeight_ - viewPort_.Width() - (startMainPos_ + firstItemOffset_ + currentOffset_);
947 } else {
948 lastOffset_ = estimatePos_ + startMainPos_ + firstItemOffset_ + currentOffset_;
949 }
950
951 int32_t firstIndex = GetIndexByPosition(0);
952 if (lastFirstIndex_ != firstIndex) {
953 if (!animatorJumpFlag_) {
954 OnScrolled(firstIndex);
955 }
956 lastFirstIndex_ = firstIndex;
957 }
958 animatorJumpFlag_ = false;
959 }
960
DealCache(int32_t start,int32_t end)961 void RenderGridScroll::DealCache(int32_t start, int32_t end)
962 {
963 if (loadingIndex_ != -1) {
964 return;
965 }
966
967 if (!inRankCache_.empty()) {
968 std::set<int32_t> rankCache(std::move(inRankCache_));
969 if (deleteChildByIndex_) {
970 for (auto index : rankCache) {
971 if (items_.find(index) == items_.end()) {
972 deleteChildByIndex_(index);
973 }
974 }
975 }
976 }
977
978 std::set<int32_t> deleteItem;
979 for (const auto& item : inCache_) {
980 if (item < start - cacheCount_ || item > end + cacheCount_) {
981 deleteItem.insert(item);
982 }
983 }
984
985 for (const auto& item : deleteItem) {
986 DeleteItems(item, false);
987 }
988
989 for (int32_t i = 1; i <= cacheCount_; i++) {
990 if (inCache_.count(i + end) == 0) {
991 loadingIndex_ = i + end;
992 break;
993 }
994
995 if (start >= i && inCache_.count(start - i) == 0) {
996 loadingIndex_ = start - i;
997 break;
998 }
999 }
1000 }
1001
DeleteItems(int32_t index,bool isTail)1002 void RenderGridScroll::DeleteItems(int32_t index, bool isTail)
1003 {
1004 if (!deleteChildByIndex_) {
1005 return;
1006 }
1007
1008 auto iter = gridMatrix_.find(index);
1009 if (iter == gridMatrix_.end()) {
1010 return;
1011 }
1012 for (const auto& item : iter->second) {
1013 deleteChildByIndex_(item.second);
1014 RemoveChildByIndex(item.second);
1015 }
1016
1017 inCache_.erase(index);
1018 }
1019
ClearLayout(bool needReservedPlace)1020 void RenderGridScroll::ClearLayout(bool needReservedPlace)
1021 {
1022 if (needReservedPlace) {
1023 RecordLocation();
1024 } else {
1025 currentOffset_ = 0.0;
1026 }
1027 showItem_.clear();
1028 childrenInRect_.clear();
1029 inCache_.clear();
1030
1031 updateFlag_ = true;
1032 reachHead_ = false;
1033 reachTail_ = false;
1034 startMainPos_ = 0.0;
1035 firstItemOffset_ = 0.0;
1036 startIndex_ = 0;
1037 endIndex_ = -1;
1038 lastOffset_ = 0.0;
1039 estimatePos_ = 0.0;
1040 estimateAverageHeight_ = 0.0;
1041 estimateHeight_ = 0.0;
1042
1043 colCount_ = 0;
1044 rowCount_ = 0;
1045
1046 gridMatrix_.clear();
1047 gridCells_.clear();
1048 }
1049
RecordLocation()1050 void RenderGridScroll::RecordLocation()
1051 {
1052 double positionMain = 0.0;
1053
1054 for (int32_t i = 0; i < startIndex_; ++i) {
1055 if (i < static_cast<int32_t>(gridCells_.size())) {
1056 positionMain += GetSize(gridCells_.at(i).at(0));
1057 }
1058 }
1059 positionMain += (startIndex_) * (*mainGap_);
1060 currentOffset_ += -positionMain - firstItemOffset_;
1061 }
1062
ClearItems()1063 void RenderGridScroll::ClearItems()
1064 {
1065 decltype(items_) items(std::move(items_));
1066 for (const auto& item : items) {
1067 if (item.first < startRankItemIndex_) {
1068 deleteChildByIndex_(item.first);
1069 } else {
1070 inRankCache_.emplace(item.first);
1071 }
1072 RemoveChildByIndex(item.first);
1073 }
1074 loadingIndex_ = -1;
1075 }
1076
GetItemMainIndex(int32_t index)1077 int32_t RenderGridScroll::GetItemMainIndex(int32_t index)
1078 {
1079 for (const auto& main : gridMatrix_) {
1080 for (const auto& cross : main.second) {
1081 if (cross.second == index) {
1082 return main.first;
1083 }
1084 }
1085 }
1086 return -1;
1087 }
1088
GetStartingItem(int32_t first)1089 int32_t RenderGridScroll::GetStartingItem(int32_t first)
1090 {
1091 int32_t firstIndex = 0;
1092 int32_t index = first;
1093 int32_t itemMain = -1;
1094 int32_t itemCross = -1;
1095 int32_t itemMainSpan = -1;
1096 int32_t itemCrossSpan = -1;
1097 while (index > 0) {
1098 if (getChildSpanByIndex_(
1099 index, useScrollable_ == SCROLLABLE::HORIZONTAL, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
1100 if (itemCross == 0) {
1101 firstIndex = index;
1102 break;
1103 }
1104 }
1105
1106 index--;
1107 }
1108 return firstIndex;
1109 }
1110
OnDataSourceUpdated(int32_t index)1111 void RenderGridScroll::OnDataSourceUpdated(int32_t index)
1112 {
1113 if (items_.empty() && updateFlag_) {
1114 return;
1115 }
1116 ACE_SCOPED_TRACE("OnDataSourceUpdated %d", index);
1117 auto items = gridMatrix_.find(startIndex_);
1118 if (items != gridMatrix_.end() && !items->second.empty()) {
1119 currentItemIndex_ = items->second.begin()->second;
1120 }
1121 startRankItemIndex_ = GetStartingItem(currentItemIndex_);
1122 auto offset = firstItemOffset_;
1123 ClearItems();
1124 ClearLayout(false);
1125
1126 currentOffset_ = -offset;
1127 MarkNeedLayout();
1128 }
1129
CalculateWholeSize(double drawLength)1130 void RenderGridScroll::CalculateWholeSize(double drawLength)
1131 {
1132 if (gridMatrix_.empty() || gridCells_.empty()) {
1133 return;
1134 }
1135 if (totalCountFlag_) {
1136 int currentItemCount = 0;
1137 auto lastRow = gridMatrix_.rbegin()->second;
1138 if (!lastRow.empty()) {
1139 currentItemCount = lastRow.rbegin()->second;
1140 }
1141 if (currentItemCount != 0) {
1142 totalCountFlag_ = false;
1143 }
1144 }
1145 double scrollBarExtent = 0.0;
1146 double itemCount = 0;
1147 startMainPos_ = 0.0;
1148 for (int index = 0; index < *mainCount_; index++) {
1149 if (index == startIndex_) {
1150 // get the start position in grid
1151 startMainPos_ = scrollBarExtent;
1152 }
1153 if (gridCells_.find(index) == gridCells_.end()) {
1154 continue;
1155 }
1156 itemCount += gridCells_.at(index).size();
1157 scrollBarExtent += GetSize(gridCells_.at(index).at(0)) + *mainGap_;
1158 }
1159 if (itemCount > 0 && !gridCells_.empty()) {
1160 estimateAverageHeight_ = scrollBarExtent / itemCount;
1161 estimateHeight_ = estimateAverageHeight_ * GetItemTotalCount();
1162 auto result = gridMatrix_.find(gridCells_.begin()->first);
1163 if (result != gridMatrix_.end()) {
1164 int32_t startItem = result->second.begin()->second;
1165 estimatePos_ = estimateAverageHeight_ * startItem;
1166 }
1167 }
1168
1169 bool isScrollable = false;
1170 if (estimateHeight_ > GetSize(GetLayoutSize()) || scrollBarExtent > GetSize(GetLayoutSize())) {
1171 isScrollable = true;
1172 }
1173 if (scrollBar_) {
1174 scrollBar_->SetScrollable(isScrollable);
1175 }
1176 if (!isScrollable) {
1177 currentOffset_ = 0.0;
1178 }
1179 }
1180
ScrollPage(bool reverse,bool smooth)1181 void RenderGridScroll::ScrollPage(bool reverse, bool smooth)
1182 {
1183 if (!reverse) {
1184 UpdateScrollPosition(-GetSize(GetLayoutSize()), SCROLL_FROM_JUMP);
1185 } else {
1186 UpdateScrollPosition(GetSize(GetLayoutSize()), SCROLL_FROM_JUMP);
1187 }
1188 }
1189
GetEstimatedHeight()1190 double RenderGridScroll::GetEstimatedHeight()
1191 {
1192 return estimateHeight_;
1193 }
1194
InitScrollBar()1195 void RenderGridScroll::InitScrollBar()
1196 {
1197 if (!component_) {
1198 LOGE("InitScrollBar failed, component_ is null.");
1199 return;
1200 }
1201 if (scrollBar_) {
1202 scrollBar_->SetDisplayMode(component_->GetScrollBar());
1203 if (!component_->GetScrollBarWidth().empty()) {
1204 const auto& width = StringUtils::StringToDimension(component_->GetScrollBarWidth());
1205 scrollBar_->SetInactiveWidth(width);
1206 scrollBar_->SetNormalWidth(width);
1207 scrollBar_->SetActiveWidth(width);
1208 scrollBar_->SetTouchWidth(width);
1209 }
1210 if (!component_->GetScrollBarColor().empty()) {
1211 scrollBarColor_ = Color::FromString(component_->GetScrollBarColor());
1212 scrollBar_->SetForegroundColor(scrollBarColor_);
1213 }
1214 scrollBar_->Reset();
1215 return;
1216 }
1217
1218 const RefPtr<ScrollBarTheme> theme = GetTheme<ScrollBarTheme>();
1219 if (!theme) {
1220 return;
1221 }
1222 RefPtr<GridScrollController> controller = AceType::MakeRefPtr<GridScrollController>();
1223 scrollBar_ = AceType::MakeRefPtr<ScrollBar>(component_->GetScrollBar(), theme->GetShapeMode());
1224 scrollBar_->SetScrollBarController(controller);
1225
1226 // set the scroll bar style
1227 scrollBar_->SetReservedHeight(theme->GetReservedHeight());
1228 scrollBar_->SetMinHeight(theme->GetMinHeight());
1229 scrollBar_->SetMinDynamicHeight(theme->GetMinDynamicHeight());
1230 auto& scrollBarColor = component_->GetScrollBarColor();
1231 if (!scrollBarColor.empty()) {
1232 scrollBarColor_ = Color::FromString(scrollBarColor);
1233 } else {
1234 scrollBarColor_ = theme->GetForegroundColor();
1235 }
1236 scrollBar_->SetForegroundColor(scrollBarColor_);
1237 scrollBar_->SetBackgroundColor(theme->GetBackgroundColor());
1238 scrollBar_->SetPadding(theme->GetPadding());
1239 scrollBar_->SetScrollable(true);
1240 if (!component_->GetScrollBarWidth().empty()) {
1241 const auto& width = StringUtils::StringToDimension(component_->GetScrollBarWidth());
1242 scrollBar_->SetInactiveWidth(width);
1243 scrollBar_->SetNormalWidth(width);
1244 scrollBar_->SetActiveWidth(width);
1245 scrollBar_->SetTouchWidth(width);
1246 } else {
1247 scrollBar_->SetInactiveWidth(theme->GetNormalWidth());
1248 scrollBar_->SetNormalWidth(theme->GetNormalWidth());
1249 scrollBar_->SetActiveWidth(theme->GetActiveWidth());
1250 scrollBar_->SetTouchWidth(theme->GetTouchWidth());
1251 }
1252 if (!isVertical_) {
1253 scrollBar_->SetPositionMode(PositionMode::BOTTOM);
1254 } else {
1255 if (rightToLeft_) {
1256 scrollBar_->SetPositionMode(PositionMode::LEFT);
1257 }
1258 }
1259 scrollBar_->InitScrollBar(AceType::WeakClaim(this), GetContext());
1260 SetScrollBarCallback();
1261 }
1262
InitScrollBarProxy()1263 void RenderGridScroll::InitScrollBarProxy()
1264 {
1265 if (!scrollBarProxy_) {
1266 return;
1267 }
1268 auto&& scrollCallback = [weakScroll = AceType::WeakClaim(this)](double value, int32_t source) {
1269 auto grid = weakScroll.Upgrade();
1270 if (!grid) {
1271 LOGE("render grid is released");
1272 return false;
1273 }
1274 return grid->UpdateScrollPosition(value, source);
1275 };
1276 scrollBarProxy_->UnRegisterScrollableNode(AceType::WeakClaim(this));
1277 scrollBarProxy_->RegisterScrollableNode({ AceType::WeakClaim(this), scrollCallback });
1278 }
1279
SetScrollBarCallback()1280 void RenderGridScroll::SetScrollBarCallback()
1281 {
1282 if (!scrollBar_ || !scrollBar_->NeedScrollBar()) {
1283 return;
1284 }
1285 auto&& barEndCallback = [weakGrid = AceType::WeakClaim(this)](int32_t value) {
1286 auto grid = weakGrid.Upgrade();
1287 if (!grid) {
1288 LOGE("render grid is released");
1289 return;
1290 }
1291 grid->scrollBarOpacity_ = value;
1292 grid->MarkNeedRender();
1293 };
1294 auto&& scrollEndCallback = [weakGrid = AceType::WeakClaim(this)]() {
1295 auto grid = weakGrid.Upgrade();
1296 if (!grid) {
1297 LOGE("render grid is released");
1298 return;
1299 }
1300 };
1301 auto&& scrollCallback = [weakScroll = AceType::WeakClaim(this)](double value, int32_t source) {
1302 auto grid = weakScroll.Upgrade();
1303 if (!grid) {
1304 LOGE("render grid is released");
1305 return false;
1306 }
1307 return grid->UpdateScrollPosition(value, source);
1308 };
1309 scrollBar_->SetCallBack(scrollCallback, barEndCallback, scrollEndCallback);
1310 }
1311
ScrollToIndex(int32_t index)1312 void RenderGridScroll::ScrollToIndex(int32_t index)
1313 {
1314 if (useScrollable_ == SCROLLABLE::NO_SCROLL || index < 0) {
1315 LOGW("Not supported.");
1316 return;
1317 }
1318 auto inputIdx = GetItemMainIndex(index);
1319 if (inputIdx < endIndex_ && inputIdx > startIndex_) {
1320 LOGI("already in map, not need to jump.");
1321 return;
1322 }
1323 auto context = context_.Upgrade();
1324 if (!context) {
1325 LOGE("context is null");
1326 return;
1327 }
1328 startRankItemIndex_ = GetStartingItem(index);
1329 // Build items
1330 if ((index < startShowItemIndex_ || index > endShowItemIndex_) &&
1331 (index - startRankItemIndex_ < static_cast<int32_t>(metaData_.size()))) {
1332 // do not need layout transition
1333 auto option = context->GetExplicitAnimationOption();
1334 context->SaveExplicitAnimationOption(AnimationOption());
1335 firstLineToBottom_.emplace(index > endShowItemIndex_);
1336 if (scrollable_ && !scrollable_->IsStopped()) {
1337 scrollable_->StopScrollable();
1338 }
1339 ClearItems();
1340 ClearLayout(false);
1341 currentOffset_ = 0;
1342 MarkNeedLayout();
1343 context->SaveExplicitAnimationOption(option);
1344 return;
1345 }
1346
1347 if (index < startShowItemIndex_) {
1348 BuildItemsForwardByRange(index, startShowItemIndex_);
1349 } else if (index > endShowItemIndex_) {
1350 BuildItemsBackwardByRange(endShowItemIndex_, index);
1351 }
1352
1353 // when scrollLength > 0, it means grid items moving forward
1354 // when scrollLength < 0, it means grid items moving backward
1355 currentOffset_ = -CalculateScrollLength(index);
1356 PerformLayout();
1357 MarkNeedRender();
1358 }
1359
CalculateScrollLength(int32_t index)1360 double RenderGridScroll::CalculateScrollLength(int32_t index)
1361 {
1362 double scrollLength = 0.0;
1363 auto inputIndex = GetItemMainIndex(index);
1364 do {
1365 if (inputIndex >= endIndex_) {
1366 // when inputIndex >= endIndex_, grid items need to move forward (i.e. jump backward)
1367 double originalTotalLen = 0; // total length from startIndex_ to endIndex_
1368 for (int32_t i = startIndex_; i <= endIndex_; ++i) {
1369 if (gridCells_.find(i) != gridCells_.end()) {
1370 originalTotalLen += GetSize(gridCells_.at(i).at(0)) + *mainGap_;
1371 }
1372 }
1373 // reduce a mainGap because the last item need to be placed to the bottom of viewport
1374 originalTotalLen -= (*mainGap_ + firstItemOffset_);
1375 // [itemLengthOutOfViewport] is the length of grid items that is out of viewport
1376 double itemLengthOutOfViewport = originalTotalLen - GetSize(GetLayoutSize());
1377 double newlyBuiltItemLength = 0;
1378 for (int32_t i = endIndex_; i < inputIndex; ++i) {
1379 SupplyItems(i, index);
1380 if (i != inputIndex) {
1381 newlyBuiltItemLength += GetSize(gridCells_.at(i).at(0)) + *mainGap_;
1382 }
1383 }
1384 // grid items move forward by scrollLength
1385 scrollLength = itemLengthOutOfViewport + newlyBuiltItemLength;
1386 break;
1387 }
1388 if (inputIndex <= startIndex_) {
1389 // when inputIndex <= startIndex_, grid items need to move backward (i.e. jump forward)
1390 double newlyBuiltItemLength = 0;
1391 for (int32_t i = startIndex_; i >= inputIndex; --i) {
1392 SupplyItems(i, index, i != inputIndex);
1393 if (i != inputIndex) {
1394 newlyBuiltItemLength -= (GetSize(gridCells_.at(i).at(0)) + *mainGap_);
1395 }
1396 }
1397 scrollLength = newlyBuiltItemLength - firstItemOffset_;
1398 break;
1399 }
1400 LOGE("branch: [startIndex_ < inputIndex < endIndex_] should not be entered here, please check.");
1401 } while (0);
1402 return scrollLength;
1403 }
1404
BuildItemsBackwardByRange(int32_t startItemIdx,int32_t endItemIdx)1405 void RenderGridScroll::BuildItemsBackwardByRange(int32_t startItemIdx, int32_t endItemIdx)
1406 {
1407 if (startItemIdx >= endItemIdx) {
1408 return;
1409 }
1410 auto itemIndex = startItemIdx;
1411 auto end = endItemIdx;
1412 while (itemIndex <= end) {
1413 if (GetItemMainIndex(itemIndex) != -1) {
1414 ++itemIndex;
1415 continue;
1416 }
1417 int32_t itemMain = -1;
1418 int32_t itemCross = -1;
1419 int32_t itemMainSpan = -1;
1420 int32_t itemCrossSpan = -1;
1421 if (!GetItemPropsByIndex(itemIndex, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
1422 return;
1423 }
1424 if (itemCrossSpan > *crossCount_) {
1425 itemIndex++;
1426 continue;
1427 }
1428 if (itemMain >= 0 && itemCross >= 0 && itemCross < *crossCount_ &&
1429 CheckGridPlaced(itemIndex, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
1430 } else {
1431 // itemMain < 0 means this item is not placed, place it to the end of the gridMatrix_.
1432 if (itemMain < 0) {
1433 itemMain = gridMatrix_.rbegin()->first;
1434 itemCross = gridMatrix_.rbegin()->second.rbegin()->first;
1435 GetNextGrid(itemMain, itemCross);
1436 while (!CheckGridPlaced(itemIndex, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
1437 GetNextGrid(itemMain, itemCross);
1438 }
1439 }
1440 }
1441 itemIndex++;
1442 }
1443 // Check current end main line is placed completely or not.
1444 int32_t lastCross = gridMatrix_.rbegin()->second.rbegin()->first;
1445 ++end;
1446 for (int32_t crossIndex = lastCross + 1; crossIndex < *crossCount_; ++crossIndex) {
1447 int32_t itemMain = -1;
1448 int32_t itemCross = -1;
1449 int32_t itemMainSpan = -1;
1450 int32_t itemCrossSpan = -1;
1451 if (!GetItemPropsByIndex(end, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
1452 return;
1453 }
1454 if (itemCrossSpan > *crossCount_) {
1455 ++end;
1456 continue;
1457 }
1458 itemMain = gridMatrix_.rbegin()->first;
1459 itemCross = itemCross == -1 ? crossIndex : itemCross;
1460 CheckGridPlaced(end, itemMain, itemCross, itemMainSpan, itemCrossSpan);
1461 ++end;
1462 }
1463 }
1464
BuildItemsForwardByRange(int32_t startItemIdx,int32_t endItemIdx)1465 void RenderGridScroll::BuildItemsForwardByRange(int32_t startItemIdx, int32_t endItemIdx)
1466 {
1467 if (startItemIdx <= endItemIdx) {
1468 return;
1469 }
1470 auto itemIndex = startItemIdx;
1471 auto end = endItemIdx;
1472 while (itemIndex >= end) {
1473 if (GetItemMainIndex(itemIndex) != -1) {
1474 --itemIndex;
1475 continue;
1476 }
1477 int32_t itemMain = -1;
1478 int32_t itemCross = -1;
1479 int32_t itemMainSpan = -1;
1480 int32_t itemCrossSpan = -1;
1481 if (!GetItemPropsByIndex(itemIndex, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
1482 return;
1483 }
1484 if (itemCrossSpan > *crossCount_) {
1485 --itemIndex;
1486 continue;
1487 }
1488 if (itemMain >= 0 && itemCross >= 0 && itemCross < *crossCount_ &&
1489 CheckGridPlaced(itemIndex, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
1490 } else {
1491 // itemMain < 0 means this item is not placed, place it to the front of the gridMatrix_.
1492 if (itemMain < 0) {
1493 itemMain = gridMatrix_.begin()->first;
1494 itemCross = gridMatrix_.begin()->second.begin()->first;
1495 GetPreviousGrid(itemMain, itemCross);
1496 while (!CheckGridPlaced(itemIndex, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
1497 GetPreviousGrid(itemMain, itemCross);
1498 }
1499 }
1500 }
1501 --itemIndex;
1502 }
1503 // Check current front main line is placed completely or not.
1504 int32_t firstCross = gridMatrix_.begin()->second.begin()->first;
1505 --end;
1506 for (int32_t crossIndex = firstCross - 1; crossIndex >= 0; --crossIndex) {
1507 int32_t itemMain = -1;
1508 int32_t itemCross = -1;
1509 int32_t itemMainSpan = -1;
1510 int32_t itemCrossSpan = -1;
1511 if (!GetItemPropsByIndex(end, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
1512 return;
1513 }
1514 if (itemCrossSpan > *crossCount_) {
1515 --end;
1516 continue;
1517 }
1518 itemMain = gridMatrix_.begin()->first;
1519 itemCross = itemCross == -1 ? crossIndex : itemCross;
1520 CheckGridPlaced(end, itemMain, itemCross, itemMainSpan, itemCrossSpan);
1521 --end;
1522 }
1523 }
1524
AnimateTo(const Dimension & position,float duration,const RefPtr<Curve> & curve)1525 bool RenderGridScroll::AnimateTo(const Dimension& position, float duration, const RefPtr<Curve>& curve)
1526 {
1527 if (!animator_->IsStopped()) {
1528 animator_->Stop();
1529 }
1530 animator_->ClearInterpolators();
1531 animateDelta_ = 0.0;
1532 auto animation = AceType::MakeRefPtr<CurveAnimation<double>>(GetCurrentOffset(), NormalizeToPx(position), curve);
1533 animation->AddListener([weakScroll = AceType::WeakClaim(this)](double value) {
1534 auto scroll = weakScroll.Upgrade();
1535 if (scroll) {
1536 scroll->DoJump(value, SCROLL_FROM_JUMP);
1537 }
1538 });
1539 animator_->AddInterpolator(animation);
1540 animator_->SetDuration(duration);
1541 animator_->ClearStopListeners();
1542 animator_->AddStopListener([weakScroll = AceType::WeakClaim(this)]() {
1543 auto scroll = weakScroll.Upgrade();
1544 if (scroll) {
1545 scroll->animateDelta_ = 0.0;
1546 }
1547 });
1548 animator_->Play();
1549 return true;
1550 }
1551
CurrentOffset()1552 Offset RenderGridScroll::CurrentOffset()
1553 {
1554 auto ctx = GetContext().Upgrade();
1555 if (!ctx) {
1556 return useScrollable_ == SCROLLABLE::HORIZONTAL ? Offset(GetCurrentOffset(), 0.0)
1557 : Offset(0.0, GetCurrentOffset());
1558 }
1559 auto mainOffset = ctx->ConvertPxToVp(Dimension(GetCurrentOffset(), DimensionUnit::PX));
1560 Offset currentOffset = useScrollable_ == SCROLLABLE::HORIZONTAL ? Offset(mainOffset, 0.0) : Offset(0.0, mainOffset);
1561 return currentOffset;
1562 }
1563
GetItemPropsByIndex(int32_t itemIndex,int32_t & itemMain,int32_t & itemCross,int32_t & itemMainSpan,int32_t & itemCrossSpan)1564 bool RenderGridScroll::GetItemPropsByIndex(
1565 int32_t itemIndex, int32_t& itemMain, int32_t& itemCross, int32_t& itemMainSpan, int32_t& itemCrossSpan)
1566 {
1567 auto item = items_.find(itemIndex);
1568 if (item != items_.end()) {
1569 itemMain = GetItemMainIndex(item->second, true);
1570 itemCross = GetItemMainIndex(item->second, false);
1571 itemMainSpan = GetItemSpan(item->second, useScrollable_ != SCROLLABLE::HORIZONTAL);
1572 itemCrossSpan = GetItemSpan(item->second, useScrollable_ == SCROLLABLE::HORIZONTAL);
1573 } else {
1574 if (!getChildSpanByIndex_(itemIndex, useScrollable_ == SCROLLABLE::HORIZONTAL, itemMain, itemCross,
1575 itemMainSpan, itemCrossSpan)) {
1576 LOGW("Not valid.");
1577 return false;
1578 }
1579 }
1580 return true;
1581 }
1582
ScrollToEdge(OHOS::Ace::ScrollEdgeType edgeType,bool smooth)1583 void RenderGridScroll::ScrollToEdge(OHOS::Ace::ScrollEdgeType edgeType, bool smooth)
1584 {
1585 if (edgeType != ScrollEdgeType::SCROLL_TOP) {
1586 LOGW("Not supported yet");
1587 return;
1588 }
1589 if (items_.empty() && updateFlag_) {
1590 return;
1591 }
1592 if (scrollable_ && !scrollable_->IsStopped()) {
1593 scrollable_->StopScrollable();
1594 }
1595 currentItemIndex_ = 0;
1596 startRankItemIndex_ = GetStartingItem(currentItemIndex_);
1597 ClearItems();
1598 ClearLayout(false);
1599 MarkNeedLayout();
1600 }
1601
DoJump(double position,int32_t source)1602 void RenderGridScroll::DoJump(double position, int32_t source)
1603 {
1604 double delta = position - animateDelta_;
1605 UpdateScrollPosition(delta, source);
1606 animateDelta_ = position;
1607 }
1608
GetIndexByPosition(double position) const1609 int32_t RenderGridScroll::GetIndexByPosition(double position) const
1610 {
1611 int32_t index = 0;
1612 double startPosition = 0.0;
1613 double endPosition = 0.0;
1614
1615 for (const auto& item : childrenInRect_) {
1616 if (!item || item->GetChildren().empty()) {
1617 continue;
1618 }
1619 auto gridItem = AceType::DynamicCast<RenderGridLayoutItem>(item);
1620 if (!gridItem) {
1621 break;
1622 }
1623 startPosition = item->GetPosition().GetY();
1624 endPosition = item->GetPosition().GetY() + item->GetLayoutSize().Height();
1625 if ((position > startPosition && position < endPosition) || NearEqual(position, startPosition) ||
1626 startPosition > position) {
1627 return gridItem->GetIndex();
1628 }
1629 }
1630 return index;
1631 }
1632
OnScrolled(int32_t scrolled) const1633 void RenderGridScroll::OnScrolled(int32_t scrolled) const
1634 {
1635 if (scrolledEventFun_) {
1636 auto event = std::make_shared<GridEventInfo>(scrolled);
1637 if (event) {
1638 scrolledEventFun_(event);
1639 }
1640 }
1641 }
1642
OnPaintFinish()1643 void RenderGridScroll::OnPaintFinish()
1644 {
1645 RenderNode::OnPaintFinish();
1646 if (showItem_.empty()) {
1647 return;
1648 }
1649 auto currentStartItemCount = *showItem_.begin();
1650 auto currentEndItemCount = *showItem_.rbegin();
1651 if ((startShowItemIndex_ != currentStartItemCount) || (endShowItemIndex_ != currentEndItemCount)) {
1652 startShowItemIndex_ = currentStartItemCount;
1653 endShowItemIndex_ = currentEndItemCount;
1654 }
1655 }
1656
OnPredictLayout(int64_t deadline)1657 void RenderGridScroll::OnPredictLayout(int64_t deadline)
1658 {
1659 auto context = context_.Upgrade();
1660 if (!context) {
1661 return;
1662 }
1663 if (!context->IsTransitionStop()) {
1664 LOGI("In page transition, skip predict.");
1665 return;
1666 }
1667 if (loadingIndex_ == -1) {
1668 DealCache(startIndex_, endIndex_);
1669 if (loadingIndex_ == -1) {
1670 if (startIndex_ == 0 && startRankItemIndex_ > 0) {
1671 LoadForward();
1672 MarkNeedPredictLayout();
1673 }
1674 return;
1675 }
1676 }
1677 ACE_SCOPED_TRACE("OnPredictLayout %d", loadingIndex_);
1678 if (gridMatrix_.find(loadingIndex_) != gridMatrix_.end() || Rank(loadingIndex_)) {
1679 auto iter = gridMatrix_.find(loadingIndex_);
1680 if (iter != gridMatrix_.end()) {
1681 for (const auto& item : iter->second) {
1682 if (items_.find(item.second) == items_.end()) {
1683 if (!buildChildByIndex_(item.second)) {
1684 break;
1685 }
1686 }
1687 // Stop predictLayout less than 3 milliseconds before the next vsync arrives.
1688 if (GetSysTimestamp() + TIME_THRESHOLD > deadline) {
1689 MarkNeedPredictLayout();
1690 return;
1691 }
1692 }
1693 SupplyItems(loadingIndex_);
1694 }
1695 MarkNeedPredictLayout();
1696 } else {
1697 loadingIndex_ = -1;
1698 }
1699 }
1700
IsAxisScrollable(AxisDirection direction)1701 bool RenderGridScroll::IsAxisScrollable(AxisDirection direction)
1702 {
1703 return (((AxisEvent::IsDirectionUp(direction) || AxisEvent::IsDirectionLeft(direction)) && !reachHead_) ||
1704 ((AxisEvent::IsDirectionLeft(direction) || AxisEvent::IsDirectionRight(direction)) && !reachTail_));
1705 }
1706
HandleAxisEvent(const AxisEvent & event)1707 void RenderGridScroll::HandleAxisEvent(const AxisEvent& event)
1708 {
1709 }
1710
ProvideRestoreInfo()1711 std::string RenderGridScroll::ProvideRestoreInfo()
1712 {
1713 int32_t currentItemIndex = 0;
1714 auto items = gridMatrix_.find(startIndex_);
1715 if (items != gridMatrix_.end() && !items->second.empty()) {
1716 currentItemIndex = items->second.begin()->second;
1717 }
1718
1719 if (currentItemIndex > 0) {
1720 return std::to_string(currentItemIndex);
1721 }
1722 return "";
1723 }
1724
ApplyRestoreInfo()1725 void RenderGridScroll::ApplyRestoreInfo()
1726 {
1727 if (GetRestoreInfo().empty()) {
1728 return;
1729 }
1730 currentItemIndex_ = StringUtils::StringToInt(GetRestoreInfo());
1731 startRankItemIndex_ = GetStartingItem(currentItemIndex_);
1732 SetRestoreInfo("");
1733 }
1734
1735 } // namespace OHOS::Ace::V2
1736