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