• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_v2/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, SCROLL_FROM_BAR);
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     LOGD("list newWindowHeight = %{public}f", newWindowHeight);
358     auto context = context_.Upgrade();
359     if (!context) {
360         return;
361     }
362     auto textFieldManager = AceType::DynamicCast<TextFieldManager>(context->GetTextFieldManager());
363     // only need to offset vertical lists
364     if (textFieldManager && (useScrollable_ == SCROLLABLE::VERTICAL)) {
365         // only when textField is onFocus
366         if (!textFieldManager->GetOnFocusTextField().Upgrade()) {
367             return;
368         }
369         auto position = textFieldManager->GetClickPosition().GetY();
370         double offset = newWindowHeight + GetGlobalOffset().GetY() - position;
371         if (LessOrEqual(offset, 0.0)) {
372             // negative offset to scroll down
373             textFieldOffset_ = offset;
374             LOGD("size change offset applied, %{public}f", offset);
375         }
376     }
377 }
378 
GetGridSize()379 bool RenderGridScroll::GetGridSize()
380 {
381     double rowSize = ((gridHeight_ > 0.0) && (gridHeight_ < GetLayoutParam().GetMaxSize().Height()))
382                          ? gridHeight_
383                          : GetLayoutParam().GetMaxSize().Height();
384     double colSize = ((gridWidth_ > 0.0) && (gridWidth_ < GetLayoutParam().GetMaxSize().Width()))
385                          ? gridWidth_
386                          : GetLayoutParam().GetMaxSize().Width();
387     if (NearEqual(rowSize_, Size::INFINITE_SIZE)) {
388         if ((rowsArgs_.find(UNIT_PERCENT) != std::string::npos || rowsArgs_.find(UNIT_RATIO) != std::string::npos)) {
389             rowSize = viewPort_.Height();
390         }
391     } else if (rowsArgs_.empty()) {
392         useScrollable_ = SCROLLABLE::VERTICAL;
393     }
394     if (NearEqual(colSize_, Size::INFINITE_SIZE)) {
395         if ((colsArgs_.find(UNIT_PERCENT) != std::string::npos || colsArgs_.find(UNIT_RATIO) != std::string::npos)) {
396             colSize = viewPort_.Width();
397         }
398     } else if (colsArgs_.empty()) {
399         useScrollable_ = SCROLLABLE::HORIZONTAL;
400         mainSize_ = &colSize_;
401         crossSize_ = &rowSize_;
402         mainCount_ = &colCount_;
403         crossCount_ = &rowCount_;
404         crossGap_ = &rowGap_;
405         mainGap_ = &colGap_;
406     }
407     LOGD("GetGridSize %lf, %lf   [%lf- %lf]", rowSize, colSize, rowSize_, colSize_);
408     if (rowSize != rowSize_ || colSize != colSize_) {
409         rowSize_ = rowSize;
410         colSize_ = colSize;
411         CreateScrollable();
412         SizeChangeOffset(rowSize);
413         return true;
414     }
415     return false;
416 }
417 
BuildGrid(std::vector<double> & main,std::vector<double> & cross)418 void RenderGridScroll::BuildGrid(std::vector<double>& main, std::vector<double>& cross)
419 {
420     if (useScrollable_ == SCROLLABLE::NO_SCROLL) {
421         main = ParseArgs(GetRowTemplate(), rowSize_, rowGap_);
422         cross = ParseArgs(GetColumnsTemplate(), colSize_, colGap_);
423     } else if (useScrollable_ == SCROLLABLE::VERTICAL) {
424         cross = ParseArgs(GetColumnsTemplate(), colSize_, colGap_);
425         int32_t col = 0;
426         for (auto width : cross) {
427             metaData_[col] = Size(width, Size::INFINITE_SIZE);
428             ++col;
429         }
430     } else if (useScrollable_ == SCROLLABLE::HORIZONTAL) {
431         cross = ParseArgs(GetRowTemplate(), rowSize_, rowGap_);
432         int32_t row = 0;
433         for (auto height : cross) {
434             metaData_[row] = Size(Size::INFINITE_SIZE, height);
435             ++row;
436         }
437     }
438 }
439 
InitialGridProp()440 void RenderGridScroll::InitialGridProp()
441 {
442     // Not first time layout after update, no need to initial.
443     if (!GetGridSize() && !updateFlag_) {
444         return;
445     }
446     ACE_SCOPED_TRACE("InitialGridProp");
447     OnDataSourceUpdated(0);
448     rowGap_ = NormalizePercentToPx(userRowGap_, true);
449     colGap_ = NormalizePercentToPx(userColGap_, false);
450     std::vector<double> main;
451     std::vector<double> cross;
452     BuildGrid(main, cross);
453 
454     // Initialize the columnCount and rowCount, default is 1
455     *crossCount_ = cross.size();
456     *mainCount_ = main.size();
457     gridCells_.clear();
458     items_.clear();
459     UpdateAccessibilityAttr();
460     if (buildChildByIndex_) {
461         int32_t endIndex = -1;
462         while (endIndex < currentItemIndex_) {
463             if (!Rank(*mainCount_, *mainCount_ == 0 ? startRankItemIndex_ : -1)) {
464                 // When [firstLineToBottom_] does not equal to [std::nullopt], it indicates that this [InitialGridProp]
465                 // is called after [ScrollToIndex].
466                 // This is the case when it scrolls to the last line and the last line is not full.
467                 // So we need to add a line to [*mainCount_].
468                 (*mainCount_) += firstLineToBottom_ ? 1 : 0;
469                 break;
470             }
471             (*mainCount_)++;
472             auto mainIter = gridMatrix_.find(*mainCount_ - 1);
473             if (mainIter == gridMatrix_.end()) {
474                 break;
475             }
476             for (int32_t crossIndex = *crossCount_ - 1; crossIndex >= 0; crossIndex--) {
477                 auto iter = mainIter->second.find(crossIndex);
478                 if (iter != mainIter->second.end()) {
479                     endIndex = iter->second;
480                     break;
481                 }
482             }
483         }
484         currentItemIndex_ = 0;
485 
486         SupplyItems(*mainCount_ > 0 ? *mainCount_ - 1 : 0);
487         startIndex_ = *mainCount_ > 0 ? *mainCount_ - 1 : 0;
488 
489         if (NearZero(currentOffset_)) {
490             needCalculateViewPort_ = true;
491         }
492     }
493     updateFlag_ = false;
494     if (firstLineToBottom_ && firstLineToBottom_.value()) {
495         // calculate the distance from the first line to the last line
496         currentOffset_ = *mainSize_ - GetSize(gridCells_.at(0).at(0));
497         needCalculateViewPort_ = false;
498     }
499     firstLineToBottom_ = std::nullopt;
500 }
501 
BuildLazyGridLayout(int32_t index,double sizeNeed)502 double RenderGridScroll::BuildLazyGridLayout(int32_t index, double sizeNeed)
503 {
504     if (!buildChildByIndex_ || index < 0 || NearZero(sizeNeed)) {
505         return 0.0;
506     }
507     LOGD("BuildLazyGridLayout index = %d sizeNeed = %lf", index, sizeNeed);
508     double size = 0.0;
509     int32_t startIndex = index;
510     while (size < sizeNeed) {
511         auto suppleSize = SupplyItems(startIndex);
512         if (NearZero(suppleSize)) {
513             break;
514         }
515         *mainCount_ = ++startIndex;
516         size += suppleSize + *mainGap_;
517     }
518     return size;
519 }
520 
CheckGridPlaced(int32_t index,int32_t main,int32_t cross,int32_t & mainSpan,int32_t & crossSpan)521 bool RenderGridScroll::CheckGridPlaced(
522     int32_t index, int32_t main, int32_t cross, int32_t& mainSpan, int32_t& crossSpan)
523 {
524     LOGD("CheckGridPlaced %{public}d %{public}d %{public}d %{public}d %{public}d", index, main, cross, mainSpan,
525         crossSpan);
526     auto mainIter = gridMatrix_.find(main);
527     if (mainIter != gridMatrix_.end()) {
528         auto crossIter = mainIter->second.find(cross);
529         if (crossIter != mainIter->second.end()) {
530             return false;
531         }
532     }
533     if (cross + crossSpan > *crossCount_) {
534         return false;
535     }
536 
537     for (int32_t i = 0; i < mainSpan; i++) {
538         mainIter = gridMatrix_.find(i + main);
539         if (mainIter != gridMatrix_.end()) {
540             for (int32_t j = 0; j < crossSpan; j++) {
541                 if (mainIter->second.find(j + cross) != mainIter->second.end()) {
542                     return false;
543                 }
544             }
545         }
546     }
547 
548     for (int32_t i = main; i < main + mainSpan; ++i) {
549         std::map<int32_t, int32_t> mainMap;
550         auto iter = gridMatrix_.find(i);
551         if (iter != gridMatrix_.end()) {
552             mainMap = iter->second;
553         }
554         for (int32_t j = cross; j < cross + crossSpan; ++j) {
555             mainMap.emplace(std::make_pair(j, index));
556         }
557         gridMatrix_[i] = mainMap;
558     }
559     LOGD("CheckGridPlaced done %{public}d %{public}d %{public}d %{public}d %{public}d", index, main, cross, mainSpan,
560         crossSpan);
561     return true;
562 }
563 
LayoutChild(const RefPtr<RenderNode> & child,int32_t main,int32_t cross,int32_t mainSpan,int32_t crossSpan,bool needPosition)564 void RenderGridScroll::LayoutChild(const RefPtr<RenderNode>& child, int32_t main, int32_t cross, int32_t mainSpan,
565     int32_t crossSpan, bool needPosition)
566 {
567     auto gridLayoutItem = AceType::DynamicCast<RenderGridLayoutItem>(child);
568     if (!gridLayoutItem) {
569         LOGE("child of GridScroll is not GridLayoutItem!");
570         return;
571     }
572     Dimension itemMainSize;
573     if (useScrollable_ == SCROLLABLE::HORIZONTAL) {
574         itemMainSize = gridLayoutItem->GetGridItemWidth();
575     } else {
576         itemMainSize = gridLayoutItem->GetGridItemHeight();
577     }
578     bool itemMainIsPercent = itemMainSize.Unit() == DimensionUnit::PERCENT;
579     child->Layout(MakeInnerLayoutParam(main, cross, mainSpan, crossSpan, itemMainIsPercent));
580     SetMainSize(gridCells_.at(main).at(cross), child->GetLayoutSize());
581     if (GetSize(gridCells_.at(main).at(0)) < GetSize(gridCells_.at(main).at(cross))) {
582         SetMainSize(gridCells_.at(main).at(0), gridCells_.at(main).at(cross));
583     }
584     if (!needPosition) {
585         return;
586     }
587     if (useScrollable_ != SCROLLABLE::HORIZONTAL) {
588         child->SetPosition(Offset(0, *mainSize_ + *mainGap_));
589     } else {
590         child->SetPosition(Offset(*mainSize_ + *mainGap_, 0));
591     }
592 }
593 
GetNextGrid(int32_t & curMain,int32_t & curCross) const594 void RenderGridScroll::GetNextGrid(int32_t& curMain, int32_t& curCross) const
595 {
596     ++curCross;
597     if (curCross >= *crossCount_) {
598         curCross = 0;
599         ++curMain;
600     }
601 }
602 
GetPreviousGrid(int32_t & curMain,int32_t & curCross)603 void RenderGridScroll::GetPreviousGrid(int32_t& curMain, int32_t& curCross)
604 {
605     --curCross;
606     if (curCross < 0) {
607         curCross = *crossCount_;
608         --curMain;
609     }
610 }
611 
MakeInnerLayoutParam(int32_t main,int32_t cross,int32_t mainSpan,int32_t crossSpan,bool itemIsPercentUnit) const612 LayoutParam RenderGridScroll::MakeInnerLayoutParam(
613     int32_t main, int32_t cross, int32_t mainSpan, int32_t crossSpan, bool itemIsPercentUnit) const
614 {
615     LayoutParam innerLayout;
616     double mainLen = 0.0;
617     double crossLen = 0.0;
618     if (itemIsPercentUnit) {
619         auto maxMainSize = GetSize(GetLayoutParam().GetMaxSize());
620         mainLen += Size::IsValueInfinite(maxMainSize) ? GetSize(viewPort_) : maxMainSize;
621     } else {
622         for (int32_t i = 0; i < mainSpan; ++i) {
623             if (gridCells_.find(main + i) != gridCells_.end() &&
624                 gridCells_.at(main + i).find(cross) != gridCells_.at(main + i).end()) {
625                 mainLen += GetSize(gridCells_.at(main + i).at(cross));
626             }
627         }
628         mainLen += (mainSpan - 1) * (*mainGap_);
629     }
630     for (int32_t i = 0; i < crossSpan; ++i) {
631         if (gridCells_.find(main) != gridCells_.end() &&
632             gridCells_.at(main).find(cross + i) != gridCells_.at(main).end()) {
633             crossLen += GetSize(gridCells_.at(main).at(cross + i), false);
634         }
635     }
636     crossLen += (crossSpan - 1) * (*crossGap_);
637 
638     Size size;
639     if (useScrollable_ == SCROLLABLE::HORIZONTAL) {
640         size = Size(mainLen, crossLen);
641     } else {
642         size = Size(crossLen, mainLen);
643     }
644     if (crossAxisAlign_ == FlexAlign::STRETCH) {
645         innerLayout.SetMinSize(size);
646         innerLayout.SetMaxSize(size);
647     } else {
648         innerLayout.SetMaxSize(size);
649     }
650     return innerLayout;
651 }
652 
LoadForward()653 void RenderGridScroll::LoadForward()
654 {
655     auto firstItem = GetStartingItem(startRankItemIndex_ - 1);
656 
657     decltype(gridCells_) gridCells(std::move(gridCells_));
658     decltype(gridMatrix_) gridMatrix(std::move(gridMatrix_));
659 
660     int32_t count = 0;
661     int32_t endIndex = -1;
662     while (endIndex < startRankItemIndex_ - 1) {
663         if (!Rank(count, count == 0 ? firstItem : -1)) {
664             break;
665         }
666         count++;
667         auto mainIter = gridMatrix_.find(count - 1);
668         if (mainIter == gridMatrix_.end()) {
669             break;
670         }
671         for (int32_t cross = *crossCount_ - 1; cross >= 0; cross--) {
672             auto iter = mainIter->second.find(cross);
673             if (iter != mainIter->second.end()) {
674                 endIndex = iter->second;
675                 break;
676             }
677         }
678     }
679     startRankItemIndex_ = firstItem;
680     if (count == 0) {
681         return;
682     }
683     for (const auto& item : gridMatrix) {
684         gridMatrix_[item.first + count] = item.second;
685     }
686     for (const auto& item : gridCells) {
687         gridCells_[item.first + count] = item.second;
688     }
689 
690     decltype(inCache_) inCache(std::move(inCache_));
691     for (const auto& item : inCache) {
692         inCache_.insert(item + count);
693     }
694 
695     *mainCount_ += count;
696     startIndex_ += count;
697 }
698 
CalculateViewPort()699 void RenderGridScroll::CalculateViewPort()
700 {
701     if (textFieldOffset_) {
702         currentOffset_ += textFieldOffset_.value();
703         textFieldOffset_.reset();
704     }
705     while (!NearZero(currentOffset_) || needCalculateViewPort_) {
706         if (currentOffset_ > 0) {
707             // [currentOffset_] > 0  means grid items are going to move down
708             // move to top/left of first row/column
709             if (!NearZero(firstItemOffset_)) {
710                 if (gridCells_.find(startIndex_ + 1) != gridCells_.end()) {
711                     currentOffset_ += GetSize(gridCells_.at(startIndex_++).at(0)) + *mainGap_ - firstItemOffset_;
712                 }
713                 firstItemOffset_ = 0.0;
714             }
715             while (currentOffset_ > 0) {
716                 if (startIndex_ > 0) {
717                     if (gridCells_.find(startIndex_ - 1) == gridCells_.end()) {
718                         SupplyItems(startIndex_ - 1);
719                     }
720                     currentOffset_ -= GetSize(gridCells_.at(startIndex_-- - 1).at(0)) + *mainGap_;
721                 }
722                 if (startIndex_ == 0 && startRankItemIndex_ > 0 && currentOffset_ > 0) {
723                     LoadForward();
724                 }
725                 if (startIndex_ == 0) {
726                     break;
727                 }
728             }
729             if (currentOffset_ < 0) {
730                 firstItemOffset_ -= currentOffset_;
731             } else {
732                 if (startIndex_ == 0) {
733                     reachHead_ = true;
734                 }
735             }
736             currentOffset_ = 0.0;
737 
738             auto blank = CalculateBlankOfEnd();
739             if (GreatOrEqual(0.0, blank)) {
740                 return;
741             }
742             // request new item until the blank is filled up
743             blank -= BuildLazyGridLayout(*mainCount_, blank);
744             if (LessOrEqual(blank, 0)) {
745                 return;
746             }
747         } else {
748             // [currentOffset_] <= 0  means grid items are going to move up.
749             if (!NearZero(firstItemOffset_)) {
750                 currentOffset_ -= firstItemOffset_;
751                 firstItemOffset_ = 0.0;
752             }
753             // step1: move [currentOffset_] to the last one of [gridCells_]
754             while (startIndex_ < *mainCount_ && (currentOffset_ < 0 || needCalculateViewPort_)) {
755                 currentOffset_ += GetSize(gridCells_.at(startIndex_++).at(0)) + *mainGap_;
756             }
757             needCalculateViewPort_ = false;
758             // step2: if [currentOffset_] is positive, it means that we've had enough grid items
759             if (currentOffset_ > 0) {
760                 firstItemOffset_ = GetSize(gridCells_.at(--startIndex_).at(0)) + *mainGap_ - currentOffset_;
761                 currentOffset_ = 0.0;
762             } else {
763                 // step3: if [currentOffset_] is non-positive, it means we need to build more grid items
764                 if (!GreatOrEqual(0.0, BuildLazyGridLayout(*mainCount_, -currentOffset_))) {
765                     continue;
766                 }
767             }
768             currentOffset_ = 0.0;
769             auto blank = CalculateBlankOfEnd();
770             if (GreatOrEqual(0.0, blank)) {
771                 return;
772             }
773             // request new item until the blank is filled up
774             blank -= BuildLazyGridLayout(*mainCount_, blank);
775             if (LessOrEqual(blank, 0)) {
776                 return;
777             }
778             blank = blank - firstItemOffset_;
779             firstItemOffset_ = 0;
780             // Move up
781             while (blank > 0) {
782                 if (startIndex_ == 0 && startRankItemIndex_ > 0) {
783                     LoadForward();
784                 }
785                 if (startIndex_ == 0) {
786                     break;
787                 }
788                 if (gridCells_.find(startIndex_ - 1) == gridCells_.end()) {
789                     SupplyItems(startIndex_ - 1);
790                 }
791                 blank -= GetSize(gridCells_.at(--startIndex_).at(0)) + *mainGap_;
792             }
793             firstItemOffset_ -= blank;
794             if (firstItemOffset_ < 0) {
795                 firstItemOffset_ = 0;
796             }
797             reachTail_ = true;
798         }
799     }
800 }
801 
CalculateBlankOfEnd()802 double RenderGridScroll::CalculateBlankOfEnd()
803 {
804     double drawLength = 0.0 - firstItemOffset_;
805     for (int32_t main = startIndex_; main < *mainCount_; main++) {
806         drawLength += GetSize(gridCells_.at(main).at(0)) + *mainGap_;
807         if (GreatOrEqual(drawLength, *mainSize_)) {
808             break;
809         }
810     }
811     return *mainSize_ - drawLength;
812 }
813 
SupplyItems(int32_t mainIndex,int32_t itemIndex,bool needPosition)814 double RenderGridScroll::SupplyItems(int32_t mainIndex, int32_t itemIndex, bool needPosition)
815 {
816     ACE_SCOPED_TRACE("SupplyItems %d", mainIndex);
817     if (loadingIndex_ == mainIndex) {
818         loadingIndex_ = -1;
819     }
820     if (gridMatrix_.find(mainIndex) == gridMatrix_.end()) {
821         Rank(mainIndex, itemIndex);
822     }
823     gridCells_.try_emplace(mainIndex, metaData_);
824     auto iter = gridMatrix_.find(mainIndex);
825     if (iter != gridMatrix_.end()) {
826         int32_t frontIndex = -1;
827         for (const auto& item : iter->second) {
828             if (item.second != frontIndex) {
829                 if (items_.find(item.second) != items_.end() || buildChildByIndex_(item.second)) {
830                     int32_t itemRowSpan = GetItemSpan(items_[item.second], true);
831                     int32_t itemColSpan = GetItemSpan(items_[item.second], false);
832                     LayoutChild(items_[item.second], mainIndex, item.first, itemRowSpan, itemColSpan, needPosition);
833                 }
834             }
835             frontIndex = item.second;
836         }
837         inCache_.insert(mainIndex);
838         return NearEqual(GetSize(gridCells_[mainIndex][0]), Size::INFINITE_SIZE) ? 0.0
839                                                                                  : GetSize(gridCells_[mainIndex][0]);
840     }
841     return 0.0;
842 }
843 
Rank(int32_t mainIndex,int32_t itemIndex)844 bool RenderGridScroll::Rank(int32_t mainIndex, int32_t itemIndex)
845 {
846     if (gridMatrix_.find(mainIndex) != gridMatrix_.end()) {
847         return true;
848     }
849     ACE_SCOPED_TRACE("Rank [%d]", mainIndex);
850     if (itemIndex == -1) {
851         auto mainIter = gridMatrix_.find(mainIndex - 1);
852         if (mainIter != gridMatrix_.end()) {
853             for (int32_t cross = *crossCount_ - 1; cross >= 0; cross--) {
854                 auto iter = mainIter->second.find(cross);
855                 if (iter != mainIter->second.end()) {
856                     itemIndex = iter->second + 1;
857                     break;
858                 }
859             }
860         }
861     }
862     if (itemIndex == -1) {
863         LOGE("failed, itemIndex = -1, mainIndex = %d", mainIndex);
864         return false;
865     }
866 
867     bool isFilled = false;
868     int32_t index = mainIndex;
869     int32_t crossIndex = 0;
870     while (!isFilled) {
871         int32_t itemMain = -1;
872         int32_t itemCross = -1;
873         int32_t itemMainSpan = -1;
874         int32_t itemCrossSpan = -1;
875         auto item = items_.find(itemIndex);
876         if (item != items_.end()) {
877             itemMain = GetItemMainIndex(item->second, true);
878             itemCross = GetItemMainIndex(item->second, false);
879             itemMainSpan = GetItemSpan(item->second, useScrollable_ != SCROLLABLE::HORIZONTAL);
880             itemCrossSpan = GetItemSpan(item->second, useScrollable_ == SCROLLABLE::HORIZONTAL);
881         } else {
882             if (!getChildSpanByIndex_(itemIndex, useScrollable_ == SCROLLABLE::HORIZONTAL, itemMain, itemCross,
883                     itemMainSpan, itemCrossSpan)) {
884                 return false;
885             }
886         }
887 
888         if (itemCrossSpan > *crossCount_) {
889             itemIndex++;
890             continue;
891         }
892         if (itemMain >= 0 && itemCross >= 0 && itemCross < *crossCount_ &&
893             CheckGridPlaced(itemIndex, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
894         } else {
895             while (!CheckGridPlaced(itemIndex, mainIndex, crossIndex, itemMainSpan, itemCrossSpan)) {
896                 GetNextGrid(mainIndex, crossIndex);
897                 if (mainIndex > index) {
898                     isFilled = true;
899                     break;
900                 }
901             }
902         }
903         itemIndex++;
904     }
905     return true;
906 }
907 
PerformLayout()908 void RenderGridScroll::PerformLayout()
909 {
910     if (rowsArgs_.empty() && colsArgs_.empty()) {
911         return;
912     }
913     if (RenderGridLayout::GetChildren().empty() && !buildChildByIndex_) {
914         return;
915     }
916     InitialGridProp();
917     CalculateViewPort();
918     showItem_.clear();
919     childrenInRect_.clear();
920     double drawLength = 0.0 - firstItemOffset_;
921     int32_t main = startIndex_ > 0 ? startIndex_ - 1 : startIndex_;
922     LOGD("startIndex_=[%d], firstItemOffset_=[%lf]", startIndex_, firstItemOffset_);
923     for (; main < *mainCount_; main++) {
924         if (gridCells_.find(main) == gridCells_.end()) {
925             continue;
926         }
927         for (int32_t cross = 0; cross < *crossCount_; cross++) {
928             auto mainIter = gridMatrix_.find(main);
929             if (mainIter == gridMatrix_.end()) {
930                 continue;
931             }
932             auto crossIter = mainIter->second.find(cross);
933             if (crossIter == mainIter->second.end()) {
934                 continue;
935             }
936             if (buildChildByIndex_ && inCache_.count(main) == 0) {
937                 SupplyItems(main);
938             }
939             if (showItem_.count(crossIter->second) == 0) {
940                 showItem_.insert(crossIter->second);
941                 auto item = items_.find(crossIter->second);
942                 if (item != items_.end()) {
943                     childrenInRect_.push_back(item->second);
944                     int32_t itemMainSpan = GetItemSpan(item->second, useScrollable_ != SCROLLABLE::HORIZONTAL);
945                     int32_t itemCrossSpan = GetItemSpan(item->second, useScrollable_ == SCROLLABLE::HORIZONTAL);
946                     SetChildPosition(item->second, main, cross, itemMainSpan, itemCrossSpan);
947                 }
948             }
949         }
950         if (main >= startIndex_) {
951             drawLength += GetSize(gridCells_.at(main).at(0)) + *mainGap_;
952         }
953         if (GreatOrEqual(drawLength, *mainSize_)) {
954             break;
955         }
956     }
957     SetLayoutSize(GetLayoutParam().Constrain(Size(colSize_, rowSize_)));
958     endIndex_ = main;
959     MarkNeedPredictLayout();
960     CalculateWholeSize(drawLength);
961 
962     if (rightToLeft_ && useScrollable_ == SCROLLABLE::HORIZONTAL) {
963         lastOffset_ = estimateHeight_ - viewPort_.Width() - (startMainPos_ + firstItemOffset_ + currentOffset_);
964     } else {
965         lastOffset_ = estimatePos_ + startMainPos_ + firstItemOffset_ + currentOffset_;
966     }
967 
968     int32_t firstIndex = GetIndexByPosition(0);
969     if (lastFirstIndex_ != firstIndex) {
970         if (!animatorJumpFlag_) {
971             OnScrolled(firstIndex);
972         }
973         lastFirstIndex_ = firstIndex;
974     }
975     animatorJumpFlag_ = false;
976 }
977 
DealCache(int32_t start,int32_t end)978 void RenderGridScroll::DealCache(int32_t start, int32_t end)
979 {
980     if (loadingIndex_ != -1) {
981         return;
982     }
983 
984     if (!inRankCache_.empty()) {
985         std::set<int32_t> rankCache(std::move(inRankCache_));
986         if (deleteChildByIndex_) {
987             for (auto index : rankCache) {
988                 if (items_.find(index) == items_.end()) {
989                     deleteChildByIndex_(index);
990                 }
991             }
992         }
993     }
994 
995     std::set<int32_t> deleteItem;
996     for (const auto& item : inCache_) {
997         if (item < start - cacheCount_ || item > end + cacheCount_) {
998             deleteItem.insert(item);
999         }
1000     }
1001 
1002     for (const auto& item : deleteItem) {
1003         DeleteItems(item, false);
1004     }
1005 
1006     for (int32_t i = 1; i <= cacheCount_; i++) {
1007         if (inCache_.count(i + end) == 0) {
1008             loadingIndex_ = i + end;
1009             break;
1010         }
1011 
1012         if (start >= i && inCache_.count(start - i) == 0) {
1013             loadingIndex_ = start - i;
1014             break;
1015         }
1016     }
1017 }
1018 
DeleteItems(int32_t index,bool isTail)1019 void RenderGridScroll::DeleteItems(int32_t index, bool isTail)
1020 {
1021     if (!deleteChildByIndex_) {
1022         return;
1023     }
1024 
1025     auto iter = gridMatrix_.find(index);
1026     if (iter == gridMatrix_.end()) {
1027         return;
1028     }
1029     for (const auto& item : iter->second) {
1030         deleteChildByIndex_(item.second);
1031         RemoveChildByIndex(item.second);
1032     }
1033 
1034     inCache_.erase(index);
1035 }
1036 
ClearLayout(bool needReservedPlace)1037 void RenderGridScroll::ClearLayout(bool needReservedPlace)
1038 {
1039     if (needReservedPlace) {
1040         RecordLocation();
1041     } else {
1042         currentOffset_ = 0.0;
1043     }
1044     showItem_.clear();
1045     childrenInRect_.clear();
1046     inCache_.clear();
1047 
1048     updateFlag_ = true;
1049     reachHead_ = false;
1050     reachTail_ = false;
1051     startMainPos_ = 0.0;
1052     firstItemOffset_ = 0.0;
1053     startIndex_ = 0;
1054     endIndex_ = -1;
1055     lastOffset_ = 0.0;
1056     estimatePos_ = 0.0;
1057     estimateAverageHeight_ = 0.0;
1058     estimateHeight_ = 0.0;
1059 
1060     colCount_ = 0;
1061     rowCount_ = 0;
1062 
1063     gridMatrix_.clear();
1064     gridCells_.clear();
1065 }
1066 
RecordLocation()1067 void RenderGridScroll::RecordLocation()
1068 {
1069     double positionMain = 0.0;
1070 
1071     for (int32_t i = 0; i < startIndex_; ++i) {
1072         if (i < static_cast<int32_t>(gridCells_.size())) {
1073             positionMain += GetSize(gridCells_.at(i).at(0));
1074         }
1075     }
1076     positionMain += (startIndex_) * (*mainGap_);
1077     currentOffset_ += -positionMain - firstItemOffset_;
1078 }
1079 
ClearItems()1080 void RenderGridScroll::ClearItems()
1081 {
1082     decltype(items_) items(std::move(items_));
1083     for (const auto& item : items) {
1084         if (item.first < startRankItemIndex_) {
1085             deleteChildByIndex_(item.first);
1086         } else {
1087             inRankCache_.emplace(item.first);
1088         }
1089         RemoveChildByIndex(item.first);
1090     }
1091     loadingIndex_ = -1;
1092 }
1093 
GetItemMainIndex(int32_t index)1094 int32_t RenderGridScroll::GetItemMainIndex(int32_t index)
1095 {
1096     for (const auto& main : gridMatrix_) {
1097         for (const auto& cross : main.second) {
1098             if (cross.second == index) {
1099                 return main.first;
1100             }
1101         }
1102     }
1103     return -1;
1104 }
1105 
GetStartingItem(int32_t first)1106 int32_t RenderGridScroll::GetStartingItem(int32_t first)
1107 {
1108     int32_t firstIndex = 0;
1109     int32_t index = first;
1110     int32_t itemMain = -1;
1111     int32_t itemCross = -1;
1112     int32_t itemMainSpan = -1;
1113     int32_t itemCrossSpan = -1;
1114     while (index > 0) {
1115         if (getChildSpanByIndex_(
1116                 index, useScrollable_ == SCROLLABLE::HORIZONTAL, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
1117             LOGD("index %d %d,  %d,  %d,  %d", index, itemMain, itemCross, itemMainSpan, itemCrossSpan);
1118             if (itemCross == 0) {
1119                 firstIndex = index;
1120                 break;
1121             }
1122         }
1123 
1124         index--;
1125     }
1126     return firstIndex;
1127 }
1128 
OnDataSourceUpdated(int32_t index)1129 void RenderGridScroll::OnDataSourceUpdated(int32_t index)
1130 {
1131     if (items_.empty() && updateFlag_) {
1132         return;
1133     }
1134     ACE_SCOPED_TRACE("OnDataSourceUpdated %d", index);
1135     auto items = gridMatrix_.find(startIndex_);
1136     if (items != gridMatrix_.end() && !items->second.empty()) {
1137         currentItemIndex_ = items->second.begin()->second;
1138     }
1139     startRankItemIndex_ = GetStartingItem(currentItemIndex_);
1140     auto offset = firstItemOffset_;
1141     ClearItems();
1142     ClearLayout(false);
1143 
1144     currentOffset_ = -offset;
1145     MarkNeedLayout();
1146 }
1147 
CalculateWholeSize(double drawLength)1148 void RenderGridScroll::CalculateWholeSize(double drawLength)
1149 {
1150     if (gridMatrix_.empty() || gridCells_.empty()) {
1151         return;
1152     }
1153     if (totalCountFlag_) {
1154         int currentItemCount = 0;
1155         auto lastRow = gridMatrix_.rbegin()->second;
1156         if (!lastRow.empty()) {
1157             currentItemCount = lastRow.rbegin()->second;
1158         }
1159         if (currentItemCount != 0) {
1160             totalCountFlag_ = false;
1161         }
1162     }
1163     double scrollBarExtent = 0.0;
1164     double itemCount = 0;
1165     startMainPos_ = 0.0;
1166     for (int index = 0; index < *mainCount_; index++) {
1167         if (index == startIndex_) {
1168             // get the start position in grid
1169             startMainPos_ = scrollBarExtent;
1170         }
1171         if (gridCells_.find(index) == gridCells_.end()) {
1172             continue;
1173         }
1174         itemCount += gridCells_.at(index).size();
1175         scrollBarExtent += GetSize(gridCells_.at(index).at(0)) + *mainGap_;
1176     }
1177     if (itemCount > 0 && !gridCells_.empty()) {
1178         estimateAverageHeight_ = scrollBarExtent / itemCount;
1179         estimateHeight_ = estimateAverageHeight_ * GetItemTotalCount();
1180         auto result = gridMatrix_.find(gridCells_.begin()->first);
1181         if (result != gridMatrix_.end()) {
1182             int32_t startItem = result->second.begin()->second;
1183             estimatePos_ = estimateAverageHeight_ * startItem;
1184         }
1185     }
1186 
1187     bool isScrollable = false;
1188     if (estimateHeight_ > GetSize(GetLayoutSize()) || scrollBarExtent > GetSize(GetLayoutSize())) {
1189         isScrollable = true;
1190     }
1191     if (scrollBar_) {
1192         scrollBar_->SetScrollable(isScrollable);
1193     }
1194     if (!isScrollable) {
1195         currentOffset_ = 0.0;
1196     }
1197 }
1198 
ScrollPage(bool reverse,bool smooth)1199 void RenderGridScroll::ScrollPage(bool reverse, bool smooth)
1200 {
1201     if (!reverse) {
1202         UpdateScrollPosition(-GetSize(GetLayoutSize()), SCROLL_FROM_JUMP);
1203     } else {
1204         UpdateScrollPosition(GetSize(GetLayoutSize()), SCROLL_FROM_JUMP);
1205     }
1206 }
1207 
GetEstimatedHeight()1208 double RenderGridScroll::GetEstimatedHeight()
1209 {
1210     return estimateHeight_;
1211 }
1212 
InitScrollBar()1213 void RenderGridScroll::InitScrollBar()
1214 {
1215     if (!component_) {
1216         LOGE("InitScrollBar failed, component_ is null.");
1217         return;
1218     }
1219     if (scrollBar_) {
1220         scrollBar_->SetDisplayMode(component_->GetScrollBar());
1221         if (!component_->GetScrollBarWidth().empty()) {
1222             const auto& width = StringUtils::StringToDimension(component_->GetScrollBarWidth());
1223             scrollBar_->SetInactiveWidth(width);
1224             scrollBar_->SetNormalWidth(width);
1225             scrollBar_->SetActiveWidth(width);
1226             scrollBar_->SetTouchWidth(width);
1227         }
1228         if (!component_->GetScrollBarColor().empty()) {
1229             scrollBarColor_ = Color::FromString(component_->GetScrollBarColor());
1230             scrollBar_->SetForegroundColor(scrollBarColor_);
1231         }
1232         scrollBar_->Reset();
1233         return;
1234     }
1235 
1236     const RefPtr<ScrollBarTheme> theme = GetTheme<ScrollBarTheme>();
1237     if (!theme) {
1238         return;
1239     }
1240     RefPtr<GridScrollController> controller = AceType::MakeRefPtr<GridScrollController>();
1241     scrollBar_ = AceType::MakeRefPtr<ScrollBar>(component_->GetScrollBar(), theme->GetShapeMode());
1242     scrollBar_->SetScrollBarController(controller);
1243 
1244     // set the scroll bar style
1245     scrollBar_->SetReservedHeight(theme->GetReservedHeight());
1246     scrollBar_->SetMinHeight(theme->GetMinHeight());
1247     scrollBar_->SetMinDynamicHeight(theme->GetMinDynamicHeight());
1248     auto& scrollBarColor = component_->GetScrollBarColor();
1249     if (!scrollBarColor.empty()) {
1250         scrollBarColor_ = Color::FromString(scrollBarColor);
1251     } else {
1252         scrollBarColor_ = theme->GetForegroundColor();
1253     }
1254     scrollBar_->SetForegroundColor(scrollBarColor_);
1255     scrollBar_->SetBackgroundColor(theme->GetBackgroundColor());
1256     scrollBar_->SetPadding(theme->GetPadding());
1257     scrollBar_->SetScrollable(true);
1258     if (!component_->GetScrollBarWidth().empty()) {
1259         const auto& width = StringUtils::StringToDimension(component_->GetScrollBarWidth());
1260         scrollBar_->SetInactiveWidth(width);
1261         scrollBar_->SetNormalWidth(width);
1262         scrollBar_->SetActiveWidth(width);
1263         scrollBar_->SetTouchWidth(width);
1264     } else {
1265         scrollBar_->SetInactiveWidth(theme->GetNormalWidth());
1266         scrollBar_->SetNormalWidth(theme->GetNormalWidth());
1267         scrollBar_->SetActiveWidth(theme->GetActiveWidth());
1268         scrollBar_->SetTouchWidth(theme->GetTouchWidth());
1269     }
1270     if (!isVertical_) {
1271         scrollBar_->SetPositionMode(PositionMode::BOTTOM);
1272     } else {
1273         if (rightToLeft_) {
1274             scrollBar_->SetPositionMode(PositionMode::LEFT);
1275         }
1276     }
1277     scrollBar_->InitScrollBar(AceType::WeakClaim(this), GetContext());
1278     SetScrollBarCallback();
1279 }
1280 
InitScrollBarProxy()1281 void RenderGridScroll::InitScrollBarProxy()
1282 {
1283     if (!scrollBarProxy_) {
1284         return;
1285     }
1286     auto&& scrollCallback = [weakScroll = AceType::WeakClaim(this)](double value, int32_t source) {
1287         auto grid = weakScroll.Upgrade();
1288         if (!grid) {
1289             LOGE("render grid is released");
1290             return false;
1291         }
1292         return grid->UpdateScrollPosition(value, source);
1293     };
1294     scrollBarProxy_->UnRegisterScrollableNode(AceType::WeakClaim(this));
1295     scrollBarProxy_->RegisterScrollableNode({ AceType::WeakClaim(this), scrollCallback });
1296 }
1297 
SetScrollBarCallback()1298 void RenderGridScroll::SetScrollBarCallback()
1299 {
1300     if (!scrollBar_ || !scrollBar_->NeedScrollBar()) {
1301         return;
1302     }
1303     auto&& barEndCallback = [weakGrid = AceType::WeakClaim(this)](int32_t value) {
1304         auto grid = weakGrid.Upgrade();
1305         if (!grid) {
1306             LOGE("render grid is released");
1307             return;
1308         }
1309         grid->scrollBarOpacity_ = value;
1310         grid->MarkNeedRender();
1311     };
1312     auto&& scrollEndCallback = [weakGrid = AceType::WeakClaim(this)]() {
1313         auto grid = weakGrid.Upgrade();
1314         if (!grid) {
1315             LOGE("render grid is released");
1316             return;
1317         }
1318         LOGD("trigger scroll end callback");
1319     };
1320     auto&& scrollCallback = [weakScroll = AceType::WeakClaim(this)](double value, int32_t source) {
1321         auto grid = weakScroll.Upgrade();
1322         if (!grid) {
1323             LOGE("render grid is released");
1324             return false;
1325         }
1326         return grid->UpdateScrollPosition(value, source);
1327     };
1328     scrollBar_->SetCallBack(scrollCallback, barEndCallback, scrollEndCallback);
1329 }
1330 
ScrollToIndex(int32_t index,int32_t source)1331 void RenderGridScroll::ScrollToIndex(int32_t index, int32_t source)
1332 {
1333     if (useScrollable_ == SCROLLABLE::NO_SCROLL || index < 0) {
1334         LOGW("Not supported.");
1335         return;
1336     }
1337     auto inputIdx = GetItemMainIndex(index);
1338     if (inputIdx < endIndex_ && inputIdx > startIndex_) {
1339         LOGI("already in map, not need to jump.");
1340         return;
1341     }
1342     auto context = context_.Upgrade();
1343     if (!context) {
1344         LOGE("context is null");
1345         return;
1346     }
1347     startRankItemIndex_ = GetStartingItem(index);
1348     // Build items
1349     if ((index < startShowItemIndex_ || index > endShowItemIndex_) &&
1350         (index - startRankItemIndex_ < static_cast<int32_t>(metaData_.size()))) {
1351         // do not need layout transition
1352         auto option = context->GetExplicitAnimationOption();
1353         context->SaveExplicitAnimationOption(AnimationOption());
1354         firstLineToBottom_.emplace(index > endShowItemIndex_);
1355         if (scrollable_ && !scrollable_->IsStopped()) {
1356             scrollable_->StopScrollable();
1357         }
1358         ClearItems();
1359         ClearLayout(false);
1360         currentOffset_ = 0;
1361         MarkNeedLayout();
1362         context->SaveExplicitAnimationOption(option);
1363         return;
1364     }
1365 
1366     if (index < startShowItemIndex_) {
1367         BuildItemsForwardByRange(index, startShowItemIndex_);
1368     } else if (index > endShowItemIndex_) {
1369         BuildItemsBackwardByRange(endShowItemIndex_, index);
1370     }
1371 
1372     // when scrollLength > 0, it means grid items moving forward
1373     // when scrollLength < 0, it means grid items moving backward
1374     currentOffset_ = -CalculateScrollLength(index);
1375     PerformLayout();
1376     MarkNeedRender();
1377 }
1378 
CalculateScrollLength(int32_t index)1379 double RenderGridScroll::CalculateScrollLength(int32_t index)
1380 {
1381     double scrollLength = 0.0;
1382     auto inputIndex = GetItemMainIndex(index);
1383     do {
1384         if (inputIndex >= endIndex_) {
1385             // when inputIndex >= endIndex_, grid items need to move forward (i.e. jump backward)
1386             double originalTotalLen = 0; // total length from startIndex_ to endIndex_
1387             for (int32_t i = startIndex_; i <= endIndex_; ++i) {
1388                 if (gridCells_.find(i) != gridCells_.end()) {
1389                     originalTotalLen += GetSize(gridCells_.at(i).at(0)) + *mainGap_;
1390                 }
1391             }
1392             // reduce a mainGap because the last item need to be placed to the bottom of viewport
1393             originalTotalLen -= (*mainGap_ + firstItemOffset_);
1394             // [itemLengthOutOfViewport] is the length of grid items that is out of viewport
1395             double itemLengthOutOfViewport = originalTotalLen - GetSize(GetLayoutSize());
1396             double newlyBuiltItemLength = 0;
1397             for (int32_t i = endIndex_; i < inputIndex; ++i) {
1398                 SupplyItems(i, index);
1399                 if (i != inputIndex) {
1400                     newlyBuiltItemLength += GetSize(gridCells_.at(i).at(0)) + *mainGap_;
1401                 }
1402             }
1403             // grid items move forward by scrollLength
1404             scrollLength = itemLengthOutOfViewport + newlyBuiltItemLength;
1405             break;
1406         }
1407         if (inputIndex <= startIndex_) {
1408             // when inputIndex <= startIndex_, grid items need to move backward (i.e. jump forward)
1409             double newlyBuiltItemLength = 0;
1410             for (int32_t i = startIndex_; i >= inputIndex; --i) {
1411                 SupplyItems(i, index, i != inputIndex);
1412                 if (i != inputIndex) {
1413                     newlyBuiltItemLength -= (GetSize(gridCells_.at(i).at(0)) + *mainGap_);
1414                 }
1415             }
1416             scrollLength = newlyBuiltItemLength - firstItemOffset_;
1417             break;
1418         }
1419         LOGE("branch: [startIndex_ < inputIndex < endIndex_] should not be entered here, please check.");
1420     } while (0);
1421     return scrollLength;
1422 }
1423 
BuildItemsBackwardByRange(int32_t startItemIdx,int32_t endItemIdx)1424 void RenderGridScroll::BuildItemsBackwardByRange(int32_t startItemIdx, int32_t endItemIdx)
1425 {
1426     if (startItemIdx >= endItemIdx) {
1427         return;
1428     }
1429     auto itemIndex = startItemIdx;
1430     auto end = endItemIdx;
1431     while (itemIndex <= end) {
1432         if (GetItemMainIndex(itemIndex) != -1) {
1433             ++itemIndex;
1434             continue;
1435         }
1436         int32_t itemMain = -1;
1437         int32_t itemCross = -1;
1438         int32_t itemMainSpan = -1;
1439         int32_t itemCrossSpan = -1;
1440         if (!GetItemPropsByIndex(itemIndex, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
1441             return;
1442         }
1443         if (itemCrossSpan > *crossCount_) {
1444             itemIndex++;
1445             continue;
1446         }
1447         if (itemMain >= 0 && itemCross >= 0 && itemCross < *crossCount_ &&
1448             CheckGridPlaced(itemIndex, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
1449         } else {
1450             // itemMain < 0 means this item is not placed, place it to the end of the gridMatrix_.
1451             if (itemMain < 0) {
1452                 itemMain = gridMatrix_.rbegin()->first;
1453                 itemCross = gridMatrix_.rbegin()->second.rbegin()->first;
1454                 GetNextGrid(itemMain, itemCross);
1455                 while (!CheckGridPlaced(itemIndex, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
1456                     GetNextGrid(itemMain, itemCross);
1457                 }
1458             }
1459         }
1460         itemIndex++;
1461     }
1462     // Check current end main line is placed completely or not.
1463     int32_t lastCross = gridMatrix_.rbegin()->second.rbegin()->first;
1464     ++end;
1465     for (int32_t crossIndex = lastCross + 1; crossIndex < *crossCount_; ++crossIndex) {
1466         int32_t itemMain = -1;
1467         int32_t itemCross = -1;
1468         int32_t itemMainSpan = -1;
1469         int32_t itemCrossSpan = -1;
1470         if (!GetItemPropsByIndex(end, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
1471             return;
1472         }
1473         if (itemCrossSpan > *crossCount_) {
1474             ++end;
1475             continue;
1476         }
1477         itemMain = gridMatrix_.rbegin()->first;
1478         itemCross = itemCross == -1 ? crossIndex : itemCross;
1479         CheckGridPlaced(end, itemMain, itemCross, itemMainSpan, itemCrossSpan);
1480         ++end;
1481     }
1482 }
1483 
BuildItemsForwardByRange(int32_t startItemIdx,int32_t endItemIdx)1484 void RenderGridScroll::BuildItemsForwardByRange(int32_t startItemIdx, int32_t endItemIdx)
1485 {
1486     if (startItemIdx <= endItemIdx) {
1487         return;
1488     }
1489     auto itemIndex = startItemIdx;
1490     auto end = endItemIdx;
1491     while (itemIndex >= end) {
1492         if (GetItemMainIndex(itemIndex) != -1) {
1493             --itemIndex;
1494             continue;
1495         }
1496         int32_t itemMain = -1;
1497         int32_t itemCross = -1;
1498         int32_t itemMainSpan = -1;
1499         int32_t itemCrossSpan = -1;
1500         if (!GetItemPropsByIndex(itemIndex, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
1501             return;
1502         }
1503         if (itemCrossSpan > *crossCount_) {
1504             --itemIndex;
1505             continue;
1506         }
1507         if (itemMain >= 0 && itemCross >= 0 && itemCross < *crossCount_ &&
1508             CheckGridPlaced(itemIndex, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
1509         } else {
1510             // itemMain < 0 means this item is not placed, place it to the front of the gridMatrix_.
1511             if (itemMain < 0) {
1512                 itemMain = gridMatrix_.begin()->first;
1513                 itemCross = gridMatrix_.begin()->second.begin()->first;
1514                 GetPreviousGrid(itemMain, itemCross);
1515                 while (!CheckGridPlaced(itemIndex, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
1516                     GetPreviousGrid(itemMain, itemCross);
1517                 }
1518             }
1519         }
1520         --itemIndex;
1521     }
1522     // Check current front main line is placed completely or not.
1523     int32_t firstCross = gridMatrix_.begin()->second.begin()->first;
1524     --end;
1525     for (int32_t crossIndex = firstCross - 1; crossIndex >= 0; --crossIndex) {
1526         int32_t itemMain = -1;
1527         int32_t itemCross = -1;
1528         int32_t itemMainSpan = -1;
1529         int32_t itemCrossSpan = -1;
1530         if (!GetItemPropsByIndex(end, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
1531             return;
1532         }
1533         if (itemCrossSpan > *crossCount_) {
1534             --end;
1535             continue;
1536         }
1537         itemMain = gridMatrix_.begin()->first;
1538         itemCross = itemCross == -1 ? crossIndex : itemCross;
1539         CheckGridPlaced(end, itemMain, itemCross, itemMainSpan, itemCrossSpan);
1540         --end;
1541     }
1542 }
1543 
AnimateTo(const Dimension & position,float duration,const RefPtr<Curve> & curve)1544 bool RenderGridScroll::AnimateTo(const Dimension& position, float duration, const RefPtr<Curve>& curve)
1545 {
1546     if (!animator_->IsStopped()) {
1547         animator_->Stop();
1548     }
1549     animator_->ClearInterpolators();
1550     animateDelta_ = 0.0;
1551     auto animation = AceType::MakeRefPtr<CurveAnimation<double>>(GetCurrentOffset(), NormalizeToPx(position), curve);
1552     animation->AddListener([weakScroll = AceType::WeakClaim(this)](double value) {
1553         auto scroll = weakScroll.Upgrade();
1554         if (scroll) {
1555             scroll->DoJump(value, SCROLL_FROM_JUMP);
1556         }
1557     });
1558     animator_->AddInterpolator(animation);
1559     animator_->SetDuration(duration);
1560     animator_->ClearStopListeners();
1561     animator_->AddStopListener([weakScroll = AceType::WeakClaim(this)]() {
1562         auto scroll = weakScroll.Upgrade();
1563         if (scroll) {
1564             scroll->animateDelta_ = 0.0;
1565         }
1566     });
1567     animator_->Play();
1568     return true;
1569 }
1570 
CurrentOffset()1571 Offset RenderGridScroll::CurrentOffset()
1572 {
1573     auto ctx = GetContext().Upgrade();
1574     if (!ctx) {
1575         return useScrollable_ == SCROLLABLE::HORIZONTAL ? Offset(GetCurrentOffset(), 0.0)
1576                                                         : Offset(0.0, GetCurrentOffset());
1577     }
1578     auto mainOffset = ctx->ConvertPxToVp(Dimension(GetCurrentOffset(), DimensionUnit::PX));
1579     Offset currentOffset = useScrollable_ == SCROLLABLE::HORIZONTAL ? Offset(mainOffset, 0.0) : Offset(0.0, mainOffset);
1580     return currentOffset;
1581 }
1582 
GetItemPropsByIndex(int32_t itemIndex,int32_t & itemMain,int32_t & itemCross,int32_t & itemMainSpan,int32_t & itemCrossSpan)1583 bool RenderGridScroll::GetItemPropsByIndex(
1584     int32_t itemIndex, int32_t& itemMain, int32_t& itemCross, int32_t& itemMainSpan, int32_t& itemCrossSpan)
1585 {
1586     auto item = items_.find(itemIndex);
1587     if (item != items_.end()) {
1588         itemMain = GetItemMainIndex(item->second, true);
1589         itemCross = GetItemMainIndex(item->second, false);
1590         itemMainSpan = GetItemSpan(item->second, useScrollable_ != SCROLLABLE::HORIZONTAL);
1591         itemCrossSpan = GetItemSpan(item->second, useScrollable_ == SCROLLABLE::HORIZONTAL);
1592     } else {
1593         if (!getChildSpanByIndex_(itemIndex, useScrollable_ == SCROLLABLE::HORIZONTAL, itemMain, itemCross,
1594                 itemMainSpan, itemCrossSpan)) {
1595             LOGW("Not valid.");
1596             return false;
1597         }
1598     }
1599     return true;
1600 }
1601 
ScrollToEdge(OHOS::Ace::ScrollEdgeType edgeType,bool smooth)1602 void RenderGridScroll::ScrollToEdge(OHOS::Ace::ScrollEdgeType edgeType, bool smooth)
1603 {
1604     if (edgeType != ScrollEdgeType::SCROLL_TOP) {
1605         LOGW("Not supported yet");
1606         return;
1607     }
1608     if (items_.empty() && updateFlag_) {
1609         return;
1610     }
1611     if (scrollable_ && !scrollable_->IsStopped()) {
1612         scrollable_->StopScrollable();
1613     }
1614     currentItemIndex_ = 0;
1615     startRankItemIndex_ = GetStartingItem(currentItemIndex_);
1616     ClearItems();
1617     ClearLayout(false);
1618     MarkNeedLayout();
1619 }
1620 
DoJump(double position,int32_t source)1621 void RenderGridScroll::DoJump(double position, int32_t source)
1622 {
1623     double delta = position - animateDelta_;
1624     UpdateScrollPosition(delta, source);
1625     animateDelta_ = position;
1626 }
1627 
GetIndexByPosition(double position) const1628 int32_t RenderGridScroll::GetIndexByPosition(double position) const
1629 {
1630     int32_t index = 0;
1631     double startPosition = 0.0;
1632     double endPosition = 0.0;
1633 
1634     for (const auto& item : childrenInRect_) {
1635         if (!item || item->GetChildren().empty()) {
1636             continue;
1637         }
1638 
1639         auto gridItem = AceType::DynamicCast<RenderGridLayoutItem>(item);
1640         if (!gridItem) {
1641             break;
1642         }
1643 
1644         startPosition = item->GetPosition().GetY();
1645         endPosition = item->GetPosition().GetY() + item->GetLayoutSize().Height();
1646 
1647         if ((position > startPosition && position < endPosition) || NearEqual(position, startPosition) ||
1648             startPosition > position) {
1649             return gridItem->GetIndex();
1650         }
1651     }
1652     return index;
1653 }
1654 
OnScrolled(int32_t scrolled) const1655 void RenderGridScroll::OnScrolled(int32_t scrolled) const
1656 {
1657     if (scrolledEventFun_) {
1658         auto event = std::make_shared<GridEventInfo>(scrolled);
1659         if (event) {
1660             scrolledEventFun_(event);
1661         }
1662     }
1663 }
1664 
OnPaintFinish()1665 void RenderGridScroll::OnPaintFinish()
1666 {
1667     RenderNode::OnPaintFinish();
1668     if (showItem_.empty()) {
1669         return;
1670     }
1671     auto currentStartItemCount = *showItem_.begin();
1672     auto currentEndItemCount = *showItem_.rbegin();
1673     if ((startShowItemIndex_ != currentStartItemCount) || (endShowItemIndex_ != currentEndItemCount)) {
1674         startShowItemIndex_ = currentStartItemCount;
1675         endShowItemIndex_ = currentEndItemCount;
1676     }
1677 }
1678 
OnPredictLayout(int64_t deadline)1679 void RenderGridScroll::OnPredictLayout(int64_t deadline)
1680 {
1681     auto context = context_.Upgrade();
1682     if (!context) {
1683         return;
1684     }
1685     if (!context->IsTransitionStop()) {
1686         LOGI("In page transition, skip predict.");
1687         return;
1688     }
1689     if (loadingIndex_ == -1) {
1690         DealCache(startIndex_, endIndex_);
1691         if (loadingIndex_ == -1) {
1692             if (startIndex_ == 0 && startRankItemIndex_ > 0) {
1693                 LoadForward();
1694                 MarkNeedPredictLayout();
1695             }
1696             return;
1697         }
1698     }
1699     ACE_SCOPED_TRACE("OnPredictLayout %d", loadingIndex_);
1700     if (gridMatrix_.find(loadingIndex_) != gridMatrix_.end() || Rank(loadingIndex_)) {
1701         auto iter = gridMatrix_.find(loadingIndex_);
1702         if (iter != gridMatrix_.end()) {
1703             for (const auto& item : iter->second) {
1704                 if (items_.find(item.second) == items_.end()) {
1705                     if (!buildChildByIndex_(item.second)) {
1706                         break;
1707                     }
1708                 }
1709                 // Stop predictLayout less than 3 milliseconds before the next vsync arrives.
1710                 if (GetSysTimestamp() + TIME_THRESHOLD > deadline) {
1711                     MarkNeedPredictLayout();
1712                     return;
1713                 }
1714             }
1715             SupplyItems(loadingIndex_);
1716         }
1717         MarkNeedPredictLayout();
1718     } else {
1719         loadingIndex_ = -1;
1720     }
1721 }
1722 
IsAxisScrollable(AxisDirection direction)1723 bool RenderGridScroll::IsAxisScrollable(AxisDirection direction)
1724 {
1725     return (((AxisEvent::IsDirectionUp(direction) || AxisEvent::IsDirectionLeft(direction)) && !reachHead_) ||
1726             ((AxisEvent::IsDirectionLeft(direction) || AxisEvent::IsDirectionRight(direction)) && !reachTail_));
1727 }
1728 
HandleAxisEvent(const AxisEvent & event)1729 void RenderGridScroll::HandleAxisEvent(const AxisEvent& event)
1730 {
1731 }
1732 
ProvideRestoreInfo()1733 std::string RenderGridScroll::ProvideRestoreInfo()
1734 {
1735     int32_t currentItemIndex = 0;
1736     auto items = gridMatrix_.find(startIndex_);
1737     if (items != gridMatrix_.end() && !items->second.empty()) {
1738         currentItemIndex = items->second.begin()->second;
1739     }
1740 
1741     if (currentItemIndex > 0) {
1742         return std::to_string(currentItemIndex);
1743     }
1744     return "";
1745 }
1746 
ApplyRestoreInfo()1747 void RenderGridScroll::ApplyRestoreInfo()
1748 {
1749     if (GetRestoreInfo().empty()) {
1750         return;
1751     }
1752     currentItemIndex_ = StringUtils::StringToInt(GetRestoreInfo());
1753     startRankItemIndex_ = GetStartingItem(currentItemIndex_);
1754     SetRestoreInfo("");
1755 }
1756 
1757 } // namespace OHOS::Ace::V2
1758