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