1 /*
2 * Copyright (c) 2021 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/components/grid_layout/grid_layout_component.h"
26 #include "core/components/grid_layout/render_grid_layout_item.h"
27 #include "core/components_v2/grid/grid_scroll_controller.h"
28 #include "core/event/ace_event_helper.h"
29
30 namespace OHOS::Ace::V2 {
31
32 namespace {
33
34 const char UNIT_PERCENT[] = "%";
35 const char UNIT_RATIO[] = "fr";
36 constexpr int32_t TIMETHRESHOLD = 3 * 1000000; // 3 millisecond
37 constexpr int32_t MICROSEC_TO_NANOSEC = 1000;
38
39 } // namespace
40
ToJSONString() const41 std::string GridEventInfo::ToJSONString() const
42 {
43 return std::string("\"grid\",{\"first\":").append(std::to_string(scrollIndex_)).append("},null");
44 }
45
~RenderGridScroll()46 RenderGridScroll::~RenderGridScroll()
47 {
48 if (scrollBarProxy_) {
49 scrollBarProxy_->UnRegisterScrollableNode(AceType::WeakClaim(this));
50 }
51 }
52
Update(const RefPtr<Component> & component)53 void RenderGridScroll::Update(const RefPtr<Component>& component)
54 {
55 InitScrollBar(component);
56 if (!NeedUpdate(component)) {
57 return;
58 }
59
60 LOGD("RenderGridScroll::Update");
61 useScrollable_ = SCROLLABLE::NO_SCROLL;
62 mainSize_ = &rowSize_;
63 crossSize_ = &colSize_;
64 mainCount_ = &rowCount_;
65 crossCount_ = &colCount_;
66 crossGap_ = &colGap_;
67 mainGap_ = &rowGap_;
68 startRankItemIndex_ = 0;
69 currentItemIndex_ = 0;
70 RenderGridLayout::Update(component);
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_ = AceType::MakeRefPtr<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 itor = items_.try_emplace(index, renderNode);
114 if (itor.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 }
154 });
155 scrollable_->Initialize(context_);
156 }
157
UpdateScrollPosition(double offset,int32_t source)158 bool RenderGridScroll::UpdateScrollPosition(double offset, int32_t source)
159 {
160 if (source == SCROLL_FROM_START) {
161 return true;
162 }
163
164 if (NearZero(offset)) {
165 return true;
166 }
167 if (scrollBar_ && scrollBar_->NeedScrollBar()) {
168 scrollBar_->SetActive(SCROLL_FROM_CHILD != source);
169 }
170
171 if (reachHead_ && reachTail_) {
172 return false;
173 }
174
175 if (offset > 0.0) {
176 if (reachHead_) {
177 return false;
178 }
179 reachTail_ = false;
180 } else {
181 if (reachTail_) {
182 return false;
183 }
184 reachHead_ = false;
185 }
186
187 currentOffset_ += Round(offset);
188 MarkNeedLayout(true);
189 return true;
190 }
191
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)192 void RenderGridScroll::OnTouchTestHit(
193 const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
194 {
195 if (!GetVisible()) {
196 return;
197 }
198 if (!scrollable_ || !scrollable_->Available()) {
199 return;
200 }
201 if (scrollBar_ && scrollBar_->InBarRegion(globalPoint_ - coordinateOffset)) {
202 scrollBar_->AddScrollBarController(coordinateOffset, result);
203 } else {
204 scrollable_->SetCoordinateOffset(coordinateOffset);
205 scrollable_->SetDragTouchRestrict(touchRestrict);
206 }
207 result.emplace_back(scrollable_);
208 }
209
IsChildrenTouchEnable()210 bool RenderGridScroll::IsChildrenTouchEnable()
211 {
212 return scrollable_->IsMotionStop();
213 }
214
SetChildPosition(const RefPtr<RenderNode> & child,int32_t main,int32_t cross,int32_t mainSpan,int32_t crossSpan)215 void RenderGridScroll::SetChildPosition(
216 const RefPtr<RenderNode>& child, int32_t main, int32_t cross, int32_t mainSpan, int32_t crossSpan)
217 {
218 // Calculate the position for current child.
219 double positionMain = 0.0;
220 double positionCross = 0.0;
221 if (main < startIndex_) {
222 positionMain -= GetSize(gridCells_.at(main).at(0));
223 positionMain += (main - startIndex_) * (*mainGap_);
224 } else {
225 for (int32_t i = startIndex_; i < main; ++i) {
226 positionMain += GetSize(gridCells_.at(i).at(0));
227 }
228 positionMain += (main - startIndex_) * (*mainGap_);
229 }
230
231 for (int32_t i = 0; i < cross; ++i) {
232 positionCross += GetSize(gridCells_.at(main).at(i), false);
233 }
234 positionCross += cross * (*crossGap_);
235
236 // Calculate the size for current child.
237 double mainLen = 0.0;
238 double crossLen = 0.0;
239 for (int32_t i = 0; i < mainSpan; ++i) {
240 mainLen += GetSize(gridCells_.at(main + i).at(0));
241 }
242
243 mainLen += (mainSpan - 1) * (*mainGap_);
244 for (int32_t i = 0; i < crossSpan; ++i) {
245 crossLen += GetSize(gridCells_.at(main).at(cross + i), false);
246 }
247 crossLen += (crossSpan - 1) * (*crossGap_);
248
249 // If RTL, place the item from right.
250 if (rightToLeft_) {
251 if (useScrollable_ != SCROLLABLE::HORIZONTAL) {
252 positionCross = colSize_ - positionCross - crossLen;
253 }
254 }
255
256 double mainOffset = (mainLen - GetSize(child->GetLayoutSize())) / 2.0;
257 double crossOffset = (crossLen - GetSize(child->GetLayoutSize(), false)) / 2.0;
258
259 Offset offset;
260 if (useScrollable_ != SCROLLABLE::HORIZONTAL) {
261 offset = Offset(positionCross + crossOffset, positionMain + mainOffset - firstItemOffset_);
262 } else {
263 offset = Offset(positionMain + mainOffset - firstItemOffset_, positionCross + crossOffset);
264 }
265
266 child->SetPosition(offset);
267 }
268
GetItemMainIndex(const RefPtr<RenderNode> & child,bool isMain) const269 int32_t RenderGridScroll::GetItemMainIndex(const RefPtr<RenderNode>& child, bool isMain) const
270 {
271 if (useScrollable_ == SCROLLABLE::HORIZONTAL) {
272 if (isMain) {
273 return GetItemColumnIndex(child);
274 } else {
275 return GetItemRowIndex(child);
276 }
277 } else {
278 if (isMain) {
279 return GetItemRowIndex(child);
280 } else {
281 return GetItemColumnIndex(child);
282 }
283 }
284 }
285
SetMainSize(Size & dst,const Size & src)286 void RenderGridScroll::SetMainSize(Size& dst, const Size& src)
287 {
288 if (useScrollable_ == SCROLLABLE::HORIZONTAL) {
289 dst.SetWidth(src.Width());
290 } else {
291 dst.SetHeight(src.Height());
292 }
293 }
294
GetSize(const Size & src,bool isMain) const295 double RenderGridScroll::GetSize(const Size& src, bool isMain) const
296 {
297 if (useScrollable_ == SCROLLABLE::HORIZONTAL) {
298 return isMain ? src.Width() : src.Height();
299 }
300
301 return isMain ? src.Height() : src.Width();
302 }
303
GetGridSize()304 bool RenderGridScroll::GetGridSize()
305 {
306 double rowSize = ((gridHeight_ > 0.0) && (gridHeight_ < GetLayoutParam().GetMaxSize().Height()))
307 ? gridHeight_
308 : GetLayoutParam().GetMaxSize().Height();
309 double colSize = ((gridWidth_ > 0.0) && (gridWidth_ < GetLayoutParam().GetMaxSize().Width()))
310 ? gridWidth_
311 : GetLayoutParam().GetMaxSize().Width();
312 if (NearEqual(rowSize_, Size::INFINITE_SIZE)) {
313 if ((rowsArgs_.find(UNIT_PERCENT) != std::string::npos || rowsArgs_.find(UNIT_RATIO) != std::string::npos)) {
314 rowSize = viewPort_.Height();
315 }
316 } else if (rowsArgs_.empty()) {
317 useScrollable_ = SCROLLABLE::VERTICAL;
318 }
319 if (NearEqual(colSize_, Size::INFINITE_SIZE)) {
320 if ((colsArgs_.find(UNIT_PERCENT) != std::string::npos || colsArgs_.find(UNIT_RATIO) != std::string::npos)) {
321 colSize = viewPort_.Width();
322 }
323 } else if (colsArgs_.empty()) {
324 useScrollable_ = SCROLLABLE::HORIZONTAL;
325 mainSize_ = &colSize_;
326 crossSize_ = &rowSize_;
327 mainCount_ = &colCount_;
328 crossCount_ = &rowCount_;
329 crossGap_ = &rowGap_;
330 mainGap_ = &colGap_;
331 }
332 LOGD("GetGridSize %lf, %lf [%lf- %lf]", rowSize, colSize, rowSize_, colSize_);
333 if (rowSize != rowSize_ || colSize != colSize_) {
334 rowSize_ = rowSize;
335 colSize_ = colSize;
336 CreateScrollable();
337 return true;
338 }
339 return false;
340 }
341
BuildGrid(std::vector<double> & main,std::vector<double> & cross)342 void RenderGridScroll::BuildGrid(std::vector<double>& main, std::vector<double>& cross)
343 {
344 if (useScrollable_ == SCROLLABLE::NO_SCROLL) {
345 main = ParseArgs(PreParseRows(), rowSize_, rowGap_);
346 cross = ParseArgs(PreParseCols(), colSize_, colGap_);
347 } else if (useScrollable_ == SCROLLABLE::VERTICAL) {
348 cross = ParseArgs(PreParseCols(), colSize_, colGap_);
349 int32_t col = 0;
350 for (auto width : cross) {
351 metaData_[col] = Size(width, Size::INFINITE_SIZE);
352 ++col;
353 }
354 } else if (useScrollable_ == SCROLLABLE::HORIZONTAL) {
355 cross = ParseArgs(PreParseRows(), rowSize_, rowGap_);
356 int32_t row = 0;
357 for (auto height : cross) {
358 metaData_[row] = Size(Size::INFINITE_SIZE, height);
359 ++row;
360 }
361 }
362 }
363
InitialGridProp()364 void RenderGridScroll::InitialGridProp()
365 {
366 // Not first time layout after update, no need to initial.
367 if (!GetGridSize() && !updateFlag_) {
368 return;
369 }
370 ACE_SCOPED_TRACE("InitialGridProp");
371 OnDataSourceUpdated(0);
372 rowGap_ = NormalizePercentToPx(userRowGap_, true);
373 colGap_ = NormalizePercentToPx(userColGap_, false);
374 std::vector<double> main;
375 std::vector<double> cross;
376 BuildGrid(main, cross);
377
378 // Initialize the columnCount and rowCount, default is 1
379 *crossCount_ = cross.size();
380 *mainCount_ = main.size();
381 gridCells_.clear();
382 items_.clear();
383 UpdateAccessibilityAttr();
384 if (buildChildByIndex_) {
385 int32_t endIndex = -1;
386 while (endIndex < currentItemIndex_) {
387 if (!Rank(*mainCount_, *mainCount_ == 0 ? startRankItemIndex_ : -1)) {
388 // When [firstLineToBottom_] does not equal to [std::nullopt], it indicates that this [InitialGridProp]
389 // is called after [ScrollToIndex].
390 // This is the case when it scrolls to the last line and the last line is not full.
391 // So we need to add a line to [*mainCount_].
392 (*mainCount_) += firstLineToBottom_ ? 1 : 0;
393 break;
394 }
395 (*mainCount_)++;
396 auto mainItor = gridMatrix_.find(*mainCount_ - 1);
397 if (mainItor == gridMatrix_.end()) {
398 break;
399 }
400 for (int32_t crossIndex = *crossCount_ - 1; crossIndex >= 0; crossIndex--) {
401 auto iter = mainItor->second.find(crossIndex);
402 if (iter != mainItor->second.end()) {
403 endIndex = iter->second;
404 break;
405 }
406 }
407 }
408 currentItemIndex_ = 0;
409
410 SupplyItems(*mainCount_ > 0 ? *mainCount_ - 1 : 0);
411 startIndex_ = *mainCount_ > 0 ? *mainCount_ - 1 : 0;
412
413 if (NearZero(currentOffset_)) {
414 needCalculateViewPort_ = true;
415 }
416 }
417 updateFlag_ = false;
418 if (firstLineToBottom_ && firstLineToBottom_.value()) {
419 // calculate the distance from the first line to the last line
420 currentOffset_ = *mainSize_ - GetSize(gridCells_.at(0).at(0));
421 needCalculateViewPort_ = false;
422 }
423 firstLineToBottom_ = std::nullopt;
424 }
425
BuildLazyGridLayout(int32_t index,double sizeNeed)426 double RenderGridScroll::BuildLazyGridLayout(int32_t index, double sizeNeed)
427 {
428 if (!buildChildByIndex_ || index < 0 || NearZero(sizeNeed)) {
429 return 0.0;
430 }
431 LOGD("BuildLazyGridLayout index = %d sizeNeed = %lf", index, sizeNeed);
432 double size = 0.0;
433 int32_t startIndex = index;
434 while (size < sizeNeed) {
435 auto suppleSize = SupplyItems(startIndex);
436 if (NearZero(suppleSize)) {
437 break;
438 }
439 *mainCount_ = ++startIndex;
440 size += suppleSize + *mainGap_;
441 }
442 return size;
443 }
444
CheckGridPlaced(int32_t index,int32_t main,int32_t cross,int32_t & mainSpan,int32_t & crossSpan)445 bool RenderGridScroll::CheckGridPlaced(
446 int32_t index, int32_t main, int32_t cross, int32_t& mainSpan, int32_t& crossSpan)
447 {
448 LOGD("CheckGridPlaced %{public}d %{public}d %{public}d %{public}d %{public}d", index, main, cross, mainSpan,
449 crossSpan);
450 auto mainIter = gridMatrix_.find(main);
451 if (mainIter != gridMatrix_.end()) {
452 auto crossIter = mainIter->second.find(cross);
453 if (crossIter != mainIter->second.end()) {
454 return false;
455 }
456 }
457 if (cross + crossSpan > *crossCount_) {
458 return false;
459 }
460
461 for (int32_t i = 0; i < mainSpan; i++) {
462 mainIter = gridMatrix_.find(i + main);
463 if (mainIter != gridMatrix_.end()) {
464 for (int32_t j = 0; j < crossSpan; j++) {
465 if (mainIter->second.find(j + cross) != mainIter->second.end()) {
466 return false;
467 }
468 }
469 }
470 }
471
472 for (int32_t i = main; i < main + mainSpan; ++i) {
473 std::map<int32_t, int32_t> mainMap;
474 auto iter = gridMatrix_.find(i);
475 if (iter != gridMatrix_.end()) {
476 mainMap = iter->second;
477 }
478 for (int32_t j = cross; j < cross + crossSpan; ++j) {
479 mainMap.emplace(std::make_pair(j, index));
480 }
481 gridMatrix_[i] = mainMap;
482 }
483 LOGD("CheckGridPlaced done %{public}d %{public}d %{public}d %{public}d %{public}d", index, main, cross, mainSpan,
484 crossSpan);
485 return true;
486 }
487
LayoutChild(const RefPtr<RenderNode> & child,int32_t main,int32_t cross,int32_t mainSpan,int32_t crossSpan,bool needPosition)488 void RenderGridScroll::LayoutChild(const RefPtr<RenderNode>& child, int32_t main, int32_t cross, int32_t mainSpan,
489 int32_t crossSpan, bool needPosition)
490 {
491 child->Layout(MakeInnerLayoutParam(main, cross, mainSpan, crossSpan));
492 SetMainSize(gridCells_.at(main).at(cross), child->GetLayoutSize());
493 if (GetSize(gridCells_.at(main).at(0)) < GetSize(gridCells_.at(main).at(cross))) {
494 SetMainSize(gridCells_.at(main).at(0), gridCells_.at(main).at(cross));
495 }
496 if (!needPosition) {
497 return;
498 }
499 if (useScrollable_ != SCROLLABLE::HORIZONTAL) {
500 child->SetPosition(Offset(0, *mainSize_ + *mainGap_));
501 } else {
502 child->SetPosition(Offset(*mainSize_ + *mainGap_, 0));
503 }
504 }
505
GetNextGrid(int32_t & curMain,int32_t & curCross) const506 void RenderGridScroll::GetNextGrid(int32_t& curMain, int32_t& curCross) const
507 {
508 ++curCross;
509 if (curCross >= *crossCount_) {
510 curCross = 0;
511 ++curMain;
512 }
513 }
514
GetPreviousGrid(int32_t & curMain,int32_t & curCross)515 void RenderGridScroll::GetPreviousGrid(int32_t& curMain, int32_t& curCross)
516 {
517 --curCross;
518 if (curCross < 0) {
519 curCross = *crossCount_;
520 --curMain;
521 }
522 }
523
MakeInnerLayoutParam(int32_t main,int32_t cross,int32_t mainSpan,int32_t crossSpan) const524 LayoutParam RenderGridScroll::MakeInnerLayoutParam(
525 int32_t main, int32_t cross, int32_t mainSpan, int32_t crossSpan) const
526 {
527 LayoutParam innerLayout;
528 double mainLen = 0.0;
529 double crossLen = 0.0;
530 for (int32_t i = 0; i < mainSpan; ++i) {
531 if (gridCells_.find(main + i) != gridCells_.end() &&
532 gridCells_.at(main + i).find(cross) != gridCells_.at(main + i).end()) {
533 mainLen += GetSize(gridCells_.at(main + i).at(cross));
534 }
535 }
536 mainLen += (mainSpan - 1) * (*mainGap_);
537 for (int32_t i = 0; i < crossSpan; ++i) {
538 if (gridCells_.find(main) != gridCells_.end() &&
539 gridCells_.at(main).find(cross + i) != gridCells_.at(main).end()) {
540 crossLen += GetSize(gridCells_.at(main).at(cross + i), false);
541 }
542 }
543 crossLen += (crossSpan - 1) * (*crossGap_);
544
545 Size size;
546 if (useScrollable_ == SCROLLABLE::HORIZONTAL) {
547 size = Size(mainLen, crossLen);
548 } else {
549 size = Size(crossLen, mainLen);
550 }
551 if (crossAxisAlign_ == FlexAlign::STRETCH) {
552 innerLayout.SetMinSize(size);
553 innerLayout.SetMaxSize(size);
554 } else {
555 innerLayout.SetMaxSize(size);
556 }
557 return innerLayout;
558 }
559
LoadForward()560 void RenderGridScroll::LoadForward()
561 {
562 auto firstItem = GetStartingItem(startRankItemIndex_ - 1);
563
564 decltype(gridCells_) gridCells(std::move(gridCells_));
565 decltype(gridMatrix_) gridMatrix(std::move(gridMatrix_));
566
567 int32_t count = 0;
568 int32_t endIndex = -1;
569 while (endIndex < startRankItemIndex_ - 1) {
570 if (!Rank(count, count == 0 ? firstItem : -1)) {
571 break;
572 }
573 count++;
574 auto mainItor = gridMatrix_.find(count - 1);
575 if (mainItor == gridMatrix_.end()) {
576 break;
577 }
578 for (int32_t cross = *crossCount_ - 1; cross >= 0; cross--) {
579 auto iter = mainItor->second.find(cross);
580 if (iter != mainItor->second.end()) {
581 endIndex = iter->second;
582 break;
583 }
584 }
585 }
586 startRankItemIndex_ = firstItem;
587 if (count == 0) {
588 return;
589 }
590 for (const auto& item : gridMatrix) {
591 gridMatrix_[item.first + count] = item.second;
592 }
593 for (const auto& item : gridCells) {
594 gridCells_[item.first + count] = item.second;
595 }
596
597 decltype(inCache_) inCache(std::move(inCache_));
598 for (const auto& item : inCache) {
599 inCache_.insert(item + count);
600 }
601
602 *mainCount_ += count;
603 startIndex_ += count;
604 }
605
CaculateViewPort()606 void RenderGridScroll::CaculateViewPort()
607 {
608 while (!NearZero(currentOffset_) || needCalculateViewPort_) {
609 if (currentOffset_ > 0) {
610 // move to top/left of first row/column
611 if (!NearZero(firstItemOffset_)) {
612 if (gridCells_.find(startIndex_ + 1) != gridCells_.end()) {
613 currentOffset_ += GetSize(gridCells_.at(startIndex_++).at(0)) + *mainGap_ - firstItemOffset_;
614 }
615 firstItemOffset_ = 0.0;
616 }
617 // Move up
618 while (currentOffset_ > 0) {
619 if (startIndex_ > 0) {
620 if (gridCells_.find(startIndex_ - 1) == gridCells_.end()) {
621 SupplyItems(startIndex_ - 1);
622 }
623 currentOffset_ -= GetSize(gridCells_.at(startIndex_-- - 1).at(0)) + *mainGap_;
624 }
625 if (startIndex_ == 0 && startRankItemIndex_ > 0 && currentOffset_ > 0) {
626 LoadForward();
627 }
628 if (startIndex_ == 0) {
629 break;
630 }
631 }
632 if (currentOffset_ < 0) {
633 firstItemOffset_ -= currentOffset_;
634 } else {
635 if (startIndex_ == 0) {
636 reachHead_ = true;
637 }
638 }
639 currentOffset_ = 0.0;
640 } else {
641 if (!NearZero(firstItemOffset_)) {
642 currentOffset_ -= firstItemOffset_;
643 firstItemOffset_ = 0.0;
644 }
645 // Move down
646 while (startIndex_ < *mainCount_ && (currentOffset_ < 0 || needCalculateViewPort_)) {
647 currentOffset_ += GetSize(gridCells_.at(startIndex_++).at(0)) + *mainGap_;
648 }
649 needCalculateViewPort_ = false;
650 if (currentOffset_ > 0) {
651 firstItemOffset_ = GetSize(gridCells_.at(--startIndex_).at(0)) + *mainGap_ - currentOffset_;
652 currentOffset_ = 0.0;
653 } else {
654 if (!GreatOrEqual(0.0, BuildLazyGridLayout(*mainCount_, -currentOffset_))) {
655 continue;
656 }
657 }
658 currentOffset_ = 0.0;
659 auto blank = CalculateBlankOfEnd();
660 if (GreatOrEqual(0.0, blank)) {
661 return;
662 }
663 // request new item.
664 blank -= BuildLazyGridLayout(*mainCount_, blank);
665 if (blank < 0) {
666 return;
667 }
668 blank = blank - firstItemOffset_;
669 firstItemOffset_ = 0;
670 // Move up
671 while (blank > 0) {
672 if (startIndex_ == 0 && startRankItemIndex_ > 0) {
673 LoadForward();
674 }
675 if (startIndex_ == 0) {
676 break;
677 }
678 if (gridCells_.find(startIndex_ - 1) == gridCells_.end()) {
679 SupplyItems(startIndex_ - 1);
680 }
681 blank -= GetSize(gridCells_.at(--startIndex_).at(0)) + *mainGap_;
682 }
683 firstItemOffset_ -= blank;
684 if (firstItemOffset_ < 0) {
685 firstItemOffset_ = 0;
686 }
687 reachTail_ = true;
688 }
689 }
690 }
691
CalculateBlankOfEnd()692 double RenderGridScroll::CalculateBlankOfEnd()
693 {
694 double drawLength = 0.0 - firstItemOffset_;
695 for (int32_t main = startIndex_; main < *mainCount_; main++) {
696 drawLength += GetSize(gridCells_.at(main).at(0)) + *mainGap_;
697 if (GreatOrEqual(drawLength, *mainSize_)) {
698 break;
699 }
700 }
701 return *mainSize_ - drawLength;
702 }
703
SupplyItems(int32_t mainIndex,int32_t itemIndex,bool needPosition)704 double RenderGridScroll::SupplyItems(int32_t mainIndex, int32_t itemIndex, bool needPosition)
705 {
706 ACE_SCOPED_TRACE("SupplyItems %d", mainIndex);
707 if (loadingIndex_ == mainIndex) {
708 loadingIndex_ = -1;
709 }
710 if (gridMatrix_.find(mainIndex) == gridMatrix_.end()) {
711 Rank(mainIndex, itemIndex);
712 }
713 gridCells_.try_emplace(mainIndex, metaData_);
714 auto iter = gridMatrix_.find(mainIndex);
715 if (iter != gridMatrix_.end()) {
716 int32_t frontIndex = -1;
717 for (const auto& item : iter->second) {
718 if (item.second != frontIndex) {
719 if (items_.find(item.second) != items_.end() || buildChildByIndex_(item.second)) {
720 int32_t itemRowSpan = GetItemSpan(items_[item.second], true);
721 int32_t itemColSpan = GetItemSpan(items_[item.second], false);
722 LayoutChild(items_[item.second], mainIndex, item.first, itemRowSpan, itemColSpan, needPosition);
723 }
724 }
725 frontIndex = item.second;
726 }
727 inCache_.insert(mainIndex);
728 return NearEqual(GetSize(gridCells_[mainIndex][0]), Size::INFINITE_SIZE) ? 0.0
729 : GetSize(gridCells_[mainIndex][0]);
730 }
731 return 0.0;
732 }
733
Rank(int32_t mainIndex,int32_t itemIndex)734 bool RenderGridScroll::Rank(int32_t mainIndex, int32_t itemIndex)
735 {
736 if (gridMatrix_.find(mainIndex) != gridMatrix_.end()) {
737 return true;
738 }
739 ACE_SCOPED_TRACE("Rank [%d]", mainIndex);
740 if (itemIndex == -1) {
741 auto mainItor = gridMatrix_.find(mainIndex - 1);
742 if (mainItor != gridMatrix_.end()) {
743 for (int32_t cross = *crossCount_ - 1; cross >= 0; cross--) {
744 auto iter = mainItor->second.find(cross);
745 if (iter != mainItor->second.end()) {
746 itemIndex = iter->second + 1;
747 break;
748 }
749 }
750 }
751 }
752 if (itemIndex == -1) {
753 LOGE("failed, itemIndex = -1, mainIndex = %d", mainIndex);
754 return false;
755 }
756
757 bool isFilled = false;
758 int32_t index = mainIndex;
759 int32_t crossIndex = 0;
760 while (!isFilled) {
761 int32_t itemMain = -1;
762 int32_t itemCross = -1;
763 int32_t itemMainSpan = -1;
764 int32_t itemCrossSpan = -1;
765 auto item = items_.find(itemIndex);
766 if (item != items_.end()) {
767 itemMain = GetItemMainIndex(item->second, true);
768 itemCross = GetItemMainIndex(item->second, false);
769 itemMainSpan = GetItemSpan(item->second, useScrollable_ != SCROLLABLE::HORIZONTAL);
770 itemCrossSpan = GetItemSpan(item->second, useScrollable_ == SCROLLABLE::HORIZONTAL);
771 } else {
772 if (!getChildSpanByIndex_(itemIndex, useScrollable_ == SCROLLABLE::HORIZONTAL, itemMain, itemCross,
773 itemMainSpan, itemCrossSpan)) {
774 return false;
775 }
776 }
777
778 if (itemCrossSpan > *crossCount_) {
779 itemIndex++;
780 continue;
781 }
782 if (itemMain >= 0 && itemCross >= 0 && itemCross < *crossCount_ &&
783 CheckGridPlaced(itemIndex, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
784 } else {
785 while (!CheckGridPlaced(itemIndex, mainIndex, crossIndex, itemMainSpan, itemCrossSpan)) {
786 GetNextGrid(mainIndex, crossIndex);
787 if (mainIndex > index) {
788 isFilled = true;
789 break;
790 }
791 }
792 }
793 itemIndex++;
794 }
795 return true;
796 }
797
PerformLayout()798 void RenderGridScroll::PerformLayout()
799 {
800 if (rowsArgs_.empty() && colsArgs_.empty()) {
801 return;
802 }
803 if (RenderGridLayout::GetChildren().empty() && !buildChildByIndex_) {
804 return;
805 }
806 lastOffset_ = startMainPos_ + firstItemOffset_ - currentOffset_;
807 InitialGridProp();
808 CaculateViewPort();
809 showItem_.clear();
810 childrenInRect_.clear();
811 double drawLength = 0.0 - firstItemOffset_;
812 int32_t main = startIndex_ > 0 ? startIndex_ - 1 : startIndex_;
813 LOGD("startIndex_=[%d], firstItemOffset_=[%lf]", startIndex_, firstItemOffset_);
814 for (; main < *mainCount_; main++) {
815 if (gridCells_.find(main) == gridCells_.end()) {
816 continue;
817 }
818 for (int32_t cross = 0; cross < *crossCount_; cross++) {
819 auto mainIter = gridMatrix_.find(main);
820 if (mainIter == gridMatrix_.end()) {
821 continue;
822 }
823 auto crossIter = mainIter->second.find(cross);
824 if (crossIter == mainIter->second.end()) {
825 continue;
826 }
827 if (buildChildByIndex_ && inCache_.count(main) == 0) {
828 SupplyItems(main);
829 }
830 if (showItem_.count(crossIter->second) == 0) {
831 showItem_.insert(crossIter->second);
832 auto item = items_.find(crossIter->second);
833 if (item != items_.end()) {
834 childrenInRect_.push_back(item->second);
835 int32_t itemMainSpan = GetItemSpan(item->second, useScrollable_ != SCROLLABLE::HORIZONTAL);
836 int32_t itemCrossSpan = GetItemSpan(item->second, useScrollable_ == SCROLLABLE::HORIZONTAL);
837 SetChildPosition(item->second, main, cross, itemMainSpan, itemCrossSpan);
838 }
839 }
840 }
841 if (main >= startIndex_) {
842 drawLength += GetSize(gridCells_.at(main).at(0)) + *mainGap_;
843 }
844 if (GreatOrEqual(drawLength, *mainSize_)) {
845 break;
846 }
847 }
848 SetLayoutSize(GetLayoutParam().Constrain(Size(colSize_, rowSize_)));
849 endIndex_ = main;
850 MarkNeedPredictLayout();
851 CalculateWholeSize(drawLength);
852
853 int32_t firstIndex = GetIndexByPosition(0);
854 if (lastFirstIndex_ != firstIndex) {
855 if (!animatorJumpFlag_) {
856 OnScrolled(firstIndex);
857 }
858 lastFirstIndex_ = firstIndex;
859 }
860 animatorJumpFlag_ = false;
861 }
862
DealCache(int32_t start,int32_t end)863 void RenderGridScroll::DealCache(int32_t start, int32_t end)
864 {
865 if (loadingIndex_ != -1) {
866 return;
867 }
868
869 std::set<int32_t> deleteItem;
870 for (const auto& item : inCache_) {
871 if (item < start - cacheCount_ || item > end + cacheCount_) {
872 deleteItem.insert(item);
873 }
874 }
875
876 for (const auto& item : deleteItem) {
877 DeleteItems(item, false);
878 }
879
880 for (int32_t i = 1; i <= cacheCount_; i++) {
881 if (inCache_.count(i + end) == 0) {
882 loadingIndex_ = i + end;
883 break;
884 }
885
886 if (start >= i && inCache_.count(start - i) == 0) {
887 loadingIndex_ = start - i;
888 break;
889 }
890 }
891 }
892
DeleteItems(int32_t index,bool isTail)893 void RenderGridScroll::DeleteItems(int32_t index, bool isTail)
894 {
895 if (!deleteChildByIndex_) {
896 return;
897 }
898
899 auto iter = gridMatrix_.find(index);
900 if (iter == gridMatrix_.end()) {
901 return;
902 }
903 for (const auto& item : iter->second) {
904 deleteChildByIndex_(item.second);
905 RemoveChildByIndex(item.second);
906 }
907
908 inCache_.erase(index);
909 }
910
ClearLayout(bool needReservedPlace)911 void RenderGridScroll::ClearLayout(bool needReservedPlace)
912 {
913 if (needReservedPlace) {
914 RecordLocation();
915 } else {
916 currentOffset_ = 0.0;
917 }
918 showItem_.clear();
919 childrenInRect_.clear();
920 inCache_.clear();
921
922 updateFlag_ = true;
923 reachHead_ = false;
924 reachTail_ = false;
925 startMainPos_ = 0.0;
926 firstItemOffset_ = 0.0;
927 startIndex_ = 0;
928 endIndex_ = -1;
929 mainScrollExtent_ = 0.0;
930 lastOffset_ = 0.0;
931 estimateHeight_ = 0.0;
932
933 colCount_ = 0;
934 rowCount_ = 0;
935
936 gridMatrix_.clear();
937 gridCells_.clear();
938 }
939
RecordLocation()940 void RenderGridScroll::RecordLocation()
941 {
942 double positionMain = 0.0;
943
944 for (int32_t i = 0; i < startIndex_; ++i) {
945 if (i < static_cast<int32_t>(gridCells_.size())) {
946 positionMain += GetSize(gridCells_.at(i).at(0));
947 }
948 }
949 positionMain += (startIndex_) * (*mainGap_);
950 currentOffset_ += -positionMain - firstItemOffset_;
951 }
952
ClearItems()953 void RenderGridScroll::ClearItems()
954 {
955 decltype(items_) items(std::move(items_));
956 for (const auto& item : items) {
957 if (item.first < startRankItemIndex_) {
958 deleteChildByIndex_(item.first);
959 }
960 RemoveChildByIndex(item.first);
961 }
962 loadingIndex_ = -1;
963 }
964
GetMinAndMaxIndex(int32_t & min,int32_t & max)965 void RenderGridScroll::GetMinAndMaxIndex(int32_t& min, int32_t& max)
966 {
967 for (const auto& item : items_) {
968 if (item.first < min) {
969 min = item.first;
970 }
971 if (item.first > max) {
972 max = item.first;
973 }
974 }
975 }
976
GetItemMainIndex(int32_t index)977 int32_t RenderGridScroll::GetItemMainIndex(int32_t index)
978 {
979 for (const auto& main : gridMatrix_) {
980 for (const auto& cross : main.second) {
981 if (cross.second == index) {
982 return main.first;
983 }
984 }
985 }
986 return -1;
987 }
988
GetStartingItem(int32_t first)989 int32_t RenderGridScroll::GetStartingItem(int32_t first)
990 {
991 int32_t firstIndex = 0;
992 int32_t index = first;
993 int32_t itemMain = -1;
994 int32_t itemCross = -1;
995 int32_t itemMainSpan = -1;
996 int32_t itemCrossSpan = -1;
997 while (index > 0) {
998 if (getChildSpanByIndex_(
999 index, useScrollable_ == SCROLLABLE::HORIZONTAL, itemMain, itemCross, itemMainSpan, itemCrossSpan)) {
1000 LOGD("index %d %d, %d, %d, %d", index, itemMain, itemCross, itemMainSpan, itemCrossSpan);
1001 if (itemCross == 0) {
1002 firstIndex = index;
1003 break;
1004 }
1005 }
1006
1007 index--;
1008 }
1009 return firstIndex;
1010 }
1011
OnDataSourceUpdated(int32_t index)1012 void RenderGridScroll::OnDataSourceUpdated(int32_t index)
1013 {
1014 if (items_.empty() && updateFlag_) {
1015 return;
1016 }
1017 ACE_SCOPED_TRACE("OnDataSourceUpdated %d", index);
1018 auto items = gridMatrix_.find(startIndex_);
1019 if (items != gridMatrix_.end() && !items->second.empty()) {
1020 currentItemIndex_ = items->second.begin()->second;
1021 }
1022 startRankItemIndex_ = GetStartingItem(currentItemIndex_);
1023 auto offset = firstItemOffset_;
1024 ClearItems();
1025 ClearLayout(false);
1026
1027 currentOffset_ = -offset;
1028 MarkNeedLayout();
1029 }
1030
CalculateWholeSize(double drawLength)1031 void RenderGridScroll::CalculateWholeSize(double drawLength)
1032 {
1033 if (gridMatrix_.empty() || gridCells_.empty()) {
1034 return;
1035 }
1036 if (totalCountFlag_) {
1037 int currentItemCount = 0;
1038 auto lastRow = gridMatrix_.rbegin()->second;
1039 if (!lastRow.empty()) {
1040 currentItemCount = lastRow.rbegin()->second;
1041 }
1042 if (currentItemCount != 0) {
1043 mainScrollExtent_ = 0.0;
1044 // calculate the whole size
1045 mainScrollExtent_ = static_cast<double>(totalCount_) / static_cast<double>(currentItemCount) * drawLength;
1046 estimateHeight_ = mainScrollExtent_;
1047 totalCountFlag_ = false;
1048 }
1049 }
1050 scrollBarExtent_ = 0.0;
1051 startMainPos_ = 0.0;
1052 for (int index = 0; index < *mainCount_; index++) {
1053 if (index == startIndex_) {
1054 // get the start position in grid
1055 startMainPos_ = scrollBarExtent_;
1056 }
1057 if (gridCells_.find(index) == gridCells_.end()) {
1058 continue;
1059 }
1060 scrollBarExtent_ += GetSize(gridCells_.at(index).at(0)) + *mainGap_;
1061 }
1062 bool isScrollable = false;
1063 if (estimateHeight_ > GetSize(GetLayoutSize()) || scrollBarExtent_ > GetSize(GetLayoutSize())) {
1064 isScrollable = true;
1065 }
1066 if (scrollBar_) {
1067 scrollBar_->SetScrollable(isScrollable);
1068 }
1069 if (!isScrollable) {
1070 currentOffset_ = 0.0;
1071 }
1072 }
1073
ScrollPage(bool reverse,bool smooth)1074 void RenderGridScroll::ScrollPage(bool reverse, bool smooth)
1075 {
1076 if (!reverse) {
1077 UpdateScrollPosition(-GetSize(GetLayoutSize()), SCROLL_FROM_JUMP);
1078 } else {
1079 UpdateScrollPosition(GetSize(GetLayoutSize()), SCROLL_FROM_JUMP);
1080 }
1081 }
1082
GetEstimatedHeight()1083 double RenderGridScroll::GetEstimatedHeight()
1084 {
1085 if (reachTail_) {
1086 // reach the end of grid, update the total scroll bar length
1087 estimateHeight_ = scrollBarExtent_;
1088 } else {
1089 estimateHeight_ = std::max(estimateHeight_, scrollBarExtent_);
1090 }
1091 return estimateHeight_;
1092 }
1093
InitScrollBar(const RefPtr<Component> & component)1094 void RenderGridScroll::InitScrollBar(const RefPtr<Component>& component)
1095 {
1096 if (scrollBar_) {
1097 scrollBar_->Reset();
1098 return;
1099 }
1100 const RefPtr<GridLayoutComponent> grid = AceType::DynamicCast<GridLayoutComponent>(component);
1101 if (!grid) {
1102 LOGE("RenderGridLayout update failed.");
1103 EventReport::SendRenderException(RenderExcepType::RENDER_COMPONENT_ERR);
1104 return;
1105 }
1106 const RefPtr<ScrollBarTheme> theme = GetTheme<ScrollBarTheme>();
1107 if (!theme) {
1108 return;
1109 }
1110 RefPtr<GridScrollController> controller = AceType::MakeRefPtr<GridScrollController>();
1111 scrollBar_ = AceType::MakeRefPtr<ScrollBar>(grid->GetScrollBar(), theme->GetShapeMode());
1112 scrollBar_->SetScrollBarController(controller);
1113
1114 // set the scroll bar style
1115 scrollBar_->SetReservedHeight(theme->GetReservedHeight());
1116 scrollBar_->SetMinHeight(theme->GetMinHeight());
1117 scrollBar_->SetMinDynamicHeight(theme->GetMinDynamicHeight());
1118 auto& scrollBarColor = grid->GetScrollBarColor();
1119 if (!scrollBarColor.empty()) {
1120 scrollBarColor_ = Color::FromString(scrollBarColor);
1121 } else {
1122 scrollBarColor_ = theme->GetForegroundColor();
1123 }
1124 scrollBar_->SetForegroundColor(scrollBarColor_);
1125 scrollBar_->SetBackgroundColor(theme->GetBackgroundColor());
1126 scrollBar_->SetPadding(theme->GetPadding());
1127 scrollBar_->SetScrollable(true);
1128 if (!grid->GetScrollBarWidth().empty()) {
1129 const auto& width = StringUtils::StringToDimension(grid->GetScrollBarWidth());
1130 scrollBar_->SetInactiveWidth(width);
1131 scrollBar_->SetNormalWidth(width);
1132 scrollBar_->SetActiveWidth(width);
1133 scrollBar_->SetTouchWidth(width);
1134 } else {
1135 scrollBar_->SetInactiveWidth(theme->GetNormalWidth());
1136 scrollBar_->SetNormalWidth(theme->GetNormalWidth());
1137 scrollBar_->SetActiveWidth(theme->GetActiveWidth());
1138 scrollBar_->SetTouchWidth(theme->GetTouchWidth());
1139 }
1140 scrollBar_->InitScrollBar(AceType::WeakClaim(this), GetContext());
1141 SetScrollBarCallback();
1142 }
1143
InitScrollBarProxy()1144 void RenderGridScroll::InitScrollBarProxy()
1145 {
1146 if (!scrollBarProxy_) {
1147 return;
1148 }
1149 auto&& scrollCallback = [weakScroll = AceType::WeakClaim(this)](double value, int32_t source) {
1150 auto grid = weakScroll.Upgrade();
1151 if (!grid) {
1152 LOGE("render grid is released");
1153 return false;
1154 }
1155 return grid->UpdateScrollPosition(value, source);
1156 };
1157 scrollBarProxy_->UnRegisterScrollableNode(AceType::WeakClaim(this));
1158 scrollBarProxy_->RegisterScrollableNode({ AceType::WeakClaim(this), scrollCallback });
1159 }
1160
SetScrollBarCallback()1161 void RenderGridScroll::SetScrollBarCallback()
1162 {
1163 if (!scrollBar_ || !scrollBar_->NeedScrollBar()) {
1164 return;
1165 }
1166 auto&& barEndCallback = [weakGrid = AceType::WeakClaim(this)](int32_t value) {
1167 auto grid = weakGrid.Upgrade();
1168 if (!grid) {
1169 LOGE("render grid is released");
1170 return;
1171 }
1172 grid->scrollBarOpacity_ = value;
1173 grid->MarkNeedRender();
1174 };
1175 auto&& scrollEndCallback = [weakGrid = AceType::WeakClaim(this)]() {
1176 auto grid = weakGrid.Upgrade();
1177 if (!grid) {
1178 LOGE("render grid is released");
1179 return;
1180 }
1181 LOGD("trigger scroll end callback");
1182 };
1183 auto&& scrollCallback = [weakScroll = AceType::WeakClaim(this)](double value, int32_t source) {
1184 auto grid = weakScroll.Upgrade();
1185 if (!grid) {
1186 LOGE("render grid is released");
1187 return false;
1188 }
1189 return grid->UpdateScrollPosition(value, source);
1190 };
1191 scrollBar_->SetCallBack(scrollCallback, barEndCallback, scrollEndCallback);
1192 }
1193
ScrollToIndex(int32_t index,int32_t source)1194 void RenderGridScroll::ScrollToIndex(int32_t index, int32_t source)
1195 {
1196 if (useScrollable_ == SCROLLABLE::NO_SCROLL || index < 0) {
1197 LOGW("Not supported.");
1198 return;
1199 }
1200 auto inputIdx = GetItemMainIndex(index);
1201 if (inputIdx < endIndex_ && inputIdx > startIndex_) {
1202 LOGI("already in map, not need to jump.");
1203 return;
1204 }
1205 auto context = context_.Upgrade();
1206 if (!context) {
1207 LOGE("context is null");
1208 return;
1209 }
1210 // Build items
1211 if (index < startShowItemIndex_ || index > endShowItemIndex_) {
1212 // do not need layout transition
1213 auto option = context->GetExplicitAnimationOption();
1214 context->SaveExplicitAnimationOption(AnimationOption());
1215 firstLineToBottom_.emplace(index > endShowItemIndex_);
1216 if (scrollable_ && !scrollable_->IsStopped()) {
1217 scrollable_->StopScrollable();
1218 }
1219 startRankItemIndex_ = GetStartingItem(index);
1220 ClearItems();
1221 ClearLayout(false);
1222 currentOffset_ = 0;
1223 MarkNeedLayout();
1224 context->SaveExplicitAnimationOption(option);
1225 return;
1226 }
1227 // Calculate scroll length
1228 double scrollLength = 0.0;
1229 inputIdx = GetItemMainIndex(index);
1230 if (inputIdx >= endIndex_) {
1231 double mainLen = -firstItemOffset_;
1232 for (int32_t i = startIndex_; i <= endIndex_; ++i) {
1233 if (gridCells_.find(i) != gridCells_.end()) {
1234 mainLen += GetSize(gridCells_.at(i).at(0)) + *mainGap_;
1235 }
1236 }
1237 scrollLength += (mainLen - GetSize(GetLayoutSize()));
1238 for (int32_t i = endIndex_; i < inputIdx; ++i) {
1239 SupplyItems(i, index);
1240 if (i != inputIdx) {
1241 scrollLength += GetSize(gridCells_.at(i).at(0)) + *mainGap_;
1242 }
1243 }
1244 scrollLength = -scrollLength;
1245 } else if (inputIdx <= startIndex_) {
1246 scrollLength += firstItemOffset_;
1247 for (int32_t i = startIndex_; i >= inputIdx; --i) {
1248 SupplyItems(i, index, i != inputIdx);
1249 if (i != inputIdx) {
1250 scrollLength += GetSize(gridCells_.at(i).at(0)) + *mainGap_;
1251 }
1252 }
1253 } else {
1254 return;
1255 }
1256 // Start animation
1257 if (!animator_->IsStopped()) {
1258 animator_->Stop();
1259 }
1260 animator_->ClearInterpolators();
1261 animateDelta_ = 0.0;
1262 auto animation = AceType::MakeRefPtr<CurveAnimation<double>>(0, scrollLength, Curves::LINEAR);
1263 animation->AddListener([weakScroll = AceType::WeakClaim(this)](double value) {
1264 auto scroll = weakScroll.Upgrade();
1265 if (scroll) {
1266 scroll->animatorJumpFlag_ = true;
1267 scroll->DoJump(value, SCROLL_FROM_JUMP);
1268 }
1269 });
1270 animator_->AddInterpolator(animation);
1271 animator_->ApplyOption(context->GetExplicitAnimationOption());
1272 animator_->ClearStopListeners();
1273 animator_->AddStopListener([weakScroll = AceType::WeakClaim(this)]() {
1274 auto scroll = weakScroll.Upgrade();
1275 if (scroll) {
1276 scroll->animateDelta_ = 0.0;
1277 }
1278 });
1279 animator_->Play();
1280 }
1281
AnimateTo(const Dimension & position,float duration,const RefPtr<Curve> & curve)1282 bool RenderGridScroll::AnimateTo(const Dimension& position, float duration, const RefPtr<Curve>& curve)
1283 {
1284 if (!animator_->IsStopped()) {
1285 animator_->Stop();
1286 }
1287 animator_->ClearInterpolators();
1288 animateDelta_ = 0.0;
1289 auto animation = AceType::MakeRefPtr<CurveAnimation<double>>(GetCurrentOffset(), NormalizeToPx(position), curve);
1290 animation->AddListener([weakScroll = AceType::WeakClaim(this)](double value) {
1291 auto scroll = weakScroll.Upgrade();
1292 if (scroll) {
1293 scroll->DoJump(value, SCROLL_FROM_JUMP);
1294 }
1295 });
1296 animator_->AddInterpolator(animation);
1297 animator_->SetDuration(duration);
1298 animator_->ClearStopListeners();
1299 animator_->AddStopListener([weakScroll = AceType::WeakClaim(this)]() {
1300 auto scroll = weakScroll.Upgrade();
1301 if (scroll) {
1302 scroll->animateDelta_ = 0.0;
1303 }
1304 });
1305 animator_->Play();
1306 return true;
1307 }
1308
CurrentOffset()1309 Offset RenderGridScroll::CurrentOffset()
1310 {
1311 auto ctx = GetContext().Upgrade();
1312 if (!ctx) {
1313 return useScrollable_ == SCROLLABLE::HORIZONTAL ? Offset(GetCurrentOffset(), 0.0)
1314 : Offset(0.0, GetCurrentOffset());
1315 }
1316 auto mainOffset = ctx->ConvertPxToVp(Dimension(GetCurrentOffset(), DimensionUnit::PX));
1317 Offset currentOffset = useScrollable_ == SCROLLABLE::HORIZONTAL ? Offset(mainOffset, 0.0) : Offset(0.0, mainOffset);
1318 return currentOffset;
1319 }
1320
GetItemPropsByIndex(int32_t itemIndex,int32_t & itemMain,int32_t & itemCross,int32_t & itemMainSpan,int32_t & itemCrossSpan)1321 bool RenderGridScroll::GetItemPropsByIndex(
1322 int32_t itemIndex, int32_t& itemMain, int32_t& itemCross, int32_t& itemMainSpan, int32_t& itemCrossSpan)
1323 {
1324 auto item = items_.find(itemIndex);
1325 if (item != items_.end()) {
1326 itemMain = GetItemMainIndex(item->second, true);
1327 itemCross = GetItemMainIndex(item->second, false);
1328 itemMainSpan = GetItemSpan(item->second, useScrollable_ != SCROLLABLE::HORIZONTAL);
1329 itemCrossSpan = GetItemSpan(item->second, useScrollable_ == SCROLLABLE::HORIZONTAL);
1330 } else {
1331 if (!getChildSpanByIndex_(itemIndex, useScrollable_ == SCROLLABLE::HORIZONTAL, itemMain, itemCross,
1332 itemMainSpan, itemCrossSpan)) {
1333 LOGW("Not valid.");
1334 return false;
1335 }
1336 }
1337 return true;
1338 }
1339
ScrollToEdge(OHOS::Ace::ScrollEdgeType edgeType,bool smooth)1340 void RenderGridScroll::ScrollToEdge(OHOS::Ace::ScrollEdgeType edgeType, bool smooth)
1341 {
1342 if (edgeType != ScrollEdgeType::SCROLL_TOP) {
1343 LOGW("Not supported yet");
1344 return;
1345 }
1346 if (items_.empty() && updateFlag_) {
1347 return;
1348 }
1349 if (scrollable_ && !scrollable_->IsStopped()) {
1350 scrollable_->StopScrollable();
1351 }
1352 currentItemIndex_ = 0;
1353 startRankItemIndex_ = GetStartingItem(currentItemIndex_);
1354 ClearItems();
1355 ClearLayout(false);
1356 MarkNeedLayout();
1357 }
1358
DoJump(double position,int32_t source)1359 void RenderGridScroll::DoJump(double position, int32_t source)
1360 {
1361 double delta = position - animateDelta_;
1362 UpdateScrollPosition(delta, source);
1363 animateDelta_ = position;
1364 }
1365
GetIndexByPosition(double position) const1366 int32_t RenderGridScroll::GetIndexByPosition(double position) const
1367 {
1368 int32_t index = 0;
1369 double startPosition = 0.0;
1370 double endPosition = 0.0;
1371
1372 for (const auto& item : childrenInRect_) {
1373 if (!item || item->GetChildren().empty()) {
1374 continue;
1375 }
1376
1377 auto gridItem = AceType::DynamicCast<RenderGridLayoutItem>(item);
1378 if (!gridItem) {
1379 break;
1380 }
1381
1382 startPosition = item->GetPosition().GetY();
1383 endPosition = item->GetPosition().GetY() + item->GetLayoutSize().Height();
1384
1385 if ((position > startPosition && position < endPosition) || NearEqual(position, startPosition) ||
1386 startPosition > position) {
1387 return gridItem->GetIndex();
1388 }
1389 }
1390 return index;
1391 }
1392
OnScrolled(int32_t scrolled) const1393 void RenderGridScroll::OnScrolled(int32_t scrolled) const
1394 {
1395 if (scrolledEventFun_) {
1396 auto event = std::make_shared<GridEventInfo>(scrolled);
1397 if (event) {
1398 scrolledEventFun_(event);
1399 }
1400 }
1401 }
1402
OnPaintFinish()1403 void RenderGridScroll::OnPaintFinish()
1404 {
1405 RenderNode::OnPaintFinish();
1406 if (showItem_.empty()) {
1407 return;
1408 }
1409 auto currentStartItemCount = *showItem_.begin();
1410 auto currentEndItemCount = *showItem_.rbegin();
1411 if ((startShowItemIndex_ != currentStartItemCount) || (endShowItemIndex_ != currentEndItemCount)) {
1412 startShowItemIndex_ = currentStartItemCount;
1413 endShowItemIndex_ = currentEndItemCount;
1414 }
1415 }
1416
OnPredictLayout(int64_t deadline)1417 void RenderGridScroll::OnPredictLayout(int64_t deadline)
1418 {
1419 auto startTime = GetSysTimestamp(); // unit: ns
1420 auto context = context_.Upgrade();
1421 if (!context) {
1422 return;
1423 }
1424 if (!context->IsTransitionStop()) {
1425 LOGI("In page transition, skip predict.");
1426 return;
1427 }
1428 if (loadingIndex_ == -1) {
1429 DealCache(startIndex_, endIndex_);
1430 if (loadingIndex_ == -1) {
1431 if (startIndex_ == 0 && startRankItemIndex_ > 0) {
1432 LoadForward();
1433 MarkNeedPredictLayout();
1434 }
1435 return;
1436 }
1437 }
1438 ACE_SCOPED_TRACE("OnPredictLayout %d", loadingIndex_);
1439 if (gridMatrix_.find(loadingIndex_) != gridMatrix_.end() || Rank(loadingIndex_)) {
1440 auto iter = gridMatrix_.find(loadingIndex_);
1441 if (iter != gridMatrix_.end()) {
1442 for (const auto& item : iter->second) {
1443 if (items_.find(item.second) == items_.end()) {
1444 if (!buildChildByIndex_(item.second)) {
1445 break;
1446 }
1447 }
1448 // Stop predictLayout less than 3 milliseconds before the next vsync arrives.
1449 if (GetSysTimestamp() - startTime + TIMETHRESHOLD > deadline * MICROSEC_TO_NANOSEC) {
1450 MarkNeedPredictLayout();
1451 return;
1452 }
1453 }
1454 SupplyItems(loadingIndex_);
1455 }
1456 MarkNeedPredictLayout();
1457 } else {
1458 loadingIndex_ = -1;
1459 }
1460 }
1461
IsAxisScrollable(AxisDirection direction)1462 bool RenderGridScroll::IsAxisScrollable(AxisDirection direction)
1463 {
1464 return (((direction == AxisDirection::UP || direction == AxisDirection::LEFT) && !reachHead_) ||
1465 ((direction == AxisDirection::DOWN || direction == AxisDirection::RIGHT) && !reachTail_));
1466 }
1467
HandleAxisEvent(const AxisEvent & event)1468 void RenderGridScroll::HandleAxisEvent(const AxisEvent& event)
1469 {
1470 double degree = 0.0f;
1471 if (!NearZero(event.horizontalAxis)) {
1472 degree = event.horizontalAxis;
1473 } else if (!NearZero(event.verticalAxis)) {
1474 degree = event.verticalAxis;
1475 }
1476 double offset = SystemProperties::Vp2Px(DP_PER_LINE_DESKTOP * LINE_NUMBER_DESKTOP * degree / MOUSE_WHEEL_DEGREES);
1477 UpdateScrollPosition(-offset, SCROLL_FROM_ROTATE);
1478 }
1479
CheckAxisNode()1480 WeakPtr<RenderNode> RenderGridScroll::CheckAxisNode()
1481 {
1482 return AceType::WeakClaim<RenderNode>(this);
1483 }
1484
1485 } // namespace OHOS::Ace::V2