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