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