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