• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-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_ng/syntax/repeat_virtual_scroll_node.h"
17 
18 #include <cstdint>
19 #include <functional>
20 #include <utility>
21 
22 #include "base/log/ace_trace.h"
23 #include "base/log/log_wrapper.h"
24 #include "core/components_ng/base/frame_node.h"
25 #include "core/pipeline/base/element_register.h"
26 #include "core/pipeline_ng/pipeline_context.h"
27 
28 namespace OHOS::Ace::NG {
29 
30 // REPEAT
GetOrCreateRepeatNode(int32_t nodeId,uint32_t totalCount,const std::map<std::string,std::pair<bool,uint32_t>> & templateCachedCountMap,const std::function<void (uint32_t)> & onCreateNode,const std::function<void (const std::string &,uint32_t)> & onUpdateNode,const std::function<std::list<std::string> (uint32_t,uint32_t)> & onGetKeys4Range,const std::function<std::list<std::string> (uint32_t,uint32_t)> & onGetTypes4Range,const std::function<void (int32_t,int32_t)> & onSetActiveRange)31 RefPtr<RepeatVirtualScrollNode> RepeatVirtualScrollNode::GetOrCreateRepeatNode(int32_t nodeId, uint32_t totalCount,
32     const std::map<std::string, std::pair<bool, uint32_t>>& templateCachedCountMap,
33     const std::function<void(uint32_t)>& onCreateNode,
34     const std::function<void(const std::string&, uint32_t)>& onUpdateNode,
35     const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetKeys4Range,
36     const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetTypes4Range,
37     const std::function<void(int32_t, int32_t)>& onSetActiveRange)
38 {
39     auto node = ElementRegister::GetInstance()->GetSpecificItemById<RepeatVirtualScrollNode>(nodeId);
40     if (node) {
41         TAG_LOGD(AceLogTag::ACE_REPEAT, "Found RepeatVirtualScrollNode");
42         node->UpdateTotalCount(totalCount);
43         return node;
44     }
45     node = MakeRefPtr<RepeatVirtualScrollNode>(
46         nodeId, totalCount, templateCachedCountMap, onCreateNode, onUpdateNode, onGetKeys4Range, onGetTypes4Range,
47         onSetActiveRange);
48 
49     ElementRegister::GetInstance()->AddUINode(node);
50     return node;
51 }
52 
RepeatVirtualScrollNode(int32_t nodeId,int32_t totalCount,const std::map<std::string,std::pair<bool,uint32_t>> & templateCachedCountMap,const std::function<void (uint32_t)> & onCreateNode,const std::function<void (const std::string &,uint32_t)> & onUpdateNode,const std::function<std::list<std::string> (uint32_t,uint32_t)> & onGetKeys4Range,const std::function<std::list<std::string> (uint32_t,uint32_t)> & onGetTypes4Range,const std::function<void (int32_t,int32_t)> & onSetActiveRange)53 RepeatVirtualScrollNode::RepeatVirtualScrollNode(int32_t nodeId, int32_t totalCount,
54     const std::map<std::string, std::pair<bool, uint32_t>>& templateCachedCountMap,
55     const std::function<void(uint32_t)>& onCreateNode,
56     const std::function<void(const std::string&, uint32_t)>& onUpdateNode,
57     const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetKeys4Range,
58     const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetTypes4Range,
59     const std::function<void(int32_t, int32_t)>& onSetActiveRange)
60     : ForEachBaseNode(V2::JS_REPEAT_ETS_TAG, nodeId), totalCount_(totalCount),
61       caches_(templateCachedCountMap, onCreateNode, onUpdateNode, onGetKeys4Range, onGetTypes4Range),
62       onSetActiveRange_(onSetActiveRange),
63       postUpdateTaskHasBeenScheduled_(false)
64 {
65 }
66 
UpdateTotalCount(uint32_t totalCount)67 void RepeatVirtualScrollNode::UpdateTotalCount(uint32_t totalCount)
68 {
69     TAG_LOGD(AceLogTag::ACE_REPEAT, "UpdateTotalCount: %{public}d", totalCount);
70     totalCount_ = totalCount;
71 }
72 
73 
DoSetActiveChildRange(int32_t start,int32_t end,int32_t cacheStart,int32_t cacheEnd)74 void RepeatVirtualScrollNode::DoSetActiveChildRange(
75     int32_t start, int32_t end,
76     int32_t cacheStart, int32_t cacheEnd)
77 {
78     TAG_LOGD(AceLogTag::ACE_REPEAT,
79         "DoSetActiveChildRange: Repeat(nodeId): %{public}d: start: %{public}d - end: %{public}d; cacheStart: "
80         "%{public}d, cacheEnd: %{public}d: ==> keep in L1: %{public}d - %{public}d,",
81         GetId(), start, end, cacheStart, cacheEnd, start - cacheStart, end + cacheEnd);
82 
83     ACE_SCOPED_TRACE("Repeat.DoSetActiveChildRange start [%d] - end [%d; cacheStart: [%d], cacheEnd: [%d]",
84         start, end, cacheStart, cacheEnd);
85 
86     // get normalized active range (with positive indices only)
87     const int32_t signed_totalCount_ = static_cast<int32_t>(totalCount_);
88     int32_t nStart = start - cacheStart;
89     int32_t nEnd = end + cacheEnd;
90     if (signed_totalCount_ > 0) {
91         nStart = (nStart + signed_totalCount_) % signed_totalCount_;
92         nEnd = (nEnd + signed_totalCount_) % signed_totalCount_;
93     }
94     nStart = std::max(nStart, 0);
95     nEnd = std::max(nEnd, 0);
96 
97     // memorize active range
98     caches_.SetLastActiveRange(static_cast<uint32_t>(nStart), static_cast<uint32_t>(nEnd));
99 
100     // notify TS side
101     onSetActiveRange_(nStart, nEnd);
102 
103     bool needSync = caches_.RebuildL1([start, end, cacheStart, cacheEnd, this](
104         int32_t index, const RefPtr<UINode>& node) -> bool {
105         if (node == nullptr) {
106             return false;
107         }
108         auto frameNode = AceType::DynamicCast<FrameNode>(node->GetFrameChildByIndex(0, true));
109         if (!frameNode) {
110             return false;
111         }
112         if (CheckNode4IndexInL1(index, start, end, cacheStart, cacheEnd, frameNode)) {
113             // keep in Repeat L1
114             TAG_LOGD(AceLogTag::ACE_REPEAT, "...in visible, index %{public}d -> nodeId %{public}d: keep in Repeat L1",
115                 static_cast<int32_t>(index), frameNode->GetId());
116             return true;
117         }
118         TAG_LOGD(AceLogTag::ACE_REPEAT, "...out of visible, index %{public}d -> nodeId %{public}d: SetActive(false), "
119             "detach, move to spare items L2", index, frameNode->GetId());
120 
121         // move active node into L2 cached. check transition flag.
122         if (node->OnRemoveFromParent(true)) {
123             // OnRemoveFromParent returns true means the child can be removed from tree immediately.
124             RemoveDisappearingChild(node);
125         } else {
126             AddDisappearingChild(node);
127         }
128         return false;
129     });
130     if (needSync) {
131         UINode::MarkNeedSyncRenderTree(false);
132         children_.clear();
133         PostIdleTask();
134     }
135 }
136 
CheckNode4IndexInL1(int32_t index,int32_t start,int32_t end,int32_t cacheStart,int32_t cacheEnd,RefPtr<FrameNode> & frameNode)137 bool RepeatVirtualScrollNode::CheckNode4IndexInL1(int32_t index, int32_t start, int32_t end,
138     int32_t cacheStart, int32_t cacheEnd, RefPtr<FrameNode>& frameNode)
139 {
140     if (((start <= index) && (index <= end)) || ((end < start) && (index <= end || start <= index))) {
141         TAG_LOGD(AceLogTag::ACE_REPEAT, "...in range: index %{public}d -> nodeId %{public}d: SetActive(true)",
142             index, static_cast<int32_t>(frameNode->GetId()));
143         frameNode->SetActive(true);
144     } else {
145         TAG_LOGD(AceLogTag::ACE_REPEAT, "...out of range: index %{public}d -> nodeId %{public}d: SetActive(false)",
146             index, frameNode->GetId());
147         frameNode->SetActive(false);
148     }
149 
150     auto totalCount = static_cast<int32_t>(totalCount_);
151     if ((start - cacheStart <= index) && (index <= end + cacheEnd)) {
152         return true;
153     }
154     if (isLoop_) {
155         if (((end < start) && (start - cacheStart <= index || index <= end + cacheEnd)) ||
156             ((start - cacheStart < 0) && (index >= start - cacheStart + totalCount)) ||
157             ((end + cacheEnd >= totalCount) && (index <= end + cacheEnd - totalCount))) {
158             return true;
159         }
160     }
161     return false;
162 }
163 
DropFromL1(const std::string & key)164 void RepeatVirtualScrollNode::DropFromL1(const std::string& key)
165 {
166     RefPtr<UINode> node = caches_.DropFromL1(key);
167     if (node == nullptr) {
168         return;
169     }
170 
171     auto frameNode = AceType::DynamicCast<FrameNode>(node->GetFrameChildByIndex(0, true));
172     if (!frameNode) {
173         frameNode->SetActive(false);
174     }
175     // move active node into L2 cached.
176     // check transition flag.
177     if (node->OnRemoveFromParent(true)) {
178         // OnRemoveFromParent returns true means the child can be removed from tree immediately.
179         RemoveDisappearingChild(node);
180     } else {
181         // else move child into disappearing children, skip syncing render tree
182         AddDisappearingChild(node);
183     }
184 
185     UINode::MarkNeedSyncRenderTree(false);
186     children_.clear();
187     // re-assemble children_
188     PostIdleTask();
189 }
190 
DoSetActiveChildRange(const std::set<int32_t> & activeItems,const std::set<int32_t> & cachedItems,int32_t baseIndex)191 void RepeatVirtualScrollNode::DoSetActiveChildRange(
192     const std::set<int32_t>& activeItems, const std::set<int32_t>& cachedItems, int32_t baseIndex)
193 {
194     // Notify TS side. Verify line below when DoSetActiveChildRange() will start to be used.
195     // Call onSetActiveRange_ here;
196 
197     bool needSync =
198         caches_.RebuildL1([&activeItems, &cachedItems, baseIndex, this](int32_t index, RefPtr<UINode> node) -> bool {
199             if (node == nullptr) {
200                 return false;
201             }
202             auto frameNode = AceType::DynamicCast<FrameNode>(node->GetFrameChildByIndex(0, true));
203             if (!frameNode) {
204                 return false;
205             }
206             if (activeItems.find(index + baseIndex) != activeItems.end()) {
207                 frameNode->SetActive(true);
208                 return true;
209             } else {
210                 frameNode->SetActive(false);
211             }
212             if (cachedItems.find(index + baseIndex) != cachedItems.end()) {
213                 return true;
214             }
215             if (node->OnRemoveFromParent(true)) {
216                 RemoveDisappearingChild(node);
217             } else {
218                 AddDisappearingChild(node);
219             }
220             return false;
221         });
222     if (needSync) {
223         UINode::MarkNeedSyncRenderTree(false);
224         children_.clear();
225         PostIdleTask();
226     }
227 }
228 
UpdateRenderState(bool visibleItemsChanged)229 void RepeatVirtualScrollNode::UpdateRenderState(bool visibleItemsChanged)
230 {
231     TAG_LOGD(AceLogTag::ACE_REPEAT,
232         "UpdateRenderState triggered by Repeat rerender: nodeId: %{public}d, visibleItemsChanged: %{public}d",
233         static_cast<int32_t>(GetId()), visibleItemsChanged);
234 
235     if (visibleItemsChanged) {
236         // empty the cache index -> key
237         // C++ will need to ask all new keys from JS side
238         caches_.InvalidateKeyAndTTypeCaches();
239         children_.clear();
240 
241         if (auto frameNode = GetParentFrameNode()) {
242             frameNode->ChildrenUpdatedFrom(0);
243         }
244     } else {
245         auto lastIndexInActiveRange = caches_.GetLastActiveRange().second;
246         TAG_LOGD(AceLogTag::ACE_REPEAT, "lastIndexInActiveRange:%{public}d", lastIndexInActiveRange);
247 
248         if (auto frameNode = GetParentFrameNode()) {
249             frameNode->ChildrenUpdatedFrom(lastIndexInActiveRange + 1);
250         }
251     }
252 
253     MarkNeedSyncRenderTree(true);
254     MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT | PROPERTY_UPDATE_BY_CHILD_REQUEST);
255 }
256 
257 // STATE_MGMT_NOTE: added
258 // index N-th item
259 // needBuild: true - if found in cache, then return, if not in cache then return newly build
260 //            false: - if found in cache, then return, if not found in cache then return nullptr
261 // isCache: true indicates prebuild item (only used by List/Grid/Waterflow, this item should go to L2 cache,
262 //          do not add to the tree,
263 //          isCache==false this item is for display or near display area
264 // addToRenderTree: true  - set it to active state, call SetActive
GetFrameChildByIndex(uint32_t index,bool needBuild,bool isCache,bool addToRenderTree)265 RefPtr<UINode> RepeatVirtualScrollNode::GetFrameChildByIndex(
266     uint32_t index, bool needBuild, bool isCache, bool addToRenderTree)
267 {
268     TAG_LOGD(AceLogTag::ACE_REPEAT, "index:%{public}d", static_cast<int32_t>(index));
269 
270     ACE_SCOPED_TRACE("Repeat.GetFrameChildByIndex index[%d], needBuild[%d] isCache[%d] "
271                      "addToRenderTree[%d]",
272         index, static_cast<int32_t>(needBuild), static_cast<int32_t>(isCache), static_cast<int32_t>(addToRenderTree));
273 
274     // whether child is reused or created
275     bool isChildReused = true;
276 
277     const auto& key = caches_.GetKey4Index(index, true);
278     if (!key) {
279         TAG_LOGE(AceLogTag::ACE_REPEAT, "fail to get key for %{public}d", index);
280         return nullptr;
281     }
282 
283     TAG_LOGD(AceLogTag::ACE_REPEAT,
284         "nodeId: %{public}d: GetFrameChildByIndex(index: %{public}d, "
285         "key %{public}s, needBuild:  %{public}d, isCache: %{public}d, "
286         "addToRenderTree: %{public}d) ...", static_cast<int32_t>(GetId()),
287         static_cast<int32_t>(index), key->c_str(), static_cast<int32_t>(needBuild),
288         static_cast<int32_t>(isCache), static_cast<int32_t>(addToRenderTree));
289 
290     // search if index -> key -> Node exist
291     // will update the same key item if needs.
292     auto node4Index = GetFromCaches(index);
293     if (!node4Index && !needBuild) {
294         TAG_LOGD(AceLogTag::ACE_REPEAT,
295             "index %{public}d -> key '%{public}s' not in caches && needBuild==false, "
296             "GetFrameChildByIndex returns nullptr .",
297             static_cast<int32_t>(index), key->c_str());
298         return nullptr;
299     }
300 
301     // node4Index needs to be created or updated on JS side
302     if (!node4Index) {
303         TAG_LOGD(AceLogTag::ACE_REPEAT,
304             "index %{public}d -> key '%{public}s' not in caches && needBuild==true, calling "
305             "CreateOrUpdateFrameChild4Index ....",
306             static_cast<int32_t>(index), key->c_str());
307         // ask TS to update a Node, if possible
308         // if no suitable node, request to crete a new node
309         node4Index = caches_.UpdateFromL2(index);
310         if (!node4Index) {
311             node4Index = caches_.CreateNewNode(index);
312             isChildReused = false;
313         }
314 
315         if (!node4Index) {
316             TAG_LOGW(AceLogTag::ACE_REPEAT, "index %{public}d -> key '%{public}s' not in caches and failed to build.",
317                 static_cast<int32_t>(index), key->c_str());
318             return nullptr;
319         }
320     }
321 
322     TAG_LOGD(AceLogTag::ACE_REPEAT,
323         "index %{public}d  -> key '%{public}s', needBuild==true, node: %{public}s .",
324         static_cast<int32_t>(index), key->c_str(), caches_.DumpUINode(node4Index).c_str());
325 
326     if (isActive_) {
327         node4Index->SetJSViewActive(true);
328     }
329 
330     if (addToRenderTree && !isCache) {
331         TAG_LOGD(AceLogTag::ACE_REPEAT,
332             "index %{public}d isCache==false setActive and adding to L1 cache",
333             static_cast<int32_t>(index));
334         node4Index->SetActive(true);
335     }
336 
337     if (caches_.IsInL1Cache(key.value())) {
338         return node4Index->GetFrameChildByIndex(0, needBuild);
339     }
340 
341     // refresh the cached ttype and verify it hasn't changed
342     if (caches_.CheckTTypeChanged(index)) {
343         return GetFrameChildByIndex(index, needBuild, isCache, addToRenderTree);
344     }
345 
346     // if the item was in L2 cache, move item to L1 cache.
347     caches_.AddKeyToL1WithNodeUpdate(key.value(), index, isChildReused);
348 
349     if (node4Index->GetDepth() != GetDepth() + 1) {
350         node4Index->SetDepth(GetDepth() + 1);
351     }
352     // attach to repeat node and pass context to it.
353     node4Index->SetParent(WeakClaim(this));
354     if (IsOnMainTree()) {
355         node4Index->AttachToMainTree(false, GetContext());
356     }
357 
358     MarkNeedSyncRenderTree();
359     children_.clear();
360     // re-assemble children_
361     PostIdleTask();
362 
363     auto childNode = node4Index->GetFrameChildByIndex(0, needBuild);
364     if (childNode && onMoveEvent_) {
365         InitDragManager(AceType::DynamicCast<FrameNode>(childNode));
366     }
367 
368     if (childNode) {
369         TAG_LOGD(AceLogTag::ACE_REPEAT, "index %{public}d, its child is %{public}d, returning child.",
370             static_cast<int32_t>(index), static_cast<int32_t>(childNode->GetId()));
371     }
372 
373     return childNode;
374 }
375 
GetFrameNodeIndex(const RefPtr<FrameNode> & node,bool)376 int32_t RepeatVirtualScrollNode::GetFrameNodeIndex(const RefPtr<FrameNode>& node, bool /*isExpanded*/)
377 {
378     return caches_.GetFrameNodeIndex(node);
379 }
380 
GetChildren(bool) const381 const std::list<RefPtr<UINode>>& RepeatVirtualScrollNode::GetChildren(bool /*notDetach*/) const
382 {
383     if (!children_.empty()) {
384         TAG_LOGD(AceLogTag::ACE_REPEAT, "GetChildren just returns non-empty children_");
385         return children_;
386     }
387 
388     // can not modify l1_cache while iterating
389     // GetChildren is overloaded, can not change it to non-const
390     // need to order the child.
391     std::map<int32_t, RefPtr<UINode>> children;
392     caches_.ForEachL1IndexUINode(
393         [&children](int32_t index, const RefPtr<UINode>& node) -> void { children.emplace(index, node); });
394     for (const auto& [index, child] : children) {
395         const_cast<RepeatVirtualScrollNode*>(this)->RemoveDisappearingChild(child);
396         children_.emplace_back(child);
397     }
398     return children_;
399 }
400 
UpdateChildrenFreezeState(bool isFreeze)401 void RepeatVirtualScrollNode::UpdateChildrenFreezeState(bool isFreeze)
402 {
403     const auto& allChildren = caches_.GetAllNodes();
404     for (auto& child : allChildren) {
405         if (child.second.item) {
406             child.second.item->SetFreeze(isFreeze);
407         }
408     }
409 }
410 
RecycleItems(int32_t from,int32_t to)411 void RepeatVirtualScrollNode::RecycleItems(int32_t from, int32_t to)
412 {
413     TAG_LOGD(AceLogTag::ACE_REPEAT,
414         "Repeat(%{public}d: Layout prediction instructs to move Repeat items from index %{public}d up to smaller than "
415         "%{public}d to the reusable items cache",
416         static_cast<int32_t>(GetId()), from - startIndex_, to - startIndex_);
417 
418     offscreenItems_.from = from;
419     offscreenItems_.to = to;
420     for (auto i = from; i < to; i++) {
421         if (i >= startIndex_ && i < startIndex_ + static_cast<int32_t>(totalCount_)) {
422             caches_.RecycleItemsByIndex(i - startIndex_);
423         }
424     }
425 }
426 
SetNodeIndexOffset(int32_t start,int32_t)427 void RepeatVirtualScrollNode::SetNodeIndexOffset(int32_t start, int32_t /*count*/)
428 {
429     startIndex_ = start;
430 }
431 
FrameCount() const432 int32_t RepeatVirtualScrollNode::FrameCount() const
433 {
434     TAG_LOGD(AceLogTag::ACE_REPEAT, "FrameCount returns %{public}d",
435         static_cast<int32_t>(totalCount_));
436     return totalCount_;
437 }
438 
PostIdleTask()439 void RepeatVirtualScrollNode::PostIdleTask()
440 {
441     if (postUpdateTaskHasBeenScheduled_) {
442         return;
443     }
444     TAG_LOGD(AceLogTag::ACE_REPEAT, "Posting idle task");
445     postUpdateTaskHasBeenScheduled_ = true;
446     auto* context = GetContext();
447     CHECK_NULL_VOID(context);
448 
449     context->AddPredictTask([weak = AceType::WeakClaim(this)](int64_t /*deadline*/, bool /*canUseLongPredictTask*/) {
450         ACE_SCOPED_TRACE("Repeat.IdleTask");
451         auto node = weak.Upgrade();
452         CHECK_NULL_VOID(node);
453         node->postUpdateTaskHasBeenScheduled_ = false;
454         TAG_LOGD(AceLogTag::ACE_REPEAT, "idle task calls GetChildren");
455         node->GetChildren();
456         node->caches_.Purge();
457         TAG_LOGD(AceLogTag::ACE_REPEAT, " ============ after caches.purge ============= ");
458         TAG_LOGD(AceLogTag::ACE_REPEAT, "%{public}s", node->caches_.DumpL1().c_str());
459         TAG_LOGD(AceLogTag::ACE_REPEAT, "%{public}s", node->caches_.DumpL2().c_str());
460     });
461 }
462 
OnConfigurationUpdate(const ConfigurationChange & configurationChange)463 void RepeatVirtualScrollNode::OnConfigurationUpdate(const ConfigurationChange& configurationChange)
464 {
465     if ((configurationChange.colorModeUpdate || configurationChange.fontUpdate)) {
466         const auto& children = caches_.GetAllNodes();
467         for (const auto& [key, child] : children) {
468             if (child.item) {
469                 child.item->UpdateConfigurationUpdate(configurationChange);
470             }
471         }
472     }
473 }
474 
SetOnMove(std::function<void (int32_t,int32_t)> && onMove)475 void RepeatVirtualScrollNode::SetOnMove(std::function<void(int32_t, int32_t)>&& onMove)
476 {
477     // To do
478 }
479 
480 // FOREAch
MoveData(int32_t from,int32_t to)481 void RepeatVirtualScrollNode::MoveData(int32_t from, int32_t to) {}
482 
GetFrameNode(int32_t index)483 RefPtr<FrameNode> RepeatVirtualScrollNode::GetFrameNode(int32_t index)
484 {
485     return AceType::DynamicCast<FrameNode>(GetFrameChildByIndex(index, false, false));
486 }
487 
InitDragManager(const RefPtr<UINode> & child)488 void RepeatVirtualScrollNode::InitDragManager(const RefPtr<UINode>& child)
489 {
490     // To do
491 }
492 
InitAllChildrenDragManager(bool init)493 void RepeatVirtualScrollNode::InitAllChildrenDragManager(bool init)
494 {
495     // To do
496 }
497 
498 } // namespace OHOS::Ace::NG
499