• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_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/components/grid_layout/grid_layout_component.h"
26 #include "core/components/grid_layout/render_grid_layout_item.h"
27 #include "core/components_v2/grid/grid_scroll_controller.h"
28 #include "core/event/ace_event_helper.h"
29 
30 namespace OHOS::Ace::V2 {
31 
32 namespace {
33 
34 const char UNIT_PERCENT[] = "%";
35 const char UNIT_RATIO[] = "fr";
36 constexpr int32_t TIMETHRESHOLD = 3 * 1000000; // 3 millisecond
37 constexpr int32_t MICROSEC_TO_NANOSEC = 1000;
38 
39 } // namespace
40 
ToJSONString() const41 std::string GridEventInfo::ToJSONString() const
42 {
43     return std::string("\"grid\",{\"first\":").append(std::to_string(scrollIndex_)).append("},null");
44 }
45 
~RenderGridScroll()46 RenderGridScroll::~RenderGridScroll()
47 {
48     if (scrollBarProxy_) {
49         scrollBarProxy_->UnRegisterScrollableNode(AceType::WeakClaim(this));
50     }
51 }
52 
Update(const RefPtr<Component> & component)53 void RenderGridScroll::Update(const RefPtr<Component>& component)
54 {
55     InitScrollBar(component);
56     if (!NeedUpdate(component)) {
57         return;
58     }
59 
60     LOGD("RenderGridScroll::Update");
61     useScrollable_ = SCROLLABLE::NO_SCROLL;
62     mainSize_ = &rowSize_;
63     crossSize_ = &colSize_;
64     mainCount_ = &rowCount_;
65     crossCount_ = &colCount_;
66     crossGap_ = &colGap_;
67     mainGap_ = &rowGap_;
68     startRankItemIndex_ = 0;
69     currentItemIndex_ = 0;
70     RenderGridLayout::Update(component);
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_ = AceType::MakeRefPtr<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 itor = items_.try_emplace(index, renderNode);
114     if (itor.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         }
154     });
155     scrollable_->Initialize(context_);
156 }
157 
UpdateScrollPosition(double offset,int32_t source)158 bool RenderGridScroll::UpdateScrollPosition(double offset, int32_t source)
159 {
160     if (source == SCROLL_FROM_START) {
161         return true;
162     }
163 
164     if (NearZero(offset)) {
165         return true;
166     }
167     if (scrollBar_ && scrollBar_->NeedScrollBar()) {
168         scrollBar_->SetActive(SCROLL_FROM_CHILD != source);
169     }
170 
171     if (reachHead_ && reachTail_) {
172         return false;
173     }
174 
175     if (offset > 0.0) {
176         if (reachHead_) {
177             return false;
178         }
179         reachTail_ = false;
180     } else {
181         if (reachTail_) {
182             return false;
183         }
184         reachHead_ = false;
185     }
186 
187     currentOffset_ += Round(offset);
188     MarkNeedLayout(true);
189     return true;
190 }
191 
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)192 void RenderGridScroll::OnTouchTestHit(
193     const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
194 {
195     if (!GetVisible()) {
196         return;
197     }
198     if (!scrollable_ || !scrollable_->Available()) {
199         return;
200     }
201     if (scrollBar_ && scrollBar_->InBarRegion(globalPoint_ - coordinateOffset)) {
202         scrollBar_->AddScrollBarController(coordinateOffset, result);
203     } else {
204         scrollable_->SetCoordinateOffset(coordinateOffset);
205         scrollable_->SetDragTouchRestrict(touchRestrict);
206     }
207     result.emplace_back(scrollable_);
208 }
209 
IsChildrenTouchEnable()210 bool RenderGridScroll::IsChildrenTouchEnable()
211 {
212     return scrollable_->IsMotionStop();
213 }
214 
SetChildPosition(const RefPtr<RenderNode> & child,int32_t main,int32_t cross,int32_t mainSpan,int32_t crossSpan)215 void RenderGridScroll::SetChildPosition(
216     const RefPtr<RenderNode>& child, int32_t main, int32_t cross, int32_t mainSpan, int32_t crossSpan)
217 {
218     // Calculate the position for current child.
219     double positionMain = 0.0;
220     double positionCross = 0.0;
221     if (main < startIndex_) {
222         positionMain -= GetSize(gridCells_.at(main).at(0));
223         positionMain += (main - startIndex_) * (*mainGap_);
224     } else {
225         for (int32_t i = startIndex_; i < main; ++i) {
226             positionMain += GetSize(gridCells_.at(i).at(0));
227         }
228         positionMain += (main - startIndex_) * (*mainGap_);
229     }
230 
231     for (int32_t i = 0; i < cross; ++i) {
232         positionCross += GetSize(gridCells_.at(main).at(i), false);
233     }
234     positionCross += cross * (*crossGap_);
235 
236     // Calculate the size for current child.
237     double mainLen = 0.0;
238     double crossLen = 0.0;
239     for (int32_t i = 0; i < mainSpan; ++i) {
240         mainLen += GetSize(gridCells_.at(main + i).at(0));
241     }
242 
243     mainLen += (mainSpan - 1) * (*mainGap_);
244     for (int32_t i = 0; i < crossSpan; ++i) {
245         crossLen += GetSize(gridCells_.at(main).at(cross + i), false);
246     }
247     crossLen += (crossSpan - 1) * (*crossGap_);
248 
249     // If RTL, place the item from right.
250     if (rightToLeft_) {
251         if (useScrollable_ != SCROLLABLE::HORIZONTAL) {
252             positionCross = colSize_ - positionCross - crossLen;
253         }
254     }
255 
256     double mainOffset = (mainLen - GetSize(child->GetLayoutSize())) / 2.0;
257     double crossOffset = (crossLen - GetSize(child->GetLayoutSize(), false)) / 2.0;
258 
259     Offset offset;
260     if (useScrollable_ != SCROLLABLE::HORIZONTAL) {
261         offset = Offset(positionCross + crossOffset, positionMain + mainOffset - firstItemOffset_);
262     } else {
263         offset = Offset(positionMain + mainOffset - firstItemOffset_, positionCross + crossOffset);
264     }
265 
266     child->SetPosition(offset);
267 }
268 
GetItemMainIndex(const RefPtr<RenderNode> & child,bool isMain) const269 int32_t RenderGridScroll::GetItemMainIndex(const RefPtr<RenderNode>& child, bool isMain) const
270 {
271     if (useScrollable_ == SCROLLABLE::HORIZONTAL) {
272         if (isMain) {
273             return GetItemColumnIndex(child);
274         } else {
275             return GetItemRowIndex(child);
276         }
277     } else {
278         if (isMain) {
279             return GetItemRowIndex(child);
280         } else {
281             return GetItemColumnIndex(child);
282         }
283     }
284 }
285 
SetMainSize(Size & dst,const Size & src)286 void RenderGridScroll::SetMainSize(Size& dst, const Size& src)
287 {
288     if (useScrollable_ == SCROLLABLE::HORIZONTAL) {
289         dst.SetWidth(src.Width());
290     } else {
291         dst.SetHeight(src.Height());
292     }
293 }
294 
GetSize(const Size & src,bool isMain) const295 double RenderGridScroll::GetSize(const Size& src, bool isMain) const
296 {
297     if (useScrollable_ == SCROLLABLE::HORIZONTAL) {
298         return isMain ? src.Width() : src.Height();
299     }
300 
301     return isMain ? src.Height() : src.Width();
302 }
303 
GetGridSize()304 bool RenderGridScroll::GetGridSize()
305 {
306     double rowSize = ((gridHeight_ > 0.0) && (gridHeight_ < GetLayoutParam().GetMaxSize().Height()))
307                          ? gridHeight_
308                          : GetLayoutParam().GetMaxSize().Height();
309     double colSize = ((gridWidth_ > 0.0) && (gridWidth_ < GetLayoutParam().GetMaxSize().Width()))
310                          ? gridWidth_
311                          : GetLayoutParam().GetMaxSize().Width();
312     if (NearEqual(rowSize_, Size::INFINITE_SIZE)) {
313         if ((rowsArgs_.find(UNIT_PERCENT) != std::string::npos || rowsArgs_.find(UNIT_RATIO) != std::string::npos)) {
314             rowSize = viewPort_.Height();
315         }
316     } else if (rowsArgs_.empty()) {
317         useScrollable_ = SCROLLABLE::VERTICAL;
318     }
319     if (NearEqual(colSize_, Size::INFINITE_SIZE)) {
320         if ((colsArgs_.find(UNIT_PERCENT) != std::string::npos || colsArgs_.find(UNIT_RATIO) != std::string::npos)) {
321             colSize = viewPort_.Width();
322         }
323     } else if (colsArgs_.empty()) {
324         useScrollable_ = SCROLLABLE::HORIZONTAL;
325         mainSize_ = &colSize_;
326         crossSize_ = &rowSize_;
327         mainCount_ = &colCount_;
328         crossCount_ = &rowCount_;
329         crossGap_ = &rowGap_;
330         mainGap_ = &colGap_;
331     }
332     LOGD("GetGridSize %lf, %lf   [%lf- %lf]", rowSize, colSize, rowSize_, colSize_);
333     if (rowSize != rowSize_ || colSize != colSize_) {
334         rowSize_ = rowSize;
335         colSize_ = colSize;
336         CreateScrollable();
337         return true;
338     }
339     return false;
340 }
341 
BuildGrid(std::vector<double> & main,std::vector<double> & cross)342 void RenderGridScroll::BuildGrid(std::vector<double>& main, std::vector<double>& cross)
343 {
344     if (useScrollable_ == SCROLLABLE::NO_SCROLL) {
345         main = ParseArgs(PreParseRows(), rowSize_, rowGap_);
346         cross = ParseArgs(PreParseCols(), colSize_, colGap_);
347     } else if (useScrollable_ == SCROLLABLE::VERTICAL) {
348         cross = ParseArgs(PreParseCols(), colSize_, colGap_);
349         int32_t col = 0;
350         for (auto width : cross) {
351             metaData_[col] = Size(width, Size::INFINITE_SIZE);
352             ++col;
353         }
354     } else if (useScrollable_ == SCROLLABLE::HORIZONTAL) {
355         cross = ParseArgs(PreParseRows(), rowSize_, rowGap_);
356         int32_t row = 0;
357         for (auto height : cross) {
358             metaData_[row] = Size(Size::INFINITE_SIZE, height);
359             ++row;
360         }
361     }
362 }
363 
InitialGridProp()364 void RenderGridScroll::InitialGridProp()
365 {
366     // Not first time layout after update, no need to initial.
367     if (!GetGridSize() && !updateFlag_) {
368         return;
369     }
370     ACE_SCOPED_TRACE("InitialGridProp");
371     OnDataSourceUpdated(0);
372     rowGap_ = NormalizePercentToPx(userRowGap_, true);
373     colGap_ = NormalizePercentToPx(userColGap_, false);
374     std::vector<double> main;
375     std::vector<double> cross;
376     BuildGrid(main, cross);
377 
378     // Initialize the columnCount and rowCount, default is 1
379     *crossCount_ = cross.size();
380     *mainCount_ = main.size();
381     gridCells_.clear();
382     items_.clear();
383     UpdateAccessibilityAttr();
384     if (buildChildByIndex_) {
385         int32_t endIndex = -1;
386         while (endIndex < currentItemIndex_) {
387             if (!Rank(*mainCount_, *mainCount_ == 0 ? startRankItemIndex_ : -1)) {
388                 // When [firstLineToBottom_] does not equal to [std::nullopt], it indicates that this [InitialGridProp]
389                 // is called after [ScrollToIndex].
390                 // This is the case when it scrolls to the last line and the last line is not full.
391                 // So we need to add a line to [*mainCount_].
392                 (*mainCount_) += firstLineToBottom_ ? 1 : 0;
393                 break;
394             }
395             (*mainCount_)++;
396             auto mainItor = gridMatrix_.find(*mainCount_ - 1);
397             if (mainItor == gridMatrix_.end()) {
398                 break;
399             }
400             for (int32_t crossIndex = *crossCount_ - 1; crossIndex >= 0; crossIndex--) {
401                 auto iter = mainItor->second.find(crossIndex);
402                 if (iter != mainItor->second.end()) {
403                     endIndex = iter->second;
404                     break;
405                 }
406             }
407         }
408         currentItemIndex_ = 0;
409 
410         SupplyItems(*mainCount_ > 0 ? *mainCount_ - 1 : 0);
411         startIndex_ = *mainCount_ > 0 ? *mainCount_ - 1 : 0;
412 
413         if (NearZero(currentOffset_)) {
414             needCalculateViewPort_ = true;
415         }
416     }
417     updateFlag_ = false;
418     if (firstLineToBottom_ && firstLineToBottom_.value()) {
419         // calculate the distance from the first line to the last line
420         currentOffset_ = *mainSize_ - GetSize(gridCells_.at(0).at(0));
421         needCalculateViewPort_ = false;
422     }
423     firstLineToBottom_ = std::nullopt;
424 }
425 
BuildLazyGridLayout(int32_t index,double sizeNeed)426 double RenderGridScroll::BuildLazyGridLayout(int32_t index, double sizeNeed)
427 {
428     if (!buildChildByIndex_ || index < 0 || NearZero(sizeNeed)) {
429         return 0.0;
430     }
431     LOGD("BuildLazyGridLayout index = %d sizeNeed = %lf", index, sizeNeed);
432     double size = 0.0;
433     int32_t startIndex = index;
434     while (size < sizeNeed) {
435         auto suppleSize = SupplyItems(startIndex);
436         if (NearZero(suppleSize)) {
437             break;
438         }
439         *mainCount_ = ++startIndex;
440         size += suppleSize + *mainGap_;
441     }
442     return size;
443 }
444 
CheckGridPlaced(int32_t index,int32_t main,int32_t cross,int32_t & mainSpan,int32_t & crossSpan)445 bool RenderGridScroll::CheckGridPlaced(
446     int32_t index, int32_t main, int32_t cross, int32_t& mainSpan, int32_t& crossSpan)
447 {
448     LOGD("CheckGridPlaced %{public}d %{public}d %{public}d %{public}d %{public}d", index, main, cross, mainSpan,
449         crossSpan);
450     auto mainIter = gridMatrix_.find(main);
451     if (mainIter != gridMatrix_.end()) {
452         auto crossIter = mainIter->second.find(cross);
453         if (crossIter != mainIter->second.end()) {
454             return false;
455         }
456     }
457     if (cross + crossSpan > *crossCount_) {
458         return false;
459     }
460 
461     for (int32_t i = 0; i < mainSpan; i++) {
462         mainIter = gridMatrix_.find(i + main);
463         if (mainIter != gridMatrix_.end()) {
464             for (int32_t j = 0; j < crossSpan; j++) {
465                 if (mainIter->second.find(j + cross) != mainIter->second.end()) {
466                     return false;
467                 }
468             }
469         }
470     }
471 
472     for (int32_t i = main; i < main + mainSpan; ++i) {
473         std::map<int32_t, int32_t> mainMap;
474         auto iter = gridMatrix_.find(i);
475         if (iter != gridMatrix_.end()) {
476             mainMap = iter->second;
477         }
478         for (int32_t j = cross; j < cross + crossSpan; ++j) {
479             mainMap.emplace(std::make_pair(j, index));
480         }
481         gridMatrix_[i] = mainMap;
482     }
483     LOGD("CheckGridPlaced done %{public}d %{public}d %{public}d %{public}d %{public}d", index, main, cross, mainSpan,
484         crossSpan);
485     return true;
486 }
487 
LayoutChild(const RefPtr<RenderNode> & child,int32_t main,int32_t cross,int32_t mainSpan,int32_t crossSpan,bool needPosition)488 void RenderGridScroll::LayoutChild(const RefPtr<RenderNode>& child, int32_t main, int32_t cross, int32_t mainSpan,
489     int32_t crossSpan, bool needPosition)
490 {
491     child->Layout(MakeInnerLayoutParam(main, cross, mainSpan, crossSpan));
492     SetMainSize(gridCells_.at(main).at(cross), child->GetLayoutSize());
493     if (GetSize(gridCells_.at(main).at(0)) < GetSize(gridCells_.at(main).at(cross))) {
494         SetMainSize(gridCells_.at(main).at(0), gridCells_.at(main).at(cross));
495     }
496     if (!needPosition) {
497         return;
498     }
499     if (useScrollable_ != SCROLLABLE::HORIZONTAL) {
500         child->SetPosition(Offset(0, *mainSize_ + *mainGap_));
501     } else {
502         child->SetPosition(Offset(*mainSize_ + *mainGap_, 0));
503     }
504 }
505 
GetNextGrid(int32_t & curMain,int32_t & curCross) const506 void RenderGridScroll::GetNextGrid(int32_t& curMain, int32_t& curCross) const
507 {
508     ++curCross;
509     if (curCross >= *crossCount_) {
510         curCross = 0;
511         ++curMain;
512     }
513 }
514 
GetPreviousGrid(int32_t & curMain,int32_t & curCross)515 void RenderGridScroll::GetPreviousGrid(int32_t& curMain, int32_t& curCross)
516 {
517     --curCross;
518     if (curCross < 0) {
519         curCross = *crossCount_;
520         --curMain;
521     }
522 }
523 
MakeInnerLayoutParam(int32_t main,int32_t cross,int32_t mainSpan,int32_t crossSpan) const524 LayoutParam RenderGridScroll::MakeInnerLayoutParam(
525     int32_t main, int32_t cross, int32_t mainSpan, int32_t crossSpan) const
526 {
527     LayoutParam innerLayout;
528     double mainLen = 0.0;
529     double crossLen = 0.0;
530     for (int32_t i = 0; i < mainSpan; ++i) {
531         if (gridCells_.find(main + i) != gridCells_.end() &&
532             gridCells_.at(main + i).find(cross) != gridCells_.at(main + i).end()) {
533             mainLen += GetSize(gridCells_.at(main + i).at(cross));
534         }
535     }
536     mainLen += (mainSpan - 1) * (*mainGap_);
537     for (int32_t i = 0; i < crossSpan; ++i) {
538         if (gridCells_.find(main) != gridCells_.end() &&
539             gridCells_.at(main).find(cross + i) != gridCells_.at(main).end()) {
540             crossLen += GetSize(gridCells_.at(main).at(cross + i), false);
541         }
542     }
543     crossLen += (crossSpan - 1) * (*crossGap_);
544 
545     Size size;
546     if (useScrollable_ == SCROLLABLE::HORIZONTAL) {
547         size = Size(mainLen, crossLen);
548     } else {
549         size = Size(crossLen, mainLen);
550     }
551     if (crossAxisAlign_ == FlexAlign::STRETCH) {
552         innerLayout.SetMinSize(size);
553         innerLayout.SetMaxSize(size);
554     } else {
555         innerLayout.SetMaxSize(size);
556     }
557     return innerLayout;
558 }
559 
LoadForward()560 void RenderGridScroll::LoadForward()
561 {
562     auto firstItem = GetStartingItem(startRankItemIndex_ - 1);
563 
564     decltype(gridCells_) gridCells(std::move(gridCells_));
565     decltype(gridMatrix_) gridMatrix(std::move(gridMatrix_));
566 
567     int32_t count = 0;
568     int32_t endIndex = -1;
569     while (endIndex < startRankItemIndex_ - 1) {
570         if (!Rank(count, count == 0 ? firstItem : -1)) {
571             break;
572         }
573         count++;
574         auto mainItor = gridMatrix_.find(count - 1);
575         if (mainItor == gridMatrix_.end()) {
576             break;
577         }
578         for (int32_t cross = *crossCount_ - 1; cross >= 0; cross--) {
579             auto iter = mainItor->second.find(cross);
580             if (iter != mainItor->second.end()) {
581                 endIndex = iter->second;
582                 break;
583             }
584         }
585     }
586     startRankItemIndex_ = firstItem;
587     if (count == 0) {
588         return;
589     }
590     for (const auto& item : gridMatrix) {
591         gridMatrix_[item.first + count] = item.second;
592     }
593     for (const auto& item : gridCells) {
594         gridCells_[item.first + count] = item.second;
595     }
596 
597     decltype(inCache_) inCache(std::move(inCache_));
598     for (const auto& item : inCache) {
599         inCache_.insert(item + count);
600     }
601 
602     *mainCount_ += count;
603     startIndex_ += count;
604 }
605 
CaculateViewPort()606 void RenderGridScroll::CaculateViewPort()
607 {
608     while (!NearZero(currentOffset_) || needCalculateViewPort_) {
609         if (currentOffset_ > 0) {
610             // move to top/left of first row/column
611             if (!NearZero(firstItemOffset_)) {
612                 if (gridCells_.find(startIndex_ + 1) != gridCells_.end()) {
613                     currentOffset_ += GetSize(gridCells_.at(startIndex_++).at(0)) + *mainGap_ - firstItemOffset_;
614                 }
615                 firstItemOffset_ = 0.0;
616             }
617             // Move up
618             while (currentOffset_ > 0) {
619                 if (startIndex_ > 0) {
620                     if (gridCells_.find(startIndex_ - 1) == gridCells_.end()) {
621                         SupplyItems(startIndex_ - 1);
622                     }
623                     currentOffset_ -= GetSize(gridCells_.at(startIndex_-- - 1).at(0)) + *mainGap_;
624                 }
625                 if (startIndex_ == 0 && startRankItemIndex_ > 0 && currentOffset_ > 0) {
626                     LoadForward();
627                 }
628                 if (startIndex_ == 0) {
629                     break;
630                 }
631             }
632             if (currentOffset_ < 0) {
633                 firstItemOffset_ -= currentOffset_;
634             } else {
635                 if (startIndex_ == 0) {
636                     reachHead_ = true;
637                 }
638             }
639             currentOffset_ = 0.0;
640         } else {
641             if (!NearZero(firstItemOffset_)) {
642                 currentOffset_ -= firstItemOffset_;
643                 firstItemOffset_ = 0.0;
644             }
645             // Move down
646             while (startIndex_ < *mainCount_ && (currentOffset_ < 0 || needCalculateViewPort_)) {
647                 currentOffset_ += GetSize(gridCells_.at(startIndex_++).at(0)) + *mainGap_;
648             }
649             needCalculateViewPort_ = false;
650             if (currentOffset_ > 0) {
651                 firstItemOffset_ = GetSize(gridCells_.at(--startIndex_).at(0)) + *mainGap_ - currentOffset_;
652                 currentOffset_ = 0.0;
653             } else {
654                 if (!GreatOrEqual(0.0, BuildLazyGridLayout(*mainCount_, -currentOffset_))) {
655                     continue;
656                 }
657             }
658             currentOffset_ = 0.0;
659             auto blank = CalculateBlankOfEnd();
660             if (GreatOrEqual(0.0, blank)) {
661                 return;
662             }
663             // request new item.
664             blank -= BuildLazyGridLayout(*mainCount_, blank);
665             if (blank < 0) {
666                 return;
667             }
668             blank = blank - firstItemOffset_;
669             firstItemOffset_ = 0;
670             // Move up
671             while (blank > 0) {
672                 if (startIndex_ == 0 && startRankItemIndex_ > 0) {
673                     LoadForward();
674                 }
675                 if (startIndex_ == 0) {
676                     break;
677                 }
678                 if (gridCells_.find(startIndex_ - 1) == gridCells_.end()) {
679                     SupplyItems(startIndex_ - 1);
680                 }
681                 blank -= GetSize(gridCells_.at(--startIndex_).at(0)) + *mainGap_;
682             }
683             firstItemOffset_ -= blank;
684             if (firstItemOffset_ < 0) {
685                 firstItemOffset_ = 0;
686             }
687             reachTail_ = true;
688         }
689     }
690 }
691 
CalculateBlankOfEnd()692 double RenderGridScroll::CalculateBlankOfEnd()
693 {
694     double drawLength = 0.0 - firstItemOffset_;
695     for (int32_t main = startIndex_; main < *mainCount_; main++) {
696         drawLength += GetSize(gridCells_.at(main).at(0)) + *mainGap_;
697         if (GreatOrEqual(drawLength, *mainSize_)) {
698             break;
699         }
700     }
701     return *mainSize_ - drawLength;
702 }
703 
SupplyItems(int32_t mainIndex,int32_t itemIndex,bool needPosition)704 double RenderGridScroll::SupplyItems(int32_t mainIndex, int32_t itemIndex, bool needPosition)
705 {
706     ACE_SCOPED_TRACE("SupplyItems %d", mainIndex);
707     if (loadingIndex_ == mainIndex) {
708         loadingIndex_ = -1;
709     }
710     if (gridMatrix_.find(mainIndex) == gridMatrix_.end()) {
711         Rank(mainIndex, itemIndex);
712     }
713     gridCells_.try_emplace(mainIndex, metaData_);
714     auto iter = gridMatrix_.find(mainIndex);
715     if (iter != gridMatrix_.end()) {
716         int32_t frontIndex = -1;
717         for (const auto& item : iter->second) {
718             if (item.second != frontIndex) {
719                 if (items_.find(item.second) != items_.end() || buildChildByIndex_(item.second)) {
720                     int32_t itemRowSpan = GetItemSpan(items_[item.second], true);
721                     int32_t itemColSpan = GetItemSpan(items_[item.second], false);
722                     LayoutChild(items_[item.second], mainIndex, item.first, itemRowSpan, itemColSpan, needPosition);
723                 }
724             }
725             frontIndex = item.second;
726         }
727         inCache_.insert(mainIndex);
728         return NearEqual(GetSize(gridCells_[mainIndex][0]), Size::INFINITE_SIZE) ? 0.0
729                                                                                  : GetSize(gridCells_[mainIndex][0]);
730     }
731     return 0.0;
732 }
733 
Rank(int32_t mainIndex,int32_t itemIndex)734 bool RenderGridScroll::Rank(int32_t mainIndex, int32_t itemIndex)
735 {
736     if (gridMatrix_.find(mainIndex) != gridMatrix_.end()) {
737         return true;
738     }
739     ACE_SCOPED_TRACE("Rank [%d]", mainIndex);
740     if (itemIndex == -1) {
741         auto mainItor = gridMatrix_.find(mainIndex - 1);
742         if (mainItor != gridMatrix_.end()) {
743             for (int32_t cross = *crossCount_ - 1; cross >= 0; cross--) {
744                 auto iter = mainItor->second.find(cross);
745                 if (iter != mainItor->second.end()) {
746                     itemIndex = iter->second + 1;
747                     break;
748                 }
749             }
750         }
751     }
752     if (itemIndex == -1) {
753         LOGE("failed, itemIndex = -1, mainIndex = %d", mainIndex);
754         return false;
755     }
756 
757     bool isFilled = false;
758     int32_t index = mainIndex;
759     int32_t crossIndex = 0;
760     while (!isFilled) {
761         int32_t itemMain = -1;
762         int32_t itemCross = -1;
763         int32_t itemMainSpan = -1;
764         int32_t itemCrossSpan = -1;
765         auto item = items_.find(itemIndex);
766         if (item != items_.end()) {
767             itemMain = GetItemMainIndex(item->second, true);
768             itemCross = GetItemMainIndex(item->second, false);
769             itemMainSpan = GetItemSpan(item->second, useScrollable_ != SCROLLABLE::HORIZONTAL);
770             itemCrossSpan = GetItemSpan(item->second, useScrollable_ == SCROLLABLE::HORIZONTAL);
771         } else {
772             if (!getChildSpanByIndex_(itemIndex, useScrollable_ == SCROLLABLE::HORIZONTAL, itemMain, itemCross,
773                     itemMainSpan, itemCrossSpan)) {
774                 return false;
775             }
776         }
777 
778         if (itemCrossSpan > *crossCount_) {
779             itemIndex++;
780             continue;
781         }
782         if (itemMain >= 0 && itemCross >= 0 && itemCross < *crossCount_ &&
783             CheckGridPlaced(itemIndex, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
784         } else {
785             while (!CheckGridPlaced(itemIndex, mainIndex, crossIndex, itemMainSpan, itemCrossSpan)) {
786                 GetNextGrid(mainIndex, crossIndex);
787                 if (mainIndex > index) {
788                     isFilled = true;
789                     break;
790                 }
791             }
792         }
793         itemIndex++;
794     }
795     return true;
796 }
797 
PerformLayout()798 void RenderGridScroll::PerformLayout()
799 {
800     if (rowsArgs_.empty() && colsArgs_.empty()) {
801         return;
802     }
803     if (RenderGridLayout::GetChildren().empty() && !buildChildByIndex_) {
804         return;
805     }
806     lastOffset_ = startMainPos_ + firstItemOffset_ - currentOffset_;
807     InitialGridProp();
808     CaculateViewPort();
809     showItem_.clear();
810     childrenInRect_.clear();
811     double drawLength = 0.0 - firstItemOffset_;
812     int32_t main = startIndex_ > 0 ? startIndex_ - 1 : startIndex_;
813     LOGD("startIndex_=[%d], firstItemOffset_=[%lf]", startIndex_, firstItemOffset_);
814     for (; main < *mainCount_; main++) {
815         if (gridCells_.find(main) == gridCells_.end()) {
816             continue;
817         }
818         for (int32_t cross = 0; cross < *crossCount_; cross++) {
819             auto mainIter = gridMatrix_.find(main);
820             if (mainIter == gridMatrix_.end()) {
821                 continue;
822             }
823             auto crossIter = mainIter->second.find(cross);
824             if (crossIter == mainIter->second.end()) {
825                 continue;
826             }
827             if (buildChildByIndex_ && inCache_.count(main) == 0) {
828                 SupplyItems(main);
829             }
830             if (showItem_.count(crossIter->second) == 0) {
831                 showItem_.insert(crossIter->second);
832                 auto item = items_.find(crossIter->second);
833                 if (item != items_.end()) {
834                     childrenInRect_.push_back(item->second);
835                     int32_t itemMainSpan = GetItemSpan(item->second, useScrollable_ != SCROLLABLE::HORIZONTAL);
836                     int32_t itemCrossSpan = GetItemSpan(item->second, useScrollable_ == SCROLLABLE::HORIZONTAL);
837                     SetChildPosition(item->second, main, cross, itemMainSpan, itemCrossSpan);
838                 }
839             }
840         }
841         if (main >= startIndex_) {
842             drawLength += GetSize(gridCells_.at(main).at(0)) + *mainGap_;
843         }
844         if (GreatOrEqual(drawLength, *mainSize_)) {
845             break;
846         }
847     }
848     SetLayoutSize(GetLayoutParam().Constrain(Size(colSize_, rowSize_)));
849     endIndex_ = main;
850     MarkNeedPredictLayout();
851     CalculateWholeSize(drawLength);
852 
853     int32_t firstIndex = GetIndexByPosition(0);
854     if (lastFirstIndex_ != firstIndex) {
855         if (!animatorJumpFlag_) {
856             OnScrolled(firstIndex);
857         }
858         lastFirstIndex_ = firstIndex;
859     }
860     animatorJumpFlag_ = false;
861 }
862 
DealCache(int32_t start,int32_t end)863 void RenderGridScroll::DealCache(int32_t start, int32_t end)
864 {
865     if (loadingIndex_ != -1) {
866         return;
867     }
868 
869     std::set<int32_t> deleteItem;
870     for (const auto& item : inCache_) {
871         if (item < start - cacheCount_ || item > end + cacheCount_) {
872             deleteItem.insert(item);
873         }
874     }
875 
876     for (const auto& item : deleteItem) {
877         DeleteItems(item, false);
878     }
879 
880     for (int32_t i = 1; i <= cacheCount_; i++) {
881         if (inCache_.count(i + end) == 0) {
882             loadingIndex_ = i + end;
883             break;
884         }
885 
886         if (start >= i && inCache_.count(start - i) == 0) {
887             loadingIndex_ = start - i;
888             break;
889         }
890     }
891 }
892 
DeleteItems(int32_t index,bool isTail)893 void RenderGridScroll::DeleteItems(int32_t index, bool isTail)
894 {
895     if (!deleteChildByIndex_) {
896         return;
897     }
898 
899     auto iter = gridMatrix_.find(index);
900     if (iter == gridMatrix_.end()) {
901         return;
902     }
903     for (const auto& item : iter->second) {
904         deleteChildByIndex_(item.second);
905         RemoveChildByIndex(item.second);
906     }
907 
908     inCache_.erase(index);
909 }
910 
ClearLayout(bool needReservedPlace)911 void RenderGridScroll::ClearLayout(bool needReservedPlace)
912 {
913     if (needReservedPlace) {
914         RecordLocation();
915     } else {
916         currentOffset_ = 0.0;
917     }
918     showItem_.clear();
919     childrenInRect_.clear();
920     inCache_.clear();
921 
922     updateFlag_ = true;
923     reachHead_ = false;
924     reachTail_ = false;
925     startMainPos_ = 0.0;
926     firstItemOffset_ = 0.0;
927     startIndex_ = 0;
928     endIndex_ = -1;
929     mainScrollExtent_ = 0.0;
930     lastOffset_ = 0.0;
931     estimateHeight_ = 0.0;
932 
933     colCount_ = 0;
934     rowCount_ = 0;
935 
936     gridMatrix_.clear();
937     gridCells_.clear();
938 }
939 
RecordLocation()940 void RenderGridScroll::RecordLocation()
941 {
942     double positionMain = 0.0;
943 
944     for (int32_t i = 0; i < startIndex_; ++i) {
945         if (i < static_cast<int32_t>(gridCells_.size())) {
946             positionMain += GetSize(gridCells_.at(i).at(0));
947         }
948     }
949     positionMain += (startIndex_) * (*mainGap_);
950     currentOffset_ += -positionMain - firstItemOffset_;
951 }
952 
ClearItems()953 void RenderGridScroll::ClearItems()
954 {
955     decltype(items_) items(std::move(items_));
956     for (const auto& item : items) {
957         if (item.first < startRankItemIndex_) {
958             deleteChildByIndex_(item.first);
959         }
960         RemoveChildByIndex(item.first);
961     }
962     loadingIndex_ = -1;
963 }
964 
GetMinAndMaxIndex(int32_t & min,int32_t & max)965 void RenderGridScroll::GetMinAndMaxIndex(int32_t& min, int32_t& max)
966 {
967     for (const auto& item : items_) {
968         if (item.first < min) {
969             min = item.first;
970         }
971         if (item.first > max) {
972             max = item.first;
973         }
974     }
975 }
976 
GetItemMainIndex(int32_t index)977 int32_t RenderGridScroll::GetItemMainIndex(int32_t index)
978 {
979     for (const auto& main : gridMatrix_) {
980         for (const auto& cross : main.second) {
981             if (cross.second == index) {
982                 return main.first;
983             }
984         }
985     }
986     return -1;
987 }
988 
GetStartingItem(int32_t first)989 int32_t RenderGridScroll::GetStartingItem(int32_t first)
990 {
991     int32_t firstIndex = 0;
992     int32_t index = first;
993     int32_t itemMain = -1;
994     int32_t itemCross = -1;
995     int32_t itemMainSpan = -1;
996     int32_t itemCrossSpan = -1;
997     while (index > 0) {
998         if (getChildSpanByIndex_(
999                 index, useScrollable_ == SCROLLABLE::HORIZONTAL, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
1000             LOGD("index %d %d,  %d,  %d,  %d", index, itemMain, itemCross, itemMainSpan, itemCrossSpan);
1001             if (itemCross == 0) {
1002                 firstIndex = index;
1003                 break;
1004             }
1005         }
1006 
1007         index--;
1008     }
1009     return firstIndex;
1010 }
1011 
OnDataSourceUpdated(int32_t index)1012 void RenderGridScroll::OnDataSourceUpdated(int32_t index)
1013 {
1014     if (items_.empty() && updateFlag_) {
1015         return;
1016     }
1017     ACE_SCOPED_TRACE("OnDataSourceUpdated %d", index);
1018     auto items = gridMatrix_.find(startIndex_);
1019     if (items != gridMatrix_.end() && !items->second.empty()) {
1020         currentItemIndex_ = items->second.begin()->second;
1021     }
1022     startRankItemIndex_ = GetStartingItem(currentItemIndex_);
1023     auto offset = firstItemOffset_;
1024     ClearItems();
1025     ClearLayout(false);
1026 
1027     currentOffset_ = -offset;
1028     MarkNeedLayout();
1029 }
1030 
CalculateWholeSize(double drawLength)1031 void RenderGridScroll::CalculateWholeSize(double drawLength)
1032 {
1033     if (gridMatrix_.empty() || gridCells_.empty()) {
1034         return;
1035     }
1036     if (totalCountFlag_) {
1037         int currentItemCount = 0;
1038         auto lastRow = gridMatrix_.rbegin()->second;
1039         if (!lastRow.empty()) {
1040             currentItemCount = lastRow.rbegin()->second;
1041         }
1042         if (currentItemCount != 0) {
1043             mainScrollExtent_ = 0.0;
1044             // calculate the whole size
1045             mainScrollExtent_ = static_cast<double>(totalCount_) / static_cast<double>(currentItemCount) * drawLength;
1046             estimateHeight_ = mainScrollExtent_;
1047             totalCountFlag_ = false;
1048         }
1049     }
1050     scrollBarExtent_ = 0.0;
1051     startMainPos_ = 0.0;
1052     for (int index = 0; index < *mainCount_; index++) {
1053         if (index == startIndex_) {
1054             // get the start position in grid
1055             startMainPos_ = scrollBarExtent_;
1056         }
1057         if (gridCells_.find(index) == gridCells_.end()) {
1058             continue;
1059         }
1060         scrollBarExtent_ += GetSize(gridCells_.at(index).at(0)) + *mainGap_;
1061     }
1062     bool isScrollable = false;
1063     if (estimateHeight_ > GetSize(GetLayoutSize()) || scrollBarExtent_ > GetSize(GetLayoutSize())) {
1064         isScrollable = true;
1065     }
1066     if (scrollBar_) {
1067         scrollBar_->SetScrollable(isScrollable);
1068     }
1069     if (!isScrollable) {
1070         currentOffset_ = 0.0;
1071     }
1072 }
1073 
ScrollPage(bool reverse,bool smooth)1074 void RenderGridScroll::ScrollPage(bool reverse, bool smooth)
1075 {
1076     if (!reverse) {
1077         UpdateScrollPosition(-GetSize(GetLayoutSize()), SCROLL_FROM_JUMP);
1078     } else {
1079         UpdateScrollPosition(GetSize(GetLayoutSize()), SCROLL_FROM_JUMP);
1080     }
1081 }
1082 
GetEstimatedHeight()1083 double RenderGridScroll::GetEstimatedHeight()
1084 {
1085     if (reachTail_) {
1086         // reach the end of grid, update the total scroll bar length
1087         estimateHeight_ = scrollBarExtent_;
1088     } else {
1089         estimateHeight_ = std::max(estimateHeight_, scrollBarExtent_);
1090     }
1091     return estimateHeight_;
1092 }
1093 
InitScrollBar(const RefPtr<Component> & component)1094 void RenderGridScroll::InitScrollBar(const RefPtr<Component>& component)
1095 {
1096     if (scrollBar_) {
1097         scrollBar_->Reset();
1098         return;
1099     }
1100     const RefPtr<GridLayoutComponent> grid = AceType::DynamicCast<GridLayoutComponent>(component);
1101     if (!grid) {
1102         LOGE("RenderGridLayout update failed.");
1103         EventReport::SendRenderException(RenderExcepType::RENDER_COMPONENT_ERR);
1104         return;
1105     }
1106     const RefPtr<ScrollBarTheme> theme = GetTheme<ScrollBarTheme>();
1107     if (!theme) {
1108         return;
1109     }
1110     RefPtr<GridScrollController> controller = AceType::MakeRefPtr<GridScrollController>();
1111     scrollBar_ = AceType::MakeRefPtr<ScrollBar>(grid->GetScrollBar(), theme->GetShapeMode());
1112     scrollBar_->SetScrollBarController(controller);
1113 
1114     // set the scroll bar style
1115     scrollBar_->SetReservedHeight(theme->GetReservedHeight());
1116     scrollBar_->SetMinHeight(theme->GetMinHeight());
1117     scrollBar_->SetMinDynamicHeight(theme->GetMinDynamicHeight());
1118     auto& scrollBarColor = grid->GetScrollBarColor();
1119     if (!scrollBarColor.empty()) {
1120         scrollBarColor_ = Color::FromString(scrollBarColor);
1121     } else {
1122         scrollBarColor_ = theme->GetForegroundColor();
1123     }
1124     scrollBar_->SetForegroundColor(scrollBarColor_);
1125     scrollBar_->SetBackgroundColor(theme->GetBackgroundColor());
1126     scrollBar_->SetPadding(theme->GetPadding());
1127     scrollBar_->SetScrollable(true);
1128     if (!grid->GetScrollBarWidth().empty()) {
1129         const auto& width = StringUtils::StringToDimension(grid->GetScrollBarWidth());
1130         scrollBar_->SetInactiveWidth(width);
1131         scrollBar_->SetNormalWidth(width);
1132         scrollBar_->SetActiveWidth(width);
1133         scrollBar_->SetTouchWidth(width);
1134     } else {
1135         scrollBar_->SetInactiveWidth(theme->GetNormalWidth());
1136         scrollBar_->SetNormalWidth(theme->GetNormalWidth());
1137         scrollBar_->SetActiveWidth(theme->GetActiveWidth());
1138         scrollBar_->SetTouchWidth(theme->GetTouchWidth());
1139     }
1140     scrollBar_->InitScrollBar(AceType::WeakClaim(this), GetContext());
1141     SetScrollBarCallback();
1142 }
1143 
InitScrollBarProxy()1144 void RenderGridScroll::InitScrollBarProxy()
1145 {
1146     if (!scrollBarProxy_) {
1147         return;
1148     }
1149     auto&& scrollCallback = [weakScroll = AceType::WeakClaim(this)](double value, int32_t source) {
1150         auto grid = weakScroll.Upgrade();
1151         if (!grid) {
1152             LOGE("render grid is released");
1153             return false;
1154         }
1155         return grid->UpdateScrollPosition(value, source);
1156     };
1157     scrollBarProxy_->UnRegisterScrollableNode(AceType::WeakClaim(this));
1158     scrollBarProxy_->RegisterScrollableNode({ AceType::WeakClaim(this), scrollCallback });
1159 }
1160 
SetScrollBarCallback()1161 void RenderGridScroll::SetScrollBarCallback()
1162 {
1163     if (!scrollBar_ || !scrollBar_->NeedScrollBar()) {
1164         return;
1165     }
1166     auto&& barEndCallback = [weakGrid = AceType::WeakClaim(this)](int32_t value) {
1167         auto grid = weakGrid.Upgrade();
1168         if (!grid) {
1169             LOGE("render grid is released");
1170             return;
1171         }
1172         grid->scrollBarOpacity_ = value;
1173         grid->MarkNeedRender();
1174     };
1175     auto&& scrollEndCallback = [weakGrid = AceType::WeakClaim(this)]() {
1176         auto grid = weakGrid.Upgrade();
1177         if (!grid) {
1178             LOGE("render grid is released");
1179             return;
1180         }
1181         LOGD("trigger scroll end callback");
1182     };
1183     auto&& scrollCallback = [weakScroll = AceType::WeakClaim(this)](double value, int32_t source) {
1184         auto grid = weakScroll.Upgrade();
1185         if (!grid) {
1186             LOGE("render grid is released");
1187             return false;
1188         }
1189         return grid->UpdateScrollPosition(value, source);
1190     };
1191     scrollBar_->SetCallBack(scrollCallback, barEndCallback, scrollEndCallback);
1192 }
1193 
ScrollToIndex(int32_t index,int32_t source)1194 void RenderGridScroll::ScrollToIndex(int32_t index, int32_t source)
1195 {
1196     if (useScrollable_ == SCROLLABLE::NO_SCROLL || index < 0) {
1197         LOGW("Not supported.");
1198         return;
1199     }
1200     auto inputIdx = GetItemMainIndex(index);
1201     if (inputIdx < endIndex_ && inputIdx > startIndex_) {
1202         LOGI("already in map, not need to jump.");
1203         return;
1204     }
1205     auto context = context_.Upgrade();
1206     if (!context) {
1207         LOGE("context is null");
1208         return;
1209     }
1210     // Build items
1211     if (index < startShowItemIndex_ || index > endShowItemIndex_) {
1212         // do not need layout transition
1213         auto option = context->GetExplicitAnimationOption();
1214         context->SaveExplicitAnimationOption(AnimationOption());
1215         firstLineToBottom_.emplace(index > endShowItemIndex_);
1216         if (scrollable_ && !scrollable_->IsStopped()) {
1217             scrollable_->StopScrollable();
1218         }
1219         startRankItemIndex_ = GetStartingItem(index);
1220         ClearItems();
1221         ClearLayout(false);
1222         currentOffset_ = 0;
1223         MarkNeedLayout();
1224         context->SaveExplicitAnimationOption(option);
1225         return;
1226     }
1227     // Calculate scroll length
1228     double scrollLength = 0.0;
1229     inputIdx = GetItemMainIndex(index);
1230     if (inputIdx >= endIndex_) {
1231         double mainLen = -firstItemOffset_;
1232         for (int32_t i = startIndex_; i <= endIndex_; ++i) {
1233             if (gridCells_.find(i) != gridCells_.end()) {
1234                 mainLen += GetSize(gridCells_.at(i).at(0)) + *mainGap_;
1235             }
1236         }
1237         scrollLength += (mainLen - GetSize(GetLayoutSize()));
1238         for (int32_t i = endIndex_; i < inputIdx; ++i) {
1239             SupplyItems(i, index);
1240             if (i != inputIdx) {
1241                 scrollLength += GetSize(gridCells_.at(i).at(0)) + *mainGap_;
1242             }
1243         }
1244         scrollLength = -scrollLength;
1245     } else if (inputIdx <= startIndex_) {
1246         scrollLength += firstItemOffset_;
1247         for (int32_t i = startIndex_; i >= inputIdx; --i) {
1248             SupplyItems(i, index, i != inputIdx);
1249             if (i != inputIdx) {
1250                 scrollLength += GetSize(gridCells_.at(i).at(0)) + *mainGap_;
1251             }
1252         }
1253     } else {
1254         return;
1255     }
1256     // Start animation
1257     if (!animator_->IsStopped()) {
1258         animator_->Stop();
1259     }
1260     animator_->ClearInterpolators();
1261     animateDelta_ = 0.0;
1262     auto animation = AceType::MakeRefPtr<CurveAnimation<double>>(0, scrollLength, Curves::LINEAR);
1263     animation->AddListener([weakScroll = AceType::WeakClaim(this)](double value) {
1264         auto scroll = weakScroll.Upgrade();
1265         if (scroll) {
1266             scroll->animatorJumpFlag_ = true;
1267             scroll->DoJump(value, SCROLL_FROM_JUMP);
1268         }
1269     });
1270     animator_->AddInterpolator(animation);
1271     animator_->ApplyOption(context->GetExplicitAnimationOption());
1272     animator_->ClearStopListeners();
1273     animator_->AddStopListener([weakScroll = AceType::WeakClaim(this)]() {
1274         auto scroll = weakScroll.Upgrade();
1275         if (scroll) {
1276             scroll->animateDelta_ = 0.0;
1277         }
1278     });
1279     animator_->Play();
1280 }
1281 
AnimateTo(const Dimension & position,float duration,const RefPtr<Curve> & curve)1282 bool RenderGridScroll::AnimateTo(const Dimension& position, float duration, const RefPtr<Curve>& curve)
1283 {
1284     if (!animator_->IsStopped()) {
1285         animator_->Stop();
1286     }
1287     animator_->ClearInterpolators();
1288     animateDelta_ = 0.0;
1289     auto animation = AceType::MakeRefPtr<CurveAnimation<double>>(GetCurrentOffset(), NormalizeToPx(position), curve);
1290     animation->AddListener([weakScroll = AceType::WeakClaim(this)](double value) {
1291         auto scroll = weakScroll.Upgrade();
1292         if (scroll) {
1293             scroll->DoJump(value, SCROLL_FROM_JUMP);
1294         }
1295     });
1296     animator_->AddInterpolator(animation);
1297     animator_->SetDuration(duration);
1298     animator_->ClearStopListeners();
1299     animator_->AddStopListener([weakScroll = AceType::WeakClaim(this)]() {
1300         auto scroll = weakScroll.Upgrade();
1301         if (scroll) {
1302             scroll->animateDelta_ = 0.0;
1303         }
1304     });
1305     animator_->Play();
1306     return true;
1307 }
1308 
CurrentOffset()1309 Offset RenderGridScroll::CurrentOffset()
1310 {
1311     auto ctx = GetContext().Upgrade();
1312     if (!ctx) {
1313         return useScrollable_ == SCROLLABLE::HORIZONTAL ? Offset(GetCurrentOffset(), 0.0)
1314                                                         : Offset(0.0, GetCurrentOffset());
1315     }
1316     auto mainOffset = ctx->ConvertPxToVp(Dimension(GetCurrentOffset(), DimensionUnit::PX));
1317     Offset currentOffset = useScrollable_ == SCROLLABLE::HORIZONTAL ? Offset(mainOffset, 0.0) : Offset(0.0, mainOffset);
1318     return currentOffset;
1319 }
1320 
GetItemPropsByIndex(int32_t itemIndex,int32_t & itemMain,int32_t & itemCross,int32_t & itemMainSpan,int32_t & itemCrossSpan)1321 bool RenderGridScroll::GetItemPropsByIndex(
1322     int32_t itemIndex, int32_t& itemMain, int32_t& itemCross, int32_t& itemMainSpan, int32_t& itemCrossSpan)
1323 {
1324     auto item = items_.find(itemIndex);
1325     if (item != items_.end()) {
1326         itemMain = GetItemMainIndex(item->second, true);
1327         itemCross = GetItemMainIndex(item->second, false);
1328         itemMainSpan = GetItemSpan(item->second, useScrollable_ != SCROLLABLE::HORIZONTAL);
1329         itemCrossSpan = GetItemSpan(item->second, useScrollable_ == SCROLLABLE::HORIZONTAL);
1330     } else {
1331         if (!getChildSpanByIndex_(itemIndex, useScrollable_ == SCROLLABLE::HORIZONTAL, itemMain, itemCross,
1332                 itemMainSpan, itemCrossSpan)) {
1333             LOGW("Not valid.");
1334             return false;
1335         }
1336     }
1337     return true;
1338 }
1339 
ScrollToEdge(OHOS::Ace::ScrollEdgeType edgeType,bool smooth)1340 void RenderGridScroll::ScrollToEdge(OHOS::Ace::ScrollEdgeType edgeType, bool smooth)
1341 {
1342     if (edgeType != ScrollEdgeType::SCROLL_TOP) {
1343         LOGW("Not supported yet");
1344         return;
1345     }
1346     if (items_.empty() && updateFlag_) {
1347         return;
1348     }
1349     if (scrollable_ && !scrollable_->IsStopped()) {
1350         scrollable_->StopScrollable();
1351     }
1352     currentItemIndex_ = 0;
1353     startRankItemIndex_ = GetStartingItem(currentItemIndex_);
1354     ClearItems();
1355     ClearLayout(false);
1356     MarkNeedLayout();
1357 }
1358 
DoJump(double position,int32_t source)1359 void RenderGridScroll::DoJump(double position, int32_t source)
1360 {
1361     double delta = position - animateDelta_;
1362     UpdateScrollPosition(delta, source);
1363     animateDelta_ = position;
1364 }
1365 
GetIndexByPosition(double position) const1366 int32_t RenderGridScroll::GetIndexByPosition(double position) const
1367 {
1368     int32_t index = 0;
1369     double startPosition = 0.0;
1370     double endPosition = 0.0;
1371 
1372     for (const auto& item : childrenInRect_) {
1373         if (!item || item->GetChildren().empty()) {
1374             continue;
1375         }
1376 
1377         auto gridItem = AceType::DynamicCast<RenderGridLayoutItem>(item);
1378         if (!gridItem) {
1379             break;
1380         }
1381 
1382         startPosition = item->GetPosition().GetY();
1383         endPosition = item->GetPosition().GetY() + item->GetLayoutSize().Height();
1384 
1385         if ((position > startPosition && position < endPosition) || NearEqual(position, startPosition) ||
1386             startPosition > position) {
1387             return gridItem->GetIndex();
1388         }
1389     }
1390     return index;
1391 }
1392 
OnScrolled(int32_t scrolled) const1393 void RenderGridScroll::OnScrolled(int32_t scrolled) const
1394 {
1395     if (scrolledEventFun_) {
1396         auto event = std::make_shared<GridEventInfo>(scrolled);
1397         if (event) {
1398             scrolledEventFun_(event);
1399         }
1400     }
1401 }
1402 
OnPaintFinish()1403 void RenderGridScroll::OnPaintFinish()
1404 {
1405     RenderNode::OnPaintFinish();
1406     if (showItem_.empty()) {
1407         return;
1408     }
1409     auto currentStartItemCount = *showItem_.begin();
1410     auto currentEndItemCount = *showItem_.rbegin();
1411     if ((startShowItemIndex_ != currentStartItemCount) || (endShowItemIndex_ != currentEndItemCount)) {
1412         startShowItemIndex_ = currentStartItemCount;
1413         endShowItemIndex_ = currentEndItemCount;
1414     }
1415 }
1416 
OnPredictLayout(int64_t deadline)1417 void RenderGridScroll::OnPredictLayout(int64_t deadline)
1418 {
1419     auto startTime = GetSysTimestamp();  // unit: ns
1420     auto context = context_.Upgrade();
1421     if (!context) {
1422         return;
1423     }
1424     if (!context->IsTransitionStop()) {
1425         LOGI("In page transition, skip predict.");
1426         return;
1427     }
1428     if (loadingIndex_ == -1) {
1429         DealCache(startIndex_, endIndex_);
1430         if (loadingIndex_ == -1) {
1431             if (startIndex_ == 0 && startRankItemIndex_ > 0) {
1432                 LoadForward();
1433                 MarkNeedPredictLayout();
1434             }
1435             return;
1436         }
1437     }
1438     ACE_SCOPED_TRACE("OnPredictLayout %d", loadingIndex_);
1439     if (gridMatrix_.find(loadingIndex_) != gridMatrix_.end() || Rank(loadingIndex_)) {
1440         auto iter = gridMatrix_.find(loadingIndex_);
1441         if (iter != gridMatrix_.end()) {
1442             for (const auto& item : iter->second) {
1443                 if (items_.find(item.second) == items_.end()) {
1444                     if (!buildChildByIndex_(item.second)) {
1445                         break;
1446                     }
1447                 }
1448                 // Stop predictLayout less than 3 milliseconds before the next vsync arrives.
1449                 if (GetSysTimestamp() - startTime + TIMETHRESHOLD > deadline * MICROSEC_TO_NANOSEC) {
1450                     MarkNeedPredictLayout();
1451                     return;
1452                 }
1453             }
1454             SupplyItems(loadingIndex_);
1455         }
1456         MarkNeedPredictLayout();
1457     } else {
1458         loadingIndex_ = -1;
1459     }
1460 }
1461 
IsAxisScrollable(AxisDirection direction)1462 bool RenderGridScroll::IsAxisScrollable(AxisDirection direction)
1463 {
1464     return (((direction == AxisDirection::UP || direction == AxisDirection::LEFT) && !reachHead_) ||
1465         ((direction == AxisDirection::DOWN || direction == AxisDirection::RIGHT) && !reachTail_));
1466 }
1467 
HandleAxisEvent(const AxisEvent & event)1468 void RenderGridScroll::HandleAxisEvent(const AxisEvent& event)
1469 {
1470     double degree = 0.0f;
1471     if (!NearZero(event.horizontalAxis)) {
1472         degree = event.horizontalAxis;
1473     } else if (!NearZero(event.verticalAxis)) {
1474         degree = event.verticalAxis;
1475     }
1476     double offset = SystemProperties::Vp2Px(DP_PER_LINE_DESKTOP * LINE_NUMBER_DESKTOP * degree / MOUSE_WHEEL_DEGREES);
1477     UpdateScrollPosition(-offset, SCROLL_FROM_ROTATE);
1478 }
1479 
CheckAxisNode()1480 WeakPtr<RenderNode> RenderGridScroll::CheckAxisNode()
1481 {
1482     return AceType::WeakClaim<RenderNode>(this);
1483 }
1484 
1485 } // namespace OHOS::Ace::V2