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