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