1 /* 2 * Copyright (c) 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 #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_REPEAT_VIRTUAL_SCROLL_CACHES_H 17 #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_REPEAT_VIRTUAL_SCROLL_CACHES_H 18 19 #include <cstdint> 20 #include <functional> 21 #include <map> 22 #include <optional> 23 #include <set> 24 #include <string> 25 #include <unordered_map> 26 #include <unordered_set> 27 28 #include "base/memory/referenced.h" 29 #include "core/components_ng/base/ui_node.h" 30 31 namespace OHOS::Ace::NG { 32 33 class RepeatVirtualScrollCaches { 34 public: 35 struct CacheItem { 36 bool isValid = false; 37 RefPtr<UINode> item; 38 }; 39 40 RepeatVirtualScrollCaches(const std::map<std::string, std::pair<bool, uint32_t>>& cacheCountL24ttype, 41 const std::function<void(uint32_t)>& onCreateNode, 42 const std::function<void(const std::string&, uint32_t)>& onUpdateNode, 43 const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetKeys4Range, 44 const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetTypes4Range, 45 bool reusable = true); 46 47 /** scenario: 48 * Repeat gets updated due to data change. 49 * 1. TS calls RepeatVirtualScrollNode, 50 * then calls this function. 51 * 2. RepeatVirtualScrollNode requests layout to rebuild the UI 52 * 3. layout sends RepeatVirtualScrollNode::GetFrameChild calls 53 * 4. how to service GetFrameChild call: 54 * - first check for L1 keys (same template type) if any can be updated. 55 * These UINodes remain in the render tree. 56 * - if no L1 item, the look for L2 keys (same template type) 57 */ 58 void InvalidateKeyAndTTypeCaches(); 59 60 /** 61 * scenario: scroll, try to update an existing UINode 62 * 63 * find an key / UINode in L2 and update the UINode to 64 * render the data source item 'forIndex'. 65 */ 66 RefPtr<UINode> UpdateFromL2(uint32_t forIndex); 67 68 void UpdateSameKeyItem(const std::string& key, uint32_t index); 69 70 /** 71 * request TS to create a new node for given index / key/ 72 */ 73 RefPtr<UINode> CreateNewNode(uint32_t forIndex); 74 75 // iterate over L1 keys, not allowed to modify L1 76 void ForEachL1IndexUINode(std::map<int32_t, RefPtr<UINode>>& children); 77 78 /** 79 * for given index get key 80 * fetch from TS if not in cache (if allowFetch == true) 81 * return false if index out of range 82 */ 83 std::optional<std::string> GetKey4Index(uint32_t index, bool allowFetch); 84 85 /** 86 * iterate over all entries of L1 and call function for each entry 87 * if function returns true, entry is added to rebuild L1, otherwise it is moved to L2 88 * cbFunc return true, [index, key] pair stays in L1 (index remains unchanged) 89 * cbFunc returns false, enqueue key in L2 90 */ 91 bool RebuildL1(const std::function<bool(int32_t index, const RefPtr<UINode>& node)>& cbFunc); 92 93 /** 94 * dito with only key as cb function parameter 95 */ 96 bool RebuildL1WithKey(const std::function<bool(const std::string& key)>& cbFunc); 97 98 /* 99 drop L1 entry with given index from L1 100 keep it in L2 101 return the affected UINode 102 caller responsibility to detach this UINode from the UI tree! 103 */ 104 RefPtr<UINode> DropFromL1(const std::string& key); 105 106 int32_t GetFrameNodeIndex(const RefPtr<FrameNode>& frameNode) const; 107 108 /** 109 * scenario: in idle process , following GetChildren() 110 * 111 * enforce L2 cacheCount for each ttype 112 * by deleting UINodes, delete their entry from 113 * node4key4ttype_ and node4key_ 114 * any other processing steps needed before UINode 115 * tree can be deleted 116 */ 117 bool Purge(); 118 119 /** 120 * return the cached UINode for given index 121 * 122 * resolve index -> key -> UINode 123 * 124 */ 125 RefPtr<UINode> GetCachedNode4Index(uint32_t forIndex); 126 127 void AddKeyToL1(const std::string& key, bool shouldTriggerReuse = true); 128 129 void AddKeyToL1WithNodeUpdate(const std::string& key, uint32_t index, bool shouldTriggerRecycle); 130 131 void RemoveKeyFromL1(const std::string& key, bool shouldTriggerRecycle = true); 132 133 bool CheckTTypeChanged(uint32_t index); 134 IsInL1Cache(const std::string & key)135 bool IsInL1Cache(const std::string& key) const 136 { 137 return activeNodeKeysInL1_.find(key) != activeNodeKeysInL1_.end(); 138 } 139 GetAllNodes()140 const std::unordered_map<std::string, CacheItem>& GetAllNodes() const 141 { 142 return node4key_; 143 } 144 145 /** 146 * memorize last active range(s) 147 */ 148 void SetLastActiveRange(uint32_t from, uint32_t to); GetLastActiveRange()149 std::pair<uint32_t, uint32_t> GetLastActiveRange() { return lastActiveRanges_[0]; }; 150 151 // formatting internal structures to string for debug output 152 // and possibly in modified form for DFX in the future 153 std::string DumpL1() const; 154 std::string DumpL2() const; 155 std::string DumpKey4Index() const; 156 std::string DumpTType4Index() const; 157 std::string DumpUINode4Key4TType() const; 158 std::string DumpUINode4Key() const; 159 160 std::string DumpUINodeWithKey(const std::string& key) const; 161 std::string DumpUINode(const RefPtr<UINode>& node) const; 162 163 private: 164 /** 165 * intended scenario: scroll 166 * servicing GetFrameChild, search for key that can be updated. 167 * 168 * return a key whose UINode can be updated 169 * the key must not be in L1, i.e. activeNodeKeysInL1_ 170 * the given ttype must match the template type the UINode for this key 171 * has been rendered for (this info is available from node4key4ttype_) 172 */ 173 std::optional<std::string> GetL2KeyToUpdate(const std::optional<std::string>& ttype) const; 174 175 /** 176 * scenario: UI rebuild following key invalidation by TS side 177 * L1 includes keys that are no longer used, the linked UINodes 178 * should be updated. 179 * 180 * This function checks all L1 keys (of active UINodes) if the key 181 * can still be found from 182 * (previously updated following invalidation) key -> index map and 183 * 184 */ 185 std::optional<std::string> GetL1KeyToUpdate(const std::string& ttype) const; 186 187 /** 188 * scenario: UINode of fromKey has been updated to render data for 'forKey' 189 * the template type (ttype) remains unchanged 190 * update node4key4ttype_ and node4key_ entries to use new key point to same UINode 191 */ 192 RefPtr<UINode> UINodeHasBeenUpdated( 193 const std::string& ttype, const std::string& fromKey, const std::string& forKey); 194 195 /** scenario: keys cache has been updated 196 * 197 * find which keys in key -> UINode map are no longer used 198 * returned set entries are pairs: 199 * pair.first: is this key a L1 item, 200 * pair.second: key 201 */ 202 void FindUnusedKeys(std::set<std::pair<bool, std::string>>& result) const; 203 204 /** 205 * given key return the index position (reverse lookup) 206 * invalidated keys (after Repeat rerender/ data change) 207 * are keys for which no index exists anymore, 208 * method returns int max value for these. 209 * int max value causes that distance from active range is max 210 * these keys will be selected for update first. 211 */ 212 uint32_t GetIndex4Key(const std::string& key) const; 213 214 /** 215 * find UINode for given key, irrespective of ttype in ttype4index_ 216 */ 217 std::optional<std::string> GetTType4Index(uint32_t index); 218 219 /** find in node4key_ */ 220 std::optional<CacheItem> GetCachedNode4Key(const std::optional<std::string>& key); 221 222 /** 223 * if key and ttype given, search for UINode of given key and ttype 224 * in caches, i.e. in node4key4ttype 225 * return nullptr in all other cases 226 */ 227 RefPtr<UINode> GetCachedNode4Key4Ttype( 228 const std::optional<std::string>& key, const std::optional<std::string>& ttype); 229 230 /** 231 * for given index return distance from active range, 232 * or 0 if within active range 233 * distance is int max for invalidated keys 234 */ 235 uint32_t GetDistanceFromRange(uint32_t index) const; 236 /** 237 * scenario: find L1 key that should be updated 238 * choose the key whose index is the furthest away from active range 239 * given two keys compare their distFromRange 240 */ 241 bool CompareKeyByIndexDistance(const std::string& key1, const std::string& key2) const; 242 243 std::set<std::string> GetL2KeysForTType( 244 const std::unordered_map<std::string, RefPtr<UINode>>& uiNode4Key) const; 245 246 /** 247 * does given range overlap the last active range? 248 */ 249 bool HasOverlapWithLastActiveRange(uint32_t from, uint32_t to); 250 251 /** 252 * get more index -> key and index -> ttype from TS side 253 * may request additional keys if allowFetchMore is true 254 */ 255 bool FetchMoreKeysTTypes(uint32_t from, uint32_t to, bool allowFetchMore = true); 256 257 // Map ttype -> cacheSize. Each ttype incl default has own L2 size 258 std::map<std::string, std::pair<bool, uint32_t>> cacheCountL24ttype_; 259 260 // request TS to create new sub-tree for given index or update existing 261 // update subtree cached for (old) index 262 // API might need to change to tell which old item to update 263 std::function<void(uint32_t)> onCreateNode_; 264 std::function<void(const std::string&, uint32_t)> onUpdateNode_; 265 266 // get index -> key for given range 267 // resulting list starts with 'from' but might end before 'to' if Array shorter 268 std::function<std::list<std::string>(uint32_t, uint32_t)> onGetTypes4Range_; 269 270 // get index -> ttype for given range 271 // resulting list starts with 'from' but might end before 'to' if Array shorter 272 std::function<std::list<std::string>(uint32_t, uint32_t)> onGetKeys4Range_; 273 274 // memorize active ranges of past 2 (last, prev) 275 // from SetActiveChildRange calls and use to calc scroll direction 276 // active range:= visible range + pre-render items above and below 277 // number of pre-render items defined by Gird/List.cacheCount and informed 278 // as cacheStart and cacheEnd in SetActiveChildRange 279 std::pair<uint32_t, uint32_t> lastActiveRanges_[2] = { { 0, 0 }, { 0, 0 } }; 280 281 // keys of active nodes, UINodes must be on the UI tree, 282 // this list is also known as L1 283 // all keys not in this set are in "L2" 284 std::unordered_set<std::string> activeNodeKeysInL1_; 285 286 // L1 287 // index -> key and reverse 288 // lazy request from TS side can be invalidated 289 std::unordered_map<uint32_t, std::string> key4index_; 290 std::unordered_map<std::string, uint32_t> index4Key_; 291 292 // index -> ttype 293 // lazy request from TS side can be invalidated 294 std::unordered_map<uint32_t, std::string> ttype4index_; 295 std::unordered_map<std::string, uint32_t> index4ttype_; 296 297 // Map ttype -> Map key -> UINode 298 std::unordered_map<std::string, std::unordered_map<std::string, RefPtr<UINode>>> node4key4ttype_; 299 300 // Map Map key -> UINode 301 std::unordered_map<std::string, CacheItem> node4key_; 302 303 // for tracking reused/recycled nodes 304 std::unordered_set<int32_t> reusedNodeIds_; 305 306 // used to record whether a PostIdleTask is requeired after RebuildL1WithKey 307 bool isModified_ = false; 308 309 // reuse node in L2 cache or not 310 bool reusable_ = true; 311 }; // class NodeCache 312 313 } // namespace OHOS::Ace::NG 314 315 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_REPEAT_VIRTUAL_SCROLL_CACHES_H 316