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