mirror of
https://gitee.com/openharmony/arkui_ace_engine
synced 2024-11-23 07:01:24 +00:00
repeat feture
Signed-off-by: chenbenzhi <chenbenzhi@huawei.com> Change-Id: Ie59b5b8d18ac833b2f44edb70be06e038c78211f
This commit is contained in:
parent
0b360b6bcc
commit
00c2b882cc
@ -45,7 +45,7 @@ BreakAfterJavaFieldAnnotations: true
|
|||||||
ColumnLimit: 120
|
ColumnLimit: 120
|
||||||
CommentPragmas: '^ IWYU pragma:'
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
QualifierAlignment: Leave
|
QualifierAlignment: Leave
|
||||||
ReflowComments: false
|
ReflowComments: true
|
||||||
CompactNamespaces: false
|
CompactNamespaces: false
|
||||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
ConstructorInitializerIndentWidth: 4
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
@ -299,6 +299,7 @@ template("declarative_js_engine") {
|
|||||||
"jsview/js_rendering_context.cpp",
|
"jsview/js_rendering_context.cpp",
|
||||||
"jsview/js_rendering_context_settings.cpp",
|
"jsview/js_rendering_context_settings.cpp",
|
||||||
"jsview/js_repeat.cpp",
|
"jsview/js_repeat.cpp",
|
||||||
|
"jsview/js_repeat_virtual_scroll.cpp",
|
||||||
"jsview/js_richeditor.cpp",
|
"jsview/js_richeditor.cpp",
|
||||||
"jsview/js_row.cpp",
|
"jsview/js_row.cpp",
|
||||||
"jsview/js_row_split.cpp",
|
"jsview/js_row_split.cpp",
|
||||||
@ -819,6 +820,7 @@ template("declarative_js_engine_ng") {
|
|||||||
"jsview/js_rendering_context.cpp",
|
"jsview/js_rendering_context.cpp",
|
||||||
"jsview/js_rendering_context_settings.cpp",
|
"jsview/js_rendering_context_settings.cpp",
|
||||||
"jsview/js_repeat.cpp",
|
"jsview/js_repeat.cpp",
|
||||||
|
"jsview/js_repeat_virtual_scroll.cpp",
|
||||||
"jsview/js_richeditor.cpp",
|
"jsview/js_richeditor.cpp",
|
||||||
"jsview/js_row.cpp",
|
"jsview/js_row.cpp",
|
||||||
"jsview/js_row_split.cpp",
|
"jsview/js_row_split.cpp",
|
||||||
|
@ -136,6 +136,7 @@
|
|||||||
#include "bridge/declarative_frontend/jsview/js_rendering_context.h"
|
#include "bridge/declarative_frontend/jsview/js_rendering_context.h"
|
||||||
#include "bridge/declarative_frontend/jsview/js_rendering_context_settings.h"
|
#include "bridge/declarative_frontend/jsview/js_rendering_context_settings.h"
|
||||||
#include "bridge/declarative_frontend/jsview/js_repeat.h"
|
#include "bridge/declarative_frontend/jsview/js_repeat.h"
|
||||||
|
#include "bridge/declarative_frontend/jsview/js_repeat_virtual_scroll.h"
|
||||||
#include "bridge/declarative_frontend/jsview/js_richeditor.h"
|
#include "bridge/declarative_frontend/jsview/js_richeditor.h"
|
||||||
#include "bridge/declarative_frontend/jsview/js_row.h"
|
#include "bridge/declarative_frontend/jsview/js_row.h"
|
||||||
#include "bridge/declarative_frontend/jsview/js_row_split.h"
|
#include "bridge/declarative_frontend/jsview/js_row_split.h"
|
||||||
@ -634,6 +635,7 @@ static const std::unordered_map<std::string, std::function<void(BindingTarget)>>
|
|||||||
{ "Swiper", JSSwiper::JSBind },
|
{ "Swiper", JSSwiper::JSBind },
|
||||||
{ "Panel", JSSlidingPanel::JSBind },
|
{ "Panel", JSSlidingPanel::JSBind },
|
||||||
{ "RepeatNative", JSRepeat::JSBind },
|
{ "RepeatNative", JSRepeat::JSBind },
|
||||||
|
{ "RepeatVirtualScrollNative", JSRepeatVirtualScroll::JSBind },
|
||||||
{ "NavDestination", JSNavDestination::JSBind },
|
{ "NavDestination", JSNavDestination::JSBind },
|
||||||
{ "Navigation", JSNavigation::JSBind },
|
{ "Navigation", JSNavigation::JSBind },
|
||||||
{ "NativeNavPathStack", JSNavPathStack::JSBind },
|
{ "NativeNavPathStack", JSNavPathStack::JSBind },
|
||||||
|
@ -123,6 +123,7 @@
|
|||||||
#include "frameworks/bridge/declarative_frontend/jsview/js_rendering_context.h"
|
#include "frameworks/bridge/declarative_frontend/jsview/js_rendering_context.h"
|
||||||
#include "frameworks/bridge/declarative_frontend/jsview/js_rendering_context_settings.h"
|
#include "frameworks/bridge/declarative_frontend/jsview/js_rendering_context_settings.h"
|
||||||
#include "frameworks/bridge/declarative_frontend/jsview/js_repeat.h"
|
#include "frameworks/bridge/declarative_frontend/jsview/js_repeat.h"
|
||||||
|
#include "frameworks/bridge/declarative_frontend/jsview/js_repeat_virtual_scroll.h"
|
||||||
#include "frameworks/bridge/declarative_frontend/jsview/js_row.h"
|
#include "frameworks/bridge/declarative_frontend/jsview/js_row.h"
|
||||||
#include "frameworks/bridge/declarative_frontend/jsview/js_row_split.h"
|
#include "frameworks/bridge/declarative_frontend/jsview/js_row_split.h"
|
||||||
#include "frameworks/bridge/declarative_frontend/jsview/js_scope_util.h"
|
#include "frameworks/bridge/declarative_frontend/jsview/js_scope_util.h"
|
||||||
@ -451,6 +452,7 @@ void JsBindViews(BindingTarget globalObj, void* nativeEngine)
|
|||||||
JSTabsController::JSBind(globalObj);
|
JSTabsController::JSBind(globalObj);
|
||||||
JSForEach::JSBind(globalObj);
|
JSForEach::JSBind(globalObj);
|
||||||
JSRepeat::JSBind(globalObj);
|
JSRepeat::JSBind(globalObj);
|
||||||
|
JSRepeatVirtualScroll::JSBind(globalObj);
|
||||||
JSIfElse::JSBind(globalObj);
|
JSIfElse::JSBind(globalObj);
|
||||||
JSDivider::JSBind(globalObj);
|
JSDivider::JSBind(globalObj);
|
||||||
JSScroll::JSBind(globalObj);
|
JSScroll::JSBind(globalObj);
|
||||||
|
@ -4113,21 +4113,15 @@ class PUV2ViewBase extends NativeViewPartialUpdate {
|
|||||||
this.purgeDeletedElmtIds();
|
this.purgeDeletedElmtIds();
|
||||||
Array.from(this.updateFuncByElmtId.keys()).sort(ViewPU.compareNumber).forEach(elmtId => this.UpdateElement(elmtId));
|
Array.from(this.updateFuncByElmtId.keys()).sort(ViewPU.compareNumber).forEach(elmtId => this.UpdateElement(elmtId));
|
||||||
if (deep) {
|
if (deep) {
|
||||||
this.childrenWeakrefMap_.forEach((weakRefChild) => {
|
for (const child of this.childrenWeakrefMap_.values()) {
|
||||||
const child = weakRefChild.deref();
|
const childView = child.deref();
|
||||||
if (child) {
|
if (childView) {
|
||||||
if (child instanceof ViewPU) {
|
childView.forceCompleteRerender(true);
|
||||||
if (!child.hasBeenRecycled_) {
|
|
||||||
child.forceCompleteRerender(true);
|
|
||||||
} else {
|
|
||||||
child.delayCompleteRerender(deep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Error('forceCompleteRender not implemented for ViewV2, yet');
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* force a complete rerender / update on specific node by executing update function.
|
* force a complete rerender / update on specific node by executing update function.
|
||||||
@ -7315,6 +7309,10 @@ class ObserveV2 {
|
|||||||
static IsObservedObjectV2(value) {
|
static IsObservedObjectV2(value) {
|
||||||
return (value && typeof (value) === 'object' && value[ObserveV2.V2_DECO_META]);
|
return (value && typeof (value) === 'object' && value[ObserveV2.V2_DECO_META]);
|
||||||
}
|
}
|
||||||
|
static getCurrentRecordedId() {
|
||||||
|
const bound = ObserveV2.getObserve().stackOfRenderedComponents_.top();
|
||||||
|
return bound ? bound[0] : -1;
|
||||||
|
}
|
||||||
// At the start of observeComponentCreation or
|
// At the start of observeComponentCreation or
|
||||||
// MonitorV2 observeObjectAccess
|
// MonitorV2 observeObjectAccess
|
||||||
startRecordDependencies(cmp, id) {
|
startRecordDependencies(cmp, id) {
|
||||||
@ -7464,8 +7462,6 @@ class ObserveV2 {
|
|||||||
// add dependency view model object 'target' property 'attrName'
|
// add dependency view model object 'target' property 'attrName'
|
||||||
// to current this.bindId
|
// to current this.bindId
|
||||||
addRef(target, attrName) {
|
addRef(target, attrName) {
|
||||||
var _a, _b, _c, _d;
|
|
||||||
var _e, _f;
|
|
||||||
const bound = this.stackOfRenderedComponents_.top();
|
const bound = this.stackOfRenderedComponents_.top();
|
||||||
if (!bound) {
|
if (!bound) {
|
||||||
return;
|
return;
|
||||||
@ -7476,7 +7472,15 @@ class ObserveV2 {
|
|||||||
throw new TypeError(error);
|
throw new TypeError(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = bound[0];
|
this.addRef4IdInternal(bound[0], target, attrName);
|
||||||
|
}
|
||||||
|
addRef4Id(id, target, attrName) {
|
||||||
|
|
||||||
|
this.addRef4IdInternal(id, target, attrName);
|
||||||
|
}
|
||||||
|
addRef4IdInternal(id, target, attrName) {
|
||||||
|
var _a, _b, _c, _d;
|
||||||
|
var _e, _f;
|
||||||
// Map: attribute/symbol -> dependent id
|
// Map: attribute/symbol -> dependent id
|
||||||
const symRefs = (_a = target[_e = ObserveV2.SYMBOL_REFS]) !== null && _a !== void 0 ? _a : (target[_e] = {});
|
const symRefs = (_a = target[_e = ObserveV2.SYMBOL_REFS]) !== null && _a !== void 0 ? _a : (target[_e] = {});
|
||||||
(_b = symRefs[attrName]) !== null && _b !== void 0 ? _b : (symRefs[attrName] = new Set());
|
(_b = symRefs[attrName]) !== null && _b !== void 0 ? _b : (symRefs[attrName] = new Set());
|
||||||
@ -7588,10 +7592,23 @@ class ObserveV2 {
|
|||||||
}
|
}
|
||||||
updateDirty() {
|
updateDirty() {
|
||||||
this.startDirty_ = true;
|
this.startDirty_ = true;
|
||||||
this.updateDirty2();
|
this.updateDirty2(false);
|
||||||
this.startDirty_ = false;
|
this.startDirty_ = false;
|
||||||
}
|
}
|
||||||
updateDirty2() {
|
/**
|
||||||
|
* execute /update in this order
|
||||||
|
* - @Computed variables
|
||||||
|
* - @Monitor functions
|
||||||
|
* - UINode re-render
|
||||||
|
* three nested loops, means:
|
||||||
|
* process @Computed until no more @Computed need update
|
||||||
|
* process @Monitor until no more @Computed and @Monitor
|
||||||
|
* process UINode update until no more @Computed and @Monitor and UINode rerender
|
||||||
|
*
|
||||||
|
* @param updateUISynchronously should be set to true if called during VSYNC only
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
updateDirty2(updateUISynchronously = false) {
|
||||||
aceTrace.begin('updateDirty2');
|
aceTrace.begin('updateDirty2');
|
||||||
|
|
||||||
// obtain and unregister the removed elmtIds
|
// obtain and unregister the removed elmtIds
|
||||||
@ -7626,9 +7643,10 @@ class ObserveV2 {
|
|||||||
if (this.elmtIdsChanged_.size) {
|
if (this.elmtIdsChanged_.size) {
|
||||||
const elmtIds = Array.from(this.elmtIdsChanged_).sort((elmtId1, elmtId2) => elmtId1 - elmtId2);
|
const elmtIds = Array.from(this.elmtIdsChanged_).sort((elmtId1, elmtId2) => elmtId1 - elmtId2);
|
||||||
this.elmtIdsChanged_ = new Set();
|
this.elmtIdsChanged_ = new Set();
|
||||||
this.updateUINodes(elmtIds);
|
updateUISynchronously ? this.updateUINodesSynchronously(elmtIds) : this.updateUINodes(elmtIds);
|
||||||
}
|
}
|
||||||
} while (this.elmtIdsChanged_.size + this.monitorIdsChanged_.size + this.computedPropIdsChanged_.size > 0);
|
} while (this.elmtIdsChanged_.size + this.monitorIdsChanged_.size + this.computedPropIdsChanged_.size > 0);
|
||||||
|
|
||||||
aceTrace.end();
|
aceTrace.end();
|
||||||
}
|
}
|
||||||
updateDirtyComputedProps(computed) {
|
updateDirtyComputedProps(computed) {
|
||||||
@ -7676,10 +7694,11 @@ class ObserveV2 {
|
|||||||
* FlushDirtyNodesUpdate to CustomNode to ViewV2.updateDirtyElements to UpdateElement
|
* FlushDirtyNodesUpdate to CustomNode to ViewV2.updateDirtyElements to UpdateElement
|
||||||
* Code left here to reproduce benchmark measurements, compare with future optimisation
|
* Code left here to reproduce benchmark measurements, compare with future optimisation
|
||||||
* @param elmtIds
|
* @param elmtIds
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
updateUINodesWithoutVSync(elmtIds) {
|
updateUINodesSynchronously(elmtIds) {
|
||||||
|
|
||||||
aceTrace.begin(`ObserveV2.updateUINodes: ${elmtIds.length} elmtId`);
|
aceTrace.begin(`ObserveV2.updateUINodesSynchronously: ${elmtIds.length} elmtId`);
|
||||||
let view;
|
let view;
|
||||||
let weak;
|
let weak;
|
||||||
elmtIds.forEach((elmtId) => {
|
elmtIds.forEach((elmtId) => {
|
||||||
@ -7703,7 +7722,7 @@ class ObserveV2 {
|
|||||||
// much slower
|
// much slower
|
||||||
updateUINodes(elmtIds) {
|
updateUINodes(elmtIds) {
|
||||||
|
|
||||||
aceTrace.begin(`ObserveV2.updateUINodesSlow: ${elmtIds.length} elmtId`);
|
aceTrace.begin(`ObserveV2.updateUINodes: ${elmtIds.length} elmtId`);
|
||||||
let viewWeak;
|
let viewWeak;
|
||||||
let view;
|
let view;
|
||||||
elmtIds.forEach((elmtId) => {
|
elmtIds.forEach((elmtId) => {
|
||||||
@ -9530,9 +9549,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|||||||
// implementation for existing state observation system
|
// implementation for existing state observation system
|
||||||
class __RepeatItemPU {
|
class __RepeatItemPU {
|
||||||
constructor(owningView, initialItem, initialIndex) {
|
constructor(owningView, initialItem, initialIndex) {
|
||||||
this._observedItem = new ObservedPropertyPU(initialItem, owningView, "Repeat item");
|
this._observedItem = new ObservedPropertyPU(initialItem, owningView, 'Repeat item');
|
||||||
if (initialIndex !== undefined) {
|
if (initialIndex !== undefined) {
|
||||||
this._observedIndex = new ObservedPropertyPU(initialIndex, owningView, "Repeat index");
|
this._observedIndex = new ObservedPropertyPU(initialIndex, owningView, 'Repeat index');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get item() {
|
get item() {
|
||||||
@ -9597,7 +9616,7 @@ class __RepeatDefaultKeyGen {
|
|||||||
}
|
}
|
||||||
static funcImpl(item) {
|
static funcImpl(item) {
|
||||||
// fast keygen logic can be used with objects/symbols only
|
// fast keygen logic can be used with objects/symbols only
|
||||||
if (typeof item !== 'object' && typeof item !== 'symbol') {
|
if (typeof item != 'object' && typeof item !== 'symbol') {
|
||||||
return JSON.stringify(item);
|
return JSON.stringify(item);
|
||||||
}
|
}
|
||||||
// generate a numeric key, store mappings in WeakMap
|
// generate a numeric key, store mappings in WeakMap
|
||||||
@ -9610,35 +9629,130 @@ class __RepeatDefaultKeyGen {
|
|||||||
}
|
}
|
||||||
__RepeatDefaultKeyGen.weakMap_ = new WeakMap();
|
__RepeatDefaultKeyGen.weakMap_ = new WeakMap();
|
||||||
__RepeatDefaultKeyGen.lastKey_ = 0;
|
__RepeatDefaultKeyGen.lastKey_ = 0;
|
||||||
|
;
|
||||||
|
;
|
||||||
// __Repeat implements ForEach with child re-use for both existing state observation
|
// __Repeat implements ForEach with child re-use for both existing state observation
|
||||||
// and deep observation , for non-virtual and virtual code paths (TODO)
|
// and deep observation , for non-virtual and virtual code paths (TODO)
|
||||||
class __RepeatV2 {
|
class __RepeatV2 {
|
||||||
constructor(arr) {
|
constructor(arr) {
|
||||||
|
this.config = {};
|
||||||
this.isVirtualScroll = false;
|
this.isVirtualScroll = false;
|
||||||
this.key2Item_ = new Map();
|
//console.log('__RepeatV2 ctor')
|
||||||
this.arr_ = arr !== null && arr !== void 0 ? arr : [];
|
this.config.arr = arr !== null && arr !== void 0 ? arr : [];
|
||||||
this.keyGenFunction_ = __RepeatDefaultKeyGen.func;
|
this.config.itemGenFuncs = {};
|
||||||
}
|
this.config.keyGenFunc = __RepeatDefaultKeyGen.func;
|
||||||
updateArr(arr) {
|
this.config.typeGenFunc = (() => '');
|
||||||
this.arr_ = arr !== null && arr !== void 0 ? arr : [];
|
this.config.totalCount = this.config.arr.length;
|
||||||
return this;
|
this.config.templateOptions = {};
|
||||||
|
this.config.mkRepeatItem = this.mkRepeatItem;
|
||||||
}
|
}
|
||||||
each(itemGenFunc) {
|
each(itemGenFunc) {
|
||||||
this.itemGenFunc_ = itemGenFunc;
|
//console.log('__RepeatV2.each()')
|
||||||
|
this.config.itemGenFuncs[''] = itemGenFunc;
|
||||||
|
this.config.templateOptions[''] = Object.assign({}, this.defaultTemplateOptions());
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
key(idGenFunc) {
|
key(keyGenFunc) {
|
||||||
this.keyGenFunction_ = idGenFunc !== null && idGenFunc !== void 0 ? idGenFunc : __RepeatDefaultKeyGen.func;
|
//console.log('__RepeatV2.key()')
|
||||||
|
this.config.keyGenFunc = keyGenFunc !== null && keyGenFunc !== void 0 ? keyGenFunc : __RepeatDefaultKeyGen.func;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
virtualScroll() {
|
virtualScroll(options) {
|
||||||
|
var _a;
|
||||||
|
//console.log('__RepeatV2.virtualScroll()')
|
||||||
|
this.config.totalCount = (_a = options === null || options === void 0 ? void 0 : options.totalCount) !== null && _a !== void 0 ? _a : this.config.arr.length;
|
||||||
this.isVirtualScroll = true;
|
this.isVirtualScroll = true;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
onMove(handler) {
|
// function to decide which template to use, each template has an id
|
||||||
this.onMoveHandler_ = handler;
|
templateId(typeFunc) {
|
||||||
|
//console.log('__RepeatV2.templateId()')
|
||||||
|
this.config.typeGenFunc = typeFunc;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
// template: id + builder function to render specific type of data item
|
||||||
|
template(type, itemGenFunc, options) {
|
||||||
|
//console.log('__RepeatV2.template()')
|
||||||
|
this.config.itemGenFuncs[type] = itemGenFunc;
|
||||||
|
this.config.templateOptions[type] = Object.assign(Object.assign({}, this.defaultTemplateOptions()), options);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
updateArr(arr) {
|
||||||
|
//console.log('__RepeatV2.updateArr()')
|
||||||
|
this.config.arr = arr !== null && arr !== void 0 ? arr : [];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
render(isInitialRender) {
|
||||||
|
var _a, _b, _c;
|
||||||
|
//console.log('__RepeatV2.render()')
|
||||||
|
if (!((_a = this.config.itemGenFuncs) === null || _a === void 0 ? void 0 : _a[''])) {
|
||||||
|
throw new Error(`__RepeatV2 item builder function unspecified. Usage error`);
|
||||||
|
}
|
||||||
|
if (!this.isVirtualScroll) {
|
||||||
|
// Repeat
|
||||||
|
(_b = this.impl) !== null && _b !== void 0 ? _b : (this.impl = new __RepeatImpl());
|
||||||
|
this.impl.render(this.config, isInitialRender);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// RepeatVirtualScroll
|
||||||
|
(_c = this.impl) !== null && _c !== void 0 ? _c : (this.impl = new __RepeatVirtualScrollImpl());
|
||||||
|
this.impl.render(this.config, isInitialRender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMove(handler) {
|
||||||
|
this.config.onMoveHandler = handler;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
defaultTemplateOptions() {
|
||||||
|
return { cachedCount: 1 };
|
||||||
|
}
|
||||||
|
mkRepeatItem(item, index) {
|
||||||
|
return new __RepeatItemV2(item, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
; // __RepeatV2<T>
|
||||||
|
// __Repeat implements ForEach with child re-use for both existing state observation
|
||||||
|
// and deep observation , for non-virtual and virtual code paths (TODO)
|
||||||
|
class __RepeatPU extends __RepeatV2 {
|
||||||
|
constructor(owningView, arr) {
|
||||||
|
super(arr);
|
||||||
|
this.owningView_ = owningView;
|
||||||
|
}
|
||||||
|
mkRepeatItem(item, index) {
|
||||||
|
return new __RepeatItemPU(this.owningView_, item, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023-2024 Huawei Device Co., Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* all definitions in this file are framework internal
|
||||||
|
*/
|
||||||
|
class __RepeatImpl {
|
||||||
|
/**/
|
||||||
|
constructor() {
|
||||||
|
this.key2Item_ = new Map();
|
||||||
|
}
|
||||||
|
/**/
|
||||||
|
render(config, isInitialRender) {
|
||||||
|
this.arr_ = config.arr;
|
||||||
|
this.itemGenFuncs_ = config.itemGenFuncs;
|
||||||
|
this.typeGenFunc_ = config.typeGenFunc;
|
||||||
|
this.keyGenFunction_ = config.keyGenFunc;
|
||||||
|
this.mkRepeatItem_ = config.mkRepeatItem;
|
||||||
|
this.onMoveHandler_ = config.onMoveHandler;
|
||||||
|
isInitialRender ? this.initialRender() : this.reRender();
|
||||||
|
}
|
||||||
genKeys() {
|
genKeys() {
|
||||||
const key2Item = new Map();
|
const key2Item = new Map();
|
||||||
this.arr_.forEach((item, index) => {
|
this.arr_.forEach((item, index) => {
|
||||||
@ -9646,34 +9760,20 @@ class __RepeatV2 {
|
|||||||
key2Item.set(key, { key, index });
|
key2Item.set(key, { key, index });
|
||||||
});
|
});
|
||||||
if (key2Item.size < this.arr_.length) {
|
if (key2Item.size < this.arr_.length) {
|
||||||
stateMgmtConsole.warn("Duplicates detected, fallback to index-based keyGen.");
|
stateMgmtConsole.warn("__RepeatImpl: Duplicates detected, fallback to index-based keyGen.");
|
||||||
// Causes all items to be re-rendered
|
// Causes all items to be re-rendered
|
||||||
this.keyGenFunction_ = __RepeatDefaultKeyGen.funcWithIndex;
|
this.keyGenFunction_ = __RepeatDefaultKeyGen.funcWithIndex;
|
||||||
return this.genKeys();
|
return this.genKeys();
|
||||||
}
|
}
|
||||||
return key2Item;
|
return key2Item;
|
||||||
}
|
}
|
||||||
mkRepeatItem(item, index) {
|
initialRender() {
|
||||||
return new __RepeatItemV2(item, index);
|
//console.log('__RepeatImpl initialRender() 0')
|
||||||
}
|
|
||||||
render(isInitialRender) {
|
|
||||||
if (!this.itemGenFunc_) {
|
|
||||||
throw new Error(`itemGen function undefined. Usage error`);
|
|
||||||
}
|
|
||||||
if (this.isVirtualScroll) {
|
|
||||||
// TODO: Add render for LazyforEach with child update.
|
|
||||||
throw new Error("TODO virtual code path");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
isInitialRender ? this.initialRenderNoneVirtual() : this.rerenderNoneVirtual();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
initialRenderNoneVirtual() {
|
|
||||||
this.key2Item_ = this.genKeys();
|
this.key2Item_ = this.genKeys();
|
||||||
RepeatNative.startRender();
|
RepeatNative.startRender();
|
||||||
let index = 0;
|
let index = 0;
|
||||||
this.key2Item_.forEach((itemInfo, key) => {
|
this.key2Item_.forEach((itemInfo, key) => {
|
||||||
itemInfo.repeatItem = this.mkRepeatItem(this.arr_[index], index);
|
itemInfo.repeatItem = this.mkRepeatItem_(this.arr_[index], index);
|
||||||
this.initialRenderItem(key, itemInfo.repeatItem);
|
this.initialRenderItem(key, itemInfo.repeatItem);
|
||||||
index++;
|
index++;
|
||||||
});
|
});
|
||||||
@ -9684,7 +9784,7 @@ class __RepeatV2 {
|
|||||||
UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs(removedChildElmtIds);
|
UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs(removedChildElmtIds);
|
||||||
|
|
||||||
}
|
}
|
||||||
rerenderNoneVirtual() {
|
reRender() {
|
||||||
const oldKey2Item = this.key2Item_;
|
const oldKey2Item = this.key2Item_;
|
||||||
this.key2Item_ = this.genKeys();
|
this.key2Item_ = this.genKeys();
|
||||||
// identify array items that have been deleted
|
// identify array items that have been deleted
|
||||||
@ -9710,6 +9810,15 @@ class __RepeatV2 {
|
|||||||
itemInfo.repeatItem.updateIndex(index);
|
itemInfo.repeatItem.updateIndex(index);
|
||||||
// C++ mv from tempChildren[oldIndex] to end of children_
|
// C++ mv from tempChildren[oldIndex] to end of children_
|
||||||
RepeatNative.moveChild(oldIndex);
|
RepeatNative.moveChild(oldIndex);
|
||||||
|
// TBD moveChild() only when item types are same
|
||||||
|
//const type0 = this.typeGenFunc_(oldItemInfo.repeatItem.item, oldIndex);
|
||||||
|
//const type1 = this.typeGenFunc_(itemInfo.repeatItem.item, index);
|
||||||
|
//if (type0 == type1) {
|
||||||
|
// // C++ mv from tempChildren[oldIndex] to end of children_
|
||||||
|
// RepeatNative.moveChild(oldIndex);
|
||||||
|
//} else {
|
||||||
|
// this.initialRenderItem(key, itemInfo.repeatItem);
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
else if (deletedKeysAndIndex.length) {
|
else if (deletedKeysAndIndex.length) {
|
||||||
// case #2:
|
// case #2:
|
||||||
@ -9725,6 +9834,7 @@ class __RepeatV2 {
|
|||||||
itemInfo.repeatItem.updateIndex(index);
|
itemInfo.repeatItem.updateIndex(index);
|
||||||
// update key2item_ Map
|
// update key2item_ Map
|
||||||
this.key2Item_.set(key, itemInfo);
|
this.key2Item_.set(key, itemInfo);
|
||||||
|
// TBD moveChild() only when item types are same
|
||||||
// C++ mv from tempChildren[oldIndex] to end of children_
|
// C++ mv from tempChildren[oldIndex] to end of children_
|
||||||
RepeatNative.moveChild(oldKeyIndex);
|
RepeatNative.moveChild(oldKeyIndex);
|
||||||
}
|
}
|
||||||
@ -9732,7 +9842,7 @@ class __RepeatV2 {
|
|||||||
// case #3:
|
// case #3:
|
||||||
// new array item, there are no deleted array items
|
// new array item, there are no deleted array items
|
||||||
// render new UINode children
|
// render new UINode children
|
||||||
itemInfo.repeatItem = this.mkRepeatItem(item, index);
|
itemInfo.repeatItem = this.mkRepeatItem_(item, index);
|
||||||
this.initialRenderItem(key, itemInfo.repeatItem);
|
this.initialRenderItem(key, itemInfo.repeatItem);
|
||||||
}
|
}
|
||||||
index++;
|
index++;
|
||||||
@ -9752,26 +9862,155 @@ class __RepeatV2 {
|
|||||||
|
|
||||||
}
|
}
|
||||||
initialRenderItem(key, repeatItem) {
|
initialRenderItem(key, repeatItem) {
|
||||||
|
var _a, _b;
|
||||||
|
//console.log('__RepeatImpl initialRenderItem()')
|
||||||
// render new UINode children
|
// render new UINode children
|
||||||
|
|
||||||
// C++: initial render will render to the end of children_
|
// C++: initial render will render to the end of children_
|
||||||
RepeatNative.createNewChildStart(key);
|
RepeatNative.createNewChildStart(key);
|
||||||
// execute the ItemGen function
|
// execute the itemGen function
|
||||||
this.itemGenFunc_(repeatItem);
|
const itemType = (_a = this.typeGenFunc_(repeatItem.item, repeatItem.index)) !== null && _a !== void 0 ? _a : '';
|
||||||
|
const itemFunc = (_b = this.itemGenFuncs_[itemType]) !== null && _b !== void 0 ? _b : this.itemGenFuncs_[''];
|
||||||
|
itemFunc(repeatItem);
|
||||||
RepeatNative.createNewChildFinish(key);
|
RepeatNative.createNewChildFinish(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// __Repeat implements ForEach with child re-use for both existing state observation
|
;
|
||||||
// and deep observation , for non-virtual and virtual code paths (TODO)
|
/*
|
||||||
class __RepeatPU extends __RepeatV2 {
|
* Copyright (c) 2023-2024 Huawei Device Co., Ltd.
|
||||||
constructor(owningView, arr) {
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
super(arr);
|
* you may not use this file except in compliance with the License.
|
||||||
this.owningView_ = owningView;
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* all definitions in this file are framework internal
|
||||||
|
*/
|
||||||
|
// Implements ForEach with child re-use for both existing state observation and
|
||||||
|
// deep observation. For virtual-scroll code paths
|
||||||
|
class __RepeatVirtualScrollImpl {
|
||||||
|
/**/
|
||||||
|
constructor() {
|
||||||
}
|
}
|
||||||
mkRepeatItem(item, index) {
|
render(config, isInitialRender) {
|
||||||
return new __RepeatItemPU(this.owningView_, item, index);
|
this.arr_ = config.arr;
|
||||||
|
this.itemGenFuncs_ = config.itemGenFuncs;
|
||||||
|
this.keyGenFunc_ = config.keyGenFunc;
|
||||||
|
this.typeGenFunc_ = config.typeGenFunc;
|
||||||
|
this.totalCount_ = config.totalCount;
|
||||||
|
this.templateOptions_ = config.templateOptions;
|
||||||
|
this.mkRepeatItem_ = config.mkRepeatItem;
|
||||||
|
this.onMoveHandler_ = config.onMoveHandler;
|
||||||
|
if (isInitialRender) {
|
||||||
|
this.initialRender(ObserveV2.getCurrentRecordedId());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.reRender();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**/
|
||||||
|
initialRender(repeatElmtId) {
|
||||||
|
// Map key -> RepeatItem
|
||||||
|
// added to closure of following lambdas
|
||||||
|
const _repeatItem4Key = new Map();
|
||||||
|
const repeatElmtId1 = repeatElmtId;
|
||||||
|
const onCreateNode = (forIndex) => {
|
||||||
|
|
||||||
|
if (forIndex < 0) {
|
||||||
|
// FIXME check also index < totalCount
|
||||||
|
throw new Error(`__RepeatVirtualScrollImpl onCreateNode: for index=${forIndex} out of range error.`);
|
||||||
|
}
|
||||||
|
// create dependency array item [forIndex] -> Repeat
|
||||||
|
// so Repeat updates when the array item changes
|
||||||
|
// FIXME observe dependencies, adding the array is insurgent for Array of objects
|
||||||
|
ObserveV2.getObserve().addRef4Id(repeatElmtId1, this.arr_, forIndex.toString());
|
||||||
|
const repeatItem = this.mkRepeatItem_(this.arr_[forIndex], forIndex);
|
||||||
|
const forKey = this.keyGenFunc_(this.arr_[forIndex], forIndex);
|
||||||
|
_repeatItem4Key.set(forKey, repeatItem);
|
||||||
|
// execute the itemGen function
|
||||||
|
this.initialRenderItem(repeatItem);
|
||||||
|
|
||||||
|
};
|
||||||
|
const onUpdateNode = (fromKey, forIndex) => {
|
||||||
|
if (!fromKey || fromKey == "" || forIndex < 0) {
|
||||||
|
// FIXME check also index < totalCount
|
||||||
|
throw new Error(`__RepeatVirtualScrollImpl onUpdateNode: fromKey "${fromKey}", forIndex=${forIndex} invalid function input error.`);
|
||||||
|
}
|
||||||
|
// create dependency array item [forIndex] -> Repeat
|
||||||
|
// so Repeat updates when the array item changes
|
||||||
|
// FIXME observe dependencies, adding the array is insurgent for Array of objects
|
||||||
|
ObserveV2.getObserve().addRef4Id(repeatElmtId1, this.arr_, forIndex.toString());
|
||||||
|
const repeatItem = _repeatItem4Key.get(fromKey);
|
||||||
|
if (!repeatItem) {
|
||||||
|
stateMgmtConsole.error(`__RepeatVirtualScrollImpl onUpdateNode: fromKey "${fromKey}", forIndex=${forIndex}, can not find RepeatItem for key. Unrecoverable error`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const forKey = this.keyGenFunc_(this.arr_[forIndex], forIndex);
|
||||||
|
|
||||||
|
repeatItem.updateItem(this.arr_[forIndex]);
|
||||||
|
repeatItem.updateIndex(forIndex);
|
||||||
|
// update Map according to made update:
|
||||||
|
// del fromKey entry and add forKey
|
||||||
|
_repeatItem4Key.delete(fromKey);
|
||||||
|
_repeatItem4Key.set(forKey, repeatItem);
|
||||||
|
|
||||||
|
// FIXME request re-render right away!
|
||||||
|
ObserveV2.getObserve().updateDirty2(true);
|
||||||
|
};
|
||||||
|
const onGetKeys4Range = (from, to) => {
|
||||||
|
|
||||||
|
const result = new Array();
|
||||||
|
for (let i = from; i <= to && i < this.arr_.length; i++) {
|
||||||
|
// create dependency array item [i] -> Repeat
|
||||||
|
// so Repeat updates when the array item changes
|
||||||
|
// FIXME observe dependencies, adding the array is insurgent for Array of objects
|
||||||
|
ObserveV2.getObserve().addRef4Id(repeatElmtId1, this.arr_, i.toString());
|
||||||
|
result.push(this.keyGenFunc_(this.arr_[i], i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
const onGetTypes4Range = (from, to) => {
|
||||||
|
var _a;
|
||||||
|
|
||||||
|
const result = new Array();
|
||||||
|
// FIXME observe dependencies, adding the array is insurgent for Array of objects
|
||||||
|
for (let i = from; i <= to && i < this.arr_.length; i++) {
|
||||||
|
// ObserveV2.getObserve().addRef4Id(repeatElmtId1, this.arr_, i.toString());
|
||||||
|
result.push((_a = this.typeGenFunc_(this.arr_[i], i)) !== null && _a !== void 0 ? _a : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
RepeatVirtualScrollNative.create(this.totalCount_, Object.entries(this.templateOptions_), {
|
||||||
|
onCreateNode,
|
||||||
|
onUpdateNode,
|
||||||
|
onGetKeys4Range,
|
||||||
|
onGetTypes4Range
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
reRender() {
|
||||||
|
|
||||||
|
RepeatVirtualScrollNative.invalidateKeyCache(this.totalCount_);
|
||||||
|
|
||||||
|
}
|
||||||
|
initialRenderItem(repeatItem) {
|
||||||
|
var _a, _b;
|
||||||
|
// execute the itemGen function
|
||||||
|
const itemType = (_a = this.typeGenFunc_(repeatItem.item, repeatItem.index)) !== null && _a !== void 0 ? _a : '';
|
||||||
|
const itemFunc = (_b = this.itemGenFuncs_[itemType]) !== null && _b !== void 0 ? _b : this.itemGenFuncs_[''];
|
||||||
|
itemFunc(repeatItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
;
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2024 Huawei Device Co., Ltd.
|
* Copyright (c) 2024 Huawei Device Co., Ltd.
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -106,7 +106,7 @@ void JSListItem::CreateForPartialUpdate(const JSCallbackInfo& args)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isLazy) {
|
if (!isLazy) {
|
||||||
ListItemModel::GetInstance()->Create();
|
ListItemModel::GetInstance()->Create(nullptr, listItemStyle);
|
||||||
} else {
|
} else {
|
||||||
RefPtr<JsFunction> jsDeepRender = AceType::MakeRefPtr<JsFunction>(args.This(), JSRef<JSFunc>::Cast(arg0));
|
RefPtr<JsFunction> jsDeepRender = AceType::MakeRefPtr<JsFunction>(args.This(), JSRef<JSFunc>::Cast(arg0));
|
||||||
auto listItemDeepRenderFunc = [execCtx = args.GetExecutionContext(),
|
auto listItemDeepRenderFunc = [execCtx = args.GetExecutionContext(),
|
||||||
|
@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Huawei Device Co., Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "bridge/declarative_frontend/jsview/js_repeat_virtual_scroll.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "base/log/ace_trace.h"
|
||||||
|
#include "base/log/log_wrapper.h"
|
||||||
|
#include "bridge/declarative_frontend/jsview/js_view_common_def.h"
|
||||||
|
#include "core/components_ng/syntax/repeat_virtual_scroll_model_ng.h"
|
||||||
|
|
||||||
|
#define JSFUNC(opts, propName) (JSRef<JSFunc>::Cast((opts)->GetProperty(propName)))
|
||||||
|
|
||||||
|
namespace OHOS::Ace {
|
||||||
|
|
||||||
|
std::unique_ptr<RepeatVirtualScrollModel> RepeatVirtualScrollModel::instance_ = nullptr;
|
||||||
|
|
||||||
|
RepeatVirtualScrollModel* RepeatVirtualScrollModel::GetInstance()
|
||||||
|
{
|
||||||
|
if (!instance_) {
|
||||||
|
instance_.reset(new NG::RepeatVirtualScrollModelNG());
|
||||||
|
}
|
||||||
|
return instance_.get();
|
||||||
|
}
|
||||||
|
} // namespace OHOS::Ace
|
||||||
|
|
||||||
|
namespace OHOS::Ace::Framework {
|
||||||
|
|
||||||
|
void JSRepeatVirtualScroll::Create(const JSCallbackInfo& info)
|
||||||
|
{
|
||||||
|
// arg 0
|
||||||
|
auto totalCount = info[0]->ToNumber<uint32_t>();
|
||||||
|
|
||||||
|
// arg 1
|
||||||
|
auto templateOptsArray = JSRef<JSArray>::Cast(info[1]);
|
||||||
|
std::map<std::string, uint32_t> templateCachedCountMap;
|
||||||
|
for (size_t i = 0; i < templateOptsArray->Length(); i++) {
|
||||||
|
JSRef<JSArray> pair = templateOptsArray->GetValueAt(i);
|
||||||
|
auto type = pair->GetValueAt(0)->ToString();
|
||||||
|
auto opts = JSRef<JSObject>::Cast(pair->GetValueAt(1));
|
||||||
|
templateCachedCountMap[type] = opts->GetProperty("cachedCount")->ToNumber<uint32_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// arg 2
|
||||||
|
auto handlers = JSRef<JSObject>::Cast(info[2]);
|
||||||
|
auto onCreateNode = [execCtx = info.GetExecutionContext(), func = JSFUNC(handlers, "onCreateNode")](
|
||||||
|
uint32_t forIndex) -> void {
|
||||||
|
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
|
||||||
|
auto params = ConvertToJSValues(forIndex);
|
||||||
|
func->Call(JSRef<JSObject>(), params.size(), params.data());
|
||||||
|
};
|
||||||
|
|
||||||
|
auto onUpdateNode = [execCtx = info.GetExecutionContext(), func = JSFUNC(handlers, "onUpdateNode")](
|
||||||
|
const std::string& fromKey, uint32_t forIndex) -> void {
|
||||||
|
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
|
||||||
|
auto params = ConvertToJSValues(fromKey, forIndex);
|
||||||
|
func->Call(JSRef<JSObject>(), params.size(), params.data());
|
||||||
|
};
|
||||||
|
|
||||||
|
auto onGetKeys4Range = [execCtx = info.GetExecutionContext(), func = JSFUNC(handlers, "onGetKeys4Range")](
|
||||||
|
uint32_t from, uint32_t to) -> std::list<std::string> {
|
||||||
|
std::list<std::string> list;
|
||||||
|
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, list);
|
||||||
|
auto params = ConvertToJSValues(from, to);
|
||||||
|
JSRef<JSVal> jsVal = func->Call(JSRef<JSObject>(), params.size(), params.data());
|
||||||
|
// convert js-array to std::list
|
||||||
|
JSRef<JSArray> jsArr = JSRef<JSArray>::Cast(jsVal);
|
||||||
|
for (size_t i = 0; i < jsArr->Length(); i++) {
|
||||||
|
list.emplace_back(jsArr->GetValueAt(i)->ToString());
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto onGetTypes4Range = [execCtx = info.GetExecutionContext(), func = JSFUNC(handlers, "onGetTypes4Range")](
|
||||||
|
uint32_t from, uint32_t to) -> std::list<std::string> {
|
||||||
|
std::list<std::string> list;
|
||||||
|
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, list);
|
||||||
|
auto params = ConvertToJSValues(from, to);
|
||||||
|
JSRef<JSVal> jsVal = func->Call(JSRef<JSObject>(), params.size(), params.data());
|
||||||
|
|
||||||
|
// convert js-array to std::list
|
||||||
|
JSRef<JSArray> jsArr = JSRef<JSArray>::Cast(jsVal);
|
||||||
|
for (size_t i = 0; i < jsArr->Length(); i++) {
|
||||||
|
list.emplace_back(jsArr->GetValueAt(i)->ToString());
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
};
|
||||||
|
|
||||||
|
RepeatVirtualScrollModel::GetInstance()->Create(
|
||||||
|
totalCount,
|
||||||
|
templateCachedCountMap,
|
||||||
|
onCreateNode,
|
||||||
|
onUpdateNode,
|
||||||
|
onGetKeys4Range,
|
||||||
|
onGetTypes4Range
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JSRepeatVirtualScroll::InvalidateKeyCache(const JSCallbackInfo& info)
|
||||||
|
{
|
||||||
|
ACE_SCOPED_TRACE("RepeatVirtualScroll:InvalidateKeyCache");
|
||||||
|
TAG_LOGD(AceLogTag::ACE_REPEAT, "JSRepeatVirtualScroll::InvalidateKeyCache");
|
||||||
|
auto totalCount = info[0]->ToNumber<uint32_t>();
|
||||||
|
RepeatVirtualScrollModel::GetInstance()->InvalidateKeyCache(totalCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JSRepeatVirtualScroll::JSBind(BindingTarget globalObj)
|
||||||
|
{
|
||||||
|
JSClass<JSRepeatVirtualScroll>::Declare("RepeatVirtualScrollNative");
|
||||||
|
JSClass<JSRepeatVirtualScroll>::StaticMethod("create", &JSRepeatVirtualScroll::Create);
|
||||||
|
JSClass<JSRepeatVirtualScroll>::StaticMethod("invalidateKeyCache", &JSRepeatVirtualScroll::InvalidateKeyCache);
|
||||||
|
JSClass<JSRepeatVirtualScroll>::Bind<>(globalObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace OHOS::Ace::Framework
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Huawei Device Co., Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FRAMEWORKS_BRIDGE_DECLARATIVE_FRONTEND_JS_VIEW_JS_REPEAT_VIRTUAL_SCROLL_H
|
||||||
|
#define FRAMEWORKS_BRIDGE_DECLARATIVE_FRONTEND_JS_VIEW_JS_REPEAT_VIRTUAL_SCROLL_H
|
||||||
|
|
||||||
|
#include "bridge/declarative_frontend/engine/bindings.h"
|
||||||
|
#include "bridge/declarative_frontend/engine/js_ref_ptr.h"
|
||||||
|
|
||||||
|
namespace OHOS::Ace::Framework {
|
||||||
|
|
||||||
|
class JSRepeatVirtualScroll {
|
||||||
|
public:
|
||||||
|
static void JSBind(BindingTarget globalObj);
|
||||||
|
|
||||||
|
static void Create(const JSCallbackInfo& info);
|
||||||
|
static void InvalidateKeyCache(const JSCallbackInfo& info);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace OHOS::Ace::Framework
|
||||||
|
#endif // FRAMEWORKS_BRIDGE_DECLARATIVE_FRONTEND_JS_VIEW_JS_REPEAT_VIRTUAL_SCROLL_H
|
@ -33,6 +33,7 @@ declare class ForEach {
|
|||||||
static createNewChildFinish(id: string, parentView: NativeViewPartialUpdate): void;
|
static createNewChildFinish(id: string, parentView: NativeViewPartialUpdate): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Repeat maps to C++ RepeatNode
|
||||||
declare class RepeatNative {
|
declare class RepeatNative {
|
||||||
static startRender(): void;
|
static startRender(): void;
|
||||||
static finishRender(removedChildElmtIds: Array<number>): void;
|
static finishRender(removedChildElmtIds: Array<number>): void;
|
||||||
@ -40,4 +41,20 @@ declare class RepeatNative {
|
|||||||
static createNewChildStart(id: string): void;
|
static createNewChildStart(id: string): void;
|
||||||
static createNewChildFinish(id: string): void;
|
static createNewChildFinish(id: string): void;
|
||||||
static onMove(handler: (from: number, to: number) => void);
|
static onMove(handler: (from: number, to: number) => void);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Repeat.virtualScroll maps to C++ RepeatVirtualScrollNode
|
||||||
|
declare class RepeatVirtualScrollNative {
|
||||||
|
static create(
|
||||||
|
totalCount: number,
|
||||||
|
templateOptions: [string, RepeatTemplateOptions][],
|
||||||
|
handlers: {
|
||||||
|
onCreateNode: (forIndex: number) => void;
|
||||||
|
onUpdateNode: (fromKey: string, forIndex: number) => void;
|
||||||
|
onGetKeys4Range: (from: number, toNumber: number) => Array<string>;
|
||||||
|
onGetTypes4Range: (from: number, toNumber: number) => Array<string>;
|
||||||
|
}
|
||||||
|
): void;
|
||||||
|
// invalidate C++ side map index -> key
|
||||||
|
static invalidateKeyCache(totalCount : number): void;
|
||||||
|
}
|
||||||
|
@ -40,9 +40,9 @@ class __RepeatItemPU<T> implements RepeatItem<T>, __IRepeatItemInternal<T> {
|
|||||||
private _observedIndex?: ObservedPropertyPU<number>;
|
private _observedIndex?: ObservedPropertyPU<number>;
|
||||||
|
|
||||||
constructor(owningView: ViewPU, initialItem: T, initialIndex?: number) {
|
constructor(owningView: ViewPU, initialItem: T, initialIndex?: number) {
|
||||||
this._observedItem = new ObservedPropertyPU<T>(initialItem, owningView, "Repeat item");
|
this._observedItem = new ObservedPropertyPU<T>(initialItem, owningView, 'Repeat item');
|
||||||
if (initialIndex !== undefined) {
|
if (initialIndex !== undefined) {
|
||||||
this._observedIndex = new ObservedPropertyPU<number>(initialIndex, owningView, "Repeat index");
|
this._observedIndex = new ObservedPropertyPU<number>(initialIndex, owningView, 'Repeat index');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,13 +116,13 @@ class __RepeatDefaultKeyGen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return the same IDs for the same pairs <item, index>
|
// Return the same IDs for the same pairs <item, index>
|
||||||
public static funcWithIndex<T>(item: T, index:number) {
|
public static funcWithIndex<T>(item: T, index: number) {
|
||||||
return `${index}__` + __RepeatDefaultKeyGen.func(item);
|
return `${index}__` + __RepeatDefaultKeyGen.func(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static funcImpl<T>(item: T) {
|
private static funcImpl<T>(item: T) {
|
||||||
// fast keygen logic can be used with objects/symbols only
|
// fast keygen logic can be used with objects/symbols only
|
||||||
if (typeof item !== 'object' && typeof item !== 'symbol') {
|
if (typeof item != 'object' && typeof item !== 'symbol') {
|
||||||
return JSON.stringify(item);
|
return JSON.stringify(item);
|
||||||
}
|
}
|
||||||
// generate a numeric key, store mappings in WeakMap
|
// generate a numeric key, store mappings in WeakMap
|
||||||
@ -132,196 +132,118 @@ class __RepeatDefaultKeyGen {
|
|||||||
// use cached key
|
// use cached key
|
||||||
return `${this.weakMap_.get(item)}`;
|
return `${this.weakMap_.get(item)}`;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
// TBD comments
|
||||||
|
interface __RepeatAPIConfig<T> {
|
||||||
|
arr?: Array<T>;
|
||||||
|
itemGenFuncs?: { [type: string]: RepeatItemGenFunc<T> };
|
||||||
|
keyGenFunc?: RepeatKeyGenFunc<T>;
|
||||||
|
typeGenFunc?: RepeatTypeGenFunc<T>;
|
||||||
|
//
|
||||||
|
totalCount?: number;
|
||||||
|
templateOptions?: { [type: string]: RepeatTemplateOptions };
|
||||||
|
//
|
||||||
|
mkRepeatItem?: (item: T, index?: number) => __RepeatItemFactoryReturn<T>;
|
||||||
|
onMoveHandler?: OnMoveHandler;
|
||||||
|
};
|
||||||
|
|
||||||
// __Repeat implements ForEach with child re-use for both existing state observation
|
// __Repeat implements ForEach with child re-use for both existing state observation
|
||||||
// and deep observation , for non-virtual and virtual code paths (TODO)
|
// and deep observation , for non-virtual and virtual code paths (TODO)
|
||||||
class __RepeatV2<T> implements RepeatAPI<T> {
|
class __RepeatV2<T> implements RepeatAPI<T> {
|
||||||
private arr_: Array<T>;
|
private config: __RepeatAPIConfig<T> = {};
|
||||||
private itemGenFunc_?: RepeatItemGenFunc<T>;
|
private impl: __RepeatImpl<T> | __RepeatVirtualScrollImpl<T>;
|
||||||
private keyGenFunction_?: RepeatKeyGenFunc<T>;
|
private isVirtualScroll = false;
|
||||||
private onMoveHandler_?: OnMoveHandler;
|
|
||||||
private isVirtualScroll: boolean = false;
|
|
||||||
private key2Item_: Map<string, __RepeatItemInfo<T>> = new Map<string, __RepeatItemInfo<T>>();
|
|
||||||
|
|
||||||
constructor(arr: Array<T>) {
|
constructor(arr: Array<T>) {
|
||||||
this.arr_ = arr ?? [];
|
//console.log('__RepeatV2 ctor')
|
||||||
this.keyGenFunction_ = __RepeatDefaultKeyGen.func;
|
this.config.arr = arr ?? [];
|
||||||
}
|
this.config.itemGenFuncs = {};
|
||||||
|
this.config.keyGenFunc = __RepeatDefaultKeyGen.func;
|
||||||
public updateArr(arr: Array<T>): RepeatAPI<T> {
|
this.config.typeGenFunc= (() => '');
|
||||||
this.arr_ = arr ?? [];
|
this.config.totalCount = this.config.arr.length;
|
||||||
return this;
|
this.config.templateOptions = {};
|
||||||
|
this.config.mkRepeatItem = this.mkRepeatItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public each(itemGenFunc: RepeatItemGenFunc<T>): RepeatAPI<T> {
|
public each(itemGenFunc: RepeatItemGenFunc<T>): RepeatAPI<T> {
|
||||||
this.itemGenFunc_ = itemGenFunc;
|
//console.log('__RepeatV2.each()')
|
||||||
|
this.config.itemGenFuncs[''] = itemGenFunc;
|
||||||
|
this.config.templateOptions[''] = { ...this.defaultTemplateOptions() };
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public key(idGenFunc: RepeatKeyGenFunc<T>): RepeatAPI<T> {
|
public key(keyGenFunc: RepeatKeyGenFunc<T>): RepeatAPI<T> {
|
||||||
this.keyGenFunction_ = idGenFunc ?? __RepeatDefaultKeyGen.func;
|
//console.log('__RepeatV2.key()')
|
||||||
|
this.config.keyGenFunc = keyGenFunc ?? __RepeatDefaultKeyGen.func;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtualScroll(): RepeatAPI<T> {
|
public virtualScroll(options? : { totalCount?: number }): RepeatAPI<T> {
|
||||||
|
//console.log('__RepeatV2.virtualScroll()')
|
||||||
|
this.config.totalCount = options?.totalCount ?? this.config.arr.length;
|
||||||
this.isVirtualScroll = true;
|
this.isVirtualScroll = true;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public onMove(handler: OnMoveHandler): RepeatAPI<T> {
|
// function to decide which template to use, each template has an id
|
||||||
this.onMoveHandler_ = handler;
|
public templateId(typeFunc: RepeatTypeGenFunc<T>): RepeatAPI<T> {
|
||||||
|
//console.log('__RepeatV2.templateId()')
|
||||||
|
this.config.typeGenFunc = typeFunc;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private genKeys(): Map<string, __RepeatItemInfo<T>> {
|
// template: id + builder function to render specific type of data item
|
||||||
const key2Item = new Map<string, __RepeatItemInfo<T>>();
|
public template(type: string, itemGenFunc: RepeatItemGenFunc<T>,
|
||||||
this.arr_.forEach((item, index) => {
|
options?: RepeatTemplateOptions): RepeatAPI<T>
|
||||||
const key = this.keyGenFunction_(item, index);
|
{
|
||||||
key2Item.set(key, { key, index });
|
//console.log('__RepeatV2.template()')
|
||||||
});
|
this.config.itemGenFuncs[type] = itemGenFunc;
|
||||||
if (key2Item.size < this.arr_.length) {
|
this.config.templateOptions[type] = { ...this.defaultTemplateOptions(), ...options };
|
||||||
stateMgmtConsole.warn("Duplicates detected, fallback to index-based keyGen.");
|
return this;
|
||||||
// Causes all items to be re-rendered
|
}
|
||||||
this.keyGenFunction_ = __RepeatDefaultKeyGen.funcWithIndex;
|
|
||||||
return this.genKeys();
|
|
||||||
|
|
||||||
|
public updateArr(arr: Array<T>): RepeatAPI<T> {
|
||||||
|
//console.log('__RepeatV2.updateArr()')
|
||||||
|
this.config.arr = arr ?? [];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(isInitialRender: boolean): void {
|
||||||
|
//console.log('__RepeatV2.render()')
|
||||||
|
if (!this.config.itemGenFuncs?.['']) {
|
||||||
|
throw new Error(`__RepeatV2 item builder function unspecified. Usage error`);
|
||||||
}
|
}
|
||||||
return key2Item;
|
if (!this.isVirtualScroll) {
|
||||||
|
// Repeat
|
||||||
|
this.impl ??= new __RepeatImpl<T>();
|
||||||
|
this.impl.render(this.config, isInitialRender);
|
||||||
|
} else {
|
||||||
|
// RepeatVirtualScroll
|
||||||
|
this.impl ??= new __RepeatVirtualScrollImpl<T>();
|
||||||
|
this.impl.render(this.config, isInitialRender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public onMove(handler: OnMoveHandler): RepeatAPI<T> {
|
||||||
|
this.config.onMoveHandler = handler;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private defaultTemplateOptions (): RepeatTemplateOptions {
|
||||||
|
return { cachedCount: 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected mkRepeatItem<T>(item: T, index?: number): __RepeatItemFactoryReturn<T> {
|
protected mkRepeatItem<T>(item: T, index?: number): __RepeatItemFactoryReturn<T> {
|
||||||
return new __RepeatItemV2(item as T, index);
|
return new __RepeatItemV2(item as T, index);
|
||||||
}
|
}
|
||||||
|
}; // __RepeatV2<T>
|
||||||
public render(isInitialRender: boolean): void {
|
|
||||||
if (!this.itemGenFunc_) {
|
|
||||||
throw new Error(`itemGen function undefined. Usage error`);
|
|
||||||
}
|
|
||||||
if (this.isVirtualScroll) {
|
|
||||||
// TODO: Add render for LazyforEach with child update.
|
|
||||||
throw new Error("TODO virtual code path");
|
|
||||||
} else {
|
|
||||||
isInitialRender ? this.initialRenderNoneVirtual() : this.rerenderNoneVirtual();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private initialRenderNoneVirtual(): void {
|
|
||||||
this.key2Item_ = this.genKeys();
|
|
||||||
|
|
||||||
RepeatNative.startRender();
|
|
||||||
|
|
||||||
let index = 0;
|
|
||||||
this.key2Item_.forEach((itemInfo, key) => {
|
|
||||||
itemInfo.repeatItem = this.mkRepeatItem(this.arr_[index], index);
|
|
||||||
this.initialRenderItem(key, itemInfo.repeatItem);
|
|
||||||
index++;
|
|
||||||
});
|
|
||||||
let removedChildElmtIds = new Array<number>();
|
|
||||||
// Fetch the removedChildElmtIds from C++ to unregister those elmtIds with UINodeRegisterProxy
|
|
||||||
RepeatNative.onMove(this.onMoveHandler_);
|
|
||||||
RepeatNative.finishRender(removedChildElmtIds);
|
|
||||||
UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs(removedChildElmtIds);
|
|
||||||
stateMgmtConsole.debug(`RepeatPU: initialRenderNoneVirtual elmtIds need unregister after repeat render: ${JSON.stringify(removedChildElmtIds)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
private rerenderNoneVirtual(): void {
|
|
||||||
const oldKey2Item: Map<string, __RepeatItemInfo<T>> = this.key2Item_;
|
|
||||||
this.key2Item_ = this.genKeys();
|
|
||||||
|
|
||||||
// identify array items that have been deleted
|
|
||||||
// these are candidates for re-use
|
|
||||||
const deletedKeysAndIndex = new Array<__RepeatItemInfo<T>>();
|
|
||||||
for (const [key, feInfo] of oldKey2Item) {
|
|
||||||
if (!this.key2Item_.has(key)) {
|
|
||||||
deletedKeysAndIndex.push(feInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// C++: mv children_ aside to tempchildren_
|
|
||||||
RepeatNative.startRender();
|
|
||||||
|
|
||||||
let index = 0;
|
|
||||||
this.key2Item_.forEach((itemInfo, key) => {
|
|
||||||
const item = this.arr_[index];
|
|
||||||
let oldItemInfo = oldKey2Item.get(key);
|
|
||||||
|
|
||||||
if (oldItemInfo) {
|
|
||||||
// case #1 retained array item
|
|
||||||
// moved from oldIndex to index
|
|
||||||
const oldIndex = oldItemInfo.index;
|
|
||||||
itemInfo.repeatItem = oldItemInfo!.repeatItem!;
|
|
||||||
stateMgmtConsole.debug(`retained: key ${key} ${oldIndex}->${index}`);
|
|
||||||
itemInfo.repeatItem.updateIndex(index);
|
|
||||||
// C++ mv from tempChildren[oldIndex] to end of children_
|
|
||||||
RepeatNative.moveChild(oldIndex);
|
|
||||||
|
|
||||||
} else if (deletedKeysAndIndex.length) {
|
|
||||||
// case #2:
|
|
||||||
// new array item, there is an deleted array items whose
|
|
||||||
// UINode children cab re-used
|
|
||||||
const oldItemInfo = deletedKeysAndIndex.pop();
|
|
||||||
const reuseKey = oldItemInfo!.key;
|
|
||||||
const oldKeyIndex = oldItemInfo!.index;
|
|
||||||
const oldRepeatItem = oldItemInfo!.repeatItem!;
|
|
||||||
itemInfo.repeatItem = oldRepeatItem;
|
|
||||||
stateMgmtConsole.debug(`new: key ${key} reuse key ${reuseKey} ${oldKeyIndex}->${index}`);
|
|
||||||
|
|
||||||
itemInfo.repeatItem.updateItem(item);
|
|
||||||
itemInfo.repeatItem.updateIndex(index);
|
|
||||||
|
|
||||||
// update key2item_ Map
|
|
||||||
this.key2Item_.set(key, itemInfo);
|
|
||||||
|
|
||||||
// C++ mv from tempChildren[oldIndex] to end of children_
|
|
||||||
RepeatNative.moveChild(oldKeyIndex);
|
|
||||||
} else {
|
|
||||||
// case #3:
|
|
||||||
// new array item, there are no deleted array items
|
|
||||||
// render new UINode children
|
|
||||||
itemInfo.repeatItem = this.mkRepeatItem(item, index);
|
|
||||||
this.initialRenderItem(key, itemInfo.repeatItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
index++;
|
|
||||||
})
|
|
||||||
|
|
||||||
// keep this.id2item_. by removing all entries for remaining
|
|
||||||
// deleted items
|
|
||||||
deletedKeysAndIndex.forEach(delItem => {
|
|
||||||
this.key2Item_.delete(delItem!.key);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Finish up for.each update
|
|
||||||
// C++ tempChildren.clear() , trigger re-layout
|
|
||||||
let removedChildElmtIds = new Array<number>();
|
|
||||||
// Fetch the removedChildElmtIds from C++ to unregister those elmtIds with UINodeRegisterProxy
|
|
||||||
RepeatNative.onMove(this.onMoveHandler_);
|
|
||||||
RepeatNative.finishRender(removedChildElmtIds);
|
|
||||||
UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs(removedChildElmtIds);
|
|
||||||
stateMgmtConsole.debug(`RepeatPU: rerenderNoneVirtual elmtIds need unregister after repeat render: ${JSON.stringify(removedChildElmtIds)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
private initialRenderItem(key: string, repeatItem: __RepeatItemFactoryReturn<T>): void {
|
|
||||||
// render new UINode children
|
|
||||||
stateMgmtConsole.debug(`new: key ${key} n/a->${repeatItem.index}`);
|
|
||||||
|
|
||||||
// C++: initial render will render to the end of children_
|
|
||||||
RepeatNative.createNewChildStart(key);
|
|
||||||
|
|
||||||
// execute the ItemGen function
|
|
||||||
this.itemGenFunc_!(repeatItem);
|
|
||||||
|
|
||||||
RepeatNative.createNewChildFinish(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// __Repeat implements ForEach with child re-use for both existing state observation
|
// __Repeat implements ForEach with child re-use for both existing state observation
|
||||||
// and deep observation , for non-virtual and virtual code paths (TODO)
|
// and deep observation , for non-virtual and virtual code paths (TODO)
|
||||||
|
|
||||||
class __RepeatPU<T> extends __RepeatV2<T> implements RepeatAPI<T> {
|
class __RepeatPU<T> extends __RepeatV2<T> implements RepeatAPI<T> {
|
||||||
private owningView_ : ViewPU;
|
private owningView_: ViewPU;
|
||||||
|
|
||||||
constructor(owningView: ViewPU, arr: Array<T>) {
|
constructor(owningView: ViewPU, arr: Array<T>) {
|
||||||
super(arr);
|
super(arr);
|
||||||
|
@ -0,0 +1,184 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023-2024 Huawei Device Co., Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* all definitions in this file are framework internal
|
||||||
|
*/
|
||||||
|
|
||||||
|
class __RepeatImpl<T> {
|
||||||
|
private arr_: Array<T>;
|
||||||
|
private itemGenFuncs_: { [type: string]: RepeatItemGenFunc<T> };
|
||||||
|
private keyGenFunction_?: RepeatKeyGenFunc<T>;
|
||||||
|
private typeGenFunc_: RepeatTypeGenFunc<T>;
|
||||||
|
//
|
||||||
|
private mkRepeatItem_: (item: T, index?: number) =>__RepeatItemFactoryReturn<T>;
|
||||||
|
private onMoveHandler_?: OnMoveHandler;
|
||||||
|
|
||||||
|
private key2Item_ = new Map<string, __RepeatItemInfo<T>>();
|
||||||
|
|
||||||
|
/**/
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**/
|
||||||
|
public render(config: __RepeatAPIConfig<T>, isInitialRender: boolean): void {
|
||||||
|
this.arr_ = config.arr;
|
||||||
|
this.itemGenFuncs_ = config.itemGenFuncs;
|
||||||
|
this.typeGenFunc_ = config.typeGenFunc;
|
||||||
|
this.keyGenFunction_ = config.keyGenFunc;
|
||||||
|
this.mkRepeatItem_ = config.mkRepeatItem;
|
||||||
|
this.onMoveHandler_ = config.onMoveHandler;
|
||||||
|
|
||||||
|
isInitialRender ? this.initialRender() : this.reRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
private genKeys(): Map<string, __RepeatItemInfo<T>> {
|
||||||
|
const key2Item = new Map<string, __RepeatItemInfo<T>>();
|
||||||
|
this.arr_.forEach((item, index) => {
|
||||||
|
const key = this.keyGenFunction_(item, index);
|
||||||
|
key2Item.set(key, { key, index })
|
||||||
|
});
|
||||||
|
if (key2Item.size < this.arr_.length) {
|
||||||
|
stateMgmtConsole.warn("__RepeatImpl: Duplicates detected, fallback to index-based keyGen.")
|
||||||
|
// Causes all items to be re-rendered
|
||||||
|
this.keyGenFunction_ = __RepeatDefaultKeyGen.funcWithIndex;
|
||||||
|
return this.genKeys();
|
||||||
|
}
|
||||||
|
return key2Item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private initialRender(): void {
|
||||||
|
//console.log('__RepeatImpl initialRender() 0')
|
||||||
|
this.key2Item_ = this.genKeys();
|
||||||
|
|
||||||
|
RepeatNative.startRender();
|
||||||
|
|
||||||
|
let index = 0;
|
||||||
|
this.key2Item_.forEach((itemInfo, key) => {
|
||||||
|
itemInfo.repeatItem = this.mkRepeatItem_(this.arr_[index], index);
|
||||||
|
this.initialRenderItem(key, itemInfo.repeatItem);
|
||||||
|
index++;
|
||||||
|
})
|
||||||
|
let removedChildElmtIds = new Array<number>();
|
||||||
|
// Fetch the removedChildElmtIds from C++ to unregister those elmtIds with UINodeRegisterProxy
|
||||||
|
RepeatNative.onMove(this.onMoveHandler_);
|
||||||
|
RepeatNative.finishRender(removedChildElmtIds);
|
||||||
|
UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs(removedChildElmtIds);
|
||||||
|
stateMgmtConsole.debug(`__RepeatImpl: initialRender elmtIds need unregister after repeat render: ${JSON.stringify(removedChildElmtIds)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private reRender(): void {
|
||||||
|
const oldKey2Item: Map<string, __RepeatItemInfo<T>> = this.key2Item_;
|
||||||
|
this.key2Item_ = this.genKeys();
|
||||||
|
|
||||||
|
// identify array items that have been deleted
|
||||||
|
// these are candidates for re-use
|
||||||
|
const deletedKeysAndIndex = new Array<__RepeatItemInfo<T>>();
|
||||||
|
for (const [key, feInfo] of oldKey2Item) {
|
||||||
|
if (!this.key2Item_.has(key)) {
|
||||||
|
deletedKeysAndIndex.push(feInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// C++: mv children_ aside to tempchildren_
|
||||||
|
RepeatNative.startRender();
|
||||||
|
|
||||||
|
let index = 0;
|
||||||
|
this.key2Item_.forEach((itemInfo, key) => {
|
||||||
|
const item = this.arr_[index];
|
||||||
|
let oldItemInfo = oldKey2Item.get(key);
|
||||||
|
|
||||||
|
if (oldItemInfo) {
|
||||||
|
// case #1 retained array item
|
||||||
|
// moved from oldIndex to index
|
||||||
|
const oldIndex = oldItemInfo.index;
|
||||||
|
itemInfo.repeatItem = oldItemInfo!.repeatItem!;
|
||||||
|
stateMgmtConsole.debug(`__RepeatImpl: retained: key ${key} ${oldIndex}->${index}`)
|
||||||
|
itemInfo.repeatItem.updateIndex(index);
|
||||||
|
// C++ mv from tempChildren[oldIndex] to end of children_
|
||||||
|
RepeatNative.moveChild(oldIndex);
|
||||||
|
|
||||||
|
// TBD moveChild() only when item types are same
|
||||||
|
//const type0 = this.typeGenFunc_(oldItemInfo.repeatItem.item, oldIndex);
|
||||||
|
//const type1 = this.typeGenFunc_(itemInfo.repeatItem.item, index);
|
||||||
|
//if (type0 == type1) {
|
||||||
|
// // C++ mv from tempChildren[oldIndex] to end of children_
|
||||||
|
// RepeatNative.moveChild(oldIndex);
|
||||||
|
//} else {
|
||||||
|
// this.initialRenderItem(key, itemInfo.repeatItem);
|
||||||
|
//}
|
||||||
|
} else if (deletedKeysAndIndex.length) {
|
||||||
|
// case #2:
|
||||||
|
// new array item, there is an deleted array items whose
|
||||||
|
// UINode children cab re-used
|
||||||
|
const oldItemInfo = deletedKeysAndIndex.pop();
|
||||||
|
const reuseKey = oldItemInfo!.key;
|
||||||
|
const oldKeyIndex = oldItemInfo!.index;
|
||||||
|
const oldRepeatItem = oldItemInfo!.repeatItem!;
|
||||||
|
itemInfo.repeatItem = oldRepeatItem;
|
||||||
|
stateMgmtConsole.debug(`__RepeatImpl: new: key ${key} reuse key ${reuseKey} ${oldKeyIndex}->${index}`)
|
||||||
|
|
||||||
|
itemInfo.repeatItem.updateItem(item);
|
||||||
|
itemInfo.repeatItem.updateIndex(index);
|
||||||
|
|
||||||
|
// update key2item_ Map
|
||||||
|
this.key2Item_.set(key, itemInfo);
|
||||||
|
|
||||||
|
// TBD moveChild() only when item types are same
|
||||||
|
|
||||||
|
// C++ mv from tempChildren[oldIndex] to end of children_
|
||||||
|
RepeatNative.moveChild(oldKeyIndex);
|
||||||
|
} else {
|
||||||
|
// case #3:
|
||||||
|
// new array item, there are no deleted array items
|
||||||
|
// render new UINode children
|
||||||
|
itemInfo.repeatItem = this.mkRepeatItem_(item, index);
|
||||||
|
this.initialRenderItem(key, itemInfo.repeatItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
})
|
||||||
|
|
||||||
|
// keep this.id2item_. by removing all entries for remaining
|
||||||
|
// deleted items
|
||||||
|
deletedKeysAndIndex.forEach(delItem => {
|
||||||
|
this.key2Item_.delete(delItem!.key);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Finish up for.each update
|
||||||
|
// C++ tempChildren.clear() , trigger re-layout
|
||||||
|
let removedChildElmtIds = new Array<number>();
|
||||||
|
// Fetch the removedChildElmtIds from C++ to unregister those elmtIds with UINodeRegisterProxy
|
||||||
|
RepeatNative.onMove(this.onMoveHandler_);
|
||||||
|
RepeatNative.finishRender(removedChildElmtIds);
|
||||||
|
UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs(removedChildElmtIds);
|
||||||
|
stateMgmtConsole.debug(`__RepeatImpl: reRender elmtIds need unregister after repeat render: ${JSON.stringify(removedChildElmtIds)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private initialRenderItem(key: string, repeatItem: __RepeatItemFactoryReturn<T>): void {
|
||||||
|
//console.log('__RepeatImpl initialRenderItem()')
|
||||||
|
// render new UINode children
|
||||||
|
stateMgmtConsole.debug(`__RepeatImpl: new: key ${key} n/a->${repeatItem.index}`)
|
||||||
|
|
||||||
|
// C++: initial render will render to the end of children_
|
||||||
|
RepeatNative.createNewChildStart(key);
|
||||||
|
|
||||||
|
// execute the itemGen function
|
||||||
|
const itemType = this.typeGenFunc_(repeatItem.item, repeatItem.index) ?? '';
|
||||||
|
const itemFunc = this.itemGenFuncs_[itemType] ?? this.itemGenFuncs_[''];
|
||||||
|
itemFunc(repeatItem);
|
||||||
|
|
||||||
|
RepeatNative.createNewChildFinish(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023-2024 Huawei Device Co., Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* all definitions in this file are framework internal
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Implements ForEach with child re-use for both existing state observation and
|
||||||
|
// deep observation. For virtual-scroll code paths
|
||||||
|
|
||||||
|
class __RepeatVirtualScrollImpl<T> {
|
||||||
|
private arr_: Array<T>;
|
||||||
|
private itemGenFuncs_: { [type: string]: RepeatItemGenFunc<T> };
|
||||||
|
private keyGenFunc_?: RepeatKeyGenFunc<T>;
|
||||||
|
private typeGenFunc_: RepeatTypeGenFunc<T>;
|
||||||
|
//
|
||||||
|
private totalCount_: number;
|
||||||
|
private templateOptions_: { [type: string]: RepeatTemplateOptions };
|
||||||
|
//
|
||||||
|
private mkRepeatItem_: (item: T, index?: number) =>__RepeatItemFactoryReturn<T>;
|
||||||
|
private onMoveHandler_?: OnMoveHandler;
|
||||||
|
|
||||||
|
/**/
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(config: __RepeatAPIConfig<T>, isInitialRender: boolean): void {
|
||||||
|
this.arr_ = config.arr;
|
||||||
|
this.itemGenFuncs_ = config.itemGenFuncs;
|
||||||
|
this.keyGenFunc_ = config.keyGenFunc;
|
||||||
|
this.typeGenFunc_ = config.typeGenFunc;
|
||||||
|
|
||||||
|
this.totalCount_ = config.totalCount;
|
||||||
|
this.templateOptions_ = config.templateOptions;
|
||||||
|
|
||||||
|
this.mkRepeatItem_ = config.mkRepeatItem;
|
||||||
|
this.onMoveHandler_ = config.onMoveHandler;
|
||||||
|
|
||||||
|
if (isInitialRender) {
|
||||||
|
this.initialRender(ObserveV2.getCurrentRecordedId());
|
||||||
|
} else {
|
||||||
|
this.reRender();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**/
|
||||||
|
private initialRender(repeatElmtId: number): void {
|
||||||
|
// Map key -> RepeatItem
|
||||||
|
// added to closure of following lambdas
|
||||||
|
const _repeatItem4Key = new Map<string, __RepeatItemFactoryReturn<T>>();
|
||||||
|
const repeatElmtId1 = repeatElmtId;
|
||||||
|
|
||||||
|
const onCreateNode = (forIndex: number): void => {
|
||||||
|
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl onCreateNode index ${forIndex} - start`);
|
||||||
|
if (forIndex < 0) {
|
||||||
|
// FIXME check also index < totalCount
|
||||||
|
throw new Error(`__RepeatVirtualScrollImpl onCreateNode: for index=${forIndex} out of range error.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create dependency array item [forIndex] -> Repeat
|
||||||
|
// so Repeat updates when the array item changes
|
||||||
|
// FIXME observe dependencies, adding the array is insurgent for Array of objects
|
||||||
|
ObserveV2.getObserve().addRef4Id(repeatElmtId1, this.arr_, forIndex.toString());
|
||||||
|
|
||||||
|
const repeatItem = this.mkRepeatItem_(this.arr_[forIndex], forIndex);
|
||||||
|
const forKey = this.keyGenFunc_(this.arr_[forIndex], forIndex);
|
||||||
|
_repeatItem4Key.set(forKey, repeatItem);
|
||||||
|
|
||||||
|
// execute the itemGen function
|
||||||
|
this.initialRenderItem(repeatItem);
|
||||||
|
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl onCreateNode for index ${forIndex} key "${forKey}" - end `);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onUpdateNode = (fromKey: string, forIndex: number): void => {
|
||||||
|
if (!fromKey || fromKey=="" || forIndex < 0) {
|
||||||
|
// FIXME check also index < totalCount
|
||||||
|
throw new Error(`__RepeatVirtualScrollImpl onUpdateNode: fromKey "${fromKey}", forIndex=${forIndex} invalid function input error.`)
|
||||||
|
}
|
||||||
|
// create dependency array item [forIndex] -> Repeat
|
||||||
|
// so Repeat updates when the array item changes
|
||||||
|
// FIXME observe dependencies, adding the array is insurgent for Array of objects
|
||||||
|
ObserveV2.getObserve().addRef4Id(repeatElmtId1, this.arr_, forIndex.toString());
|
||||||
|
const repeatItem = _repeatItem4Key.get(fromKey);
|
||||||
|
if (!repeatItem) {
|
||||||
|
stateMgmtConsole.error(`__RepeatVirtualScrollImpl onUpdateNode: fromKey "${fromKey}", forIndex=${forIndex}, can not find RepeatItem for key. Unrecoverable error`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const forKey= this.keyGenFunc_(this.arr_[forIndex], forIndex);
|
||||||
|
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl onUpdateNode: fromKey "${fromKey}", forIndex=${forIndex} forKey="${forKey}". Updating RepeatItem ...`);
|
||||||
|
repeatItem.updateItem(this.arr_[forIndex]);
|
||||||
|
repeatItem.updateIndex(forIndex);
|
||||||
|
|
||||||
|
// update Map according to made update:
|
||||||
|
// del fromKey entry and add forKey
|
||||||
|
_repeatItem4Key.delete(fromKey);
|
||||||
|
_repeatItem4Key.set(forKey, repeatItem);
|
||||||
|
|
||||||
|
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl onUpdateNode: fromKey "${fromKey}", forIndex=${forIndex} forKey="${forKey}". Initiating update synchronously (TODO)...`);
|
||||||
|
// FIXME request re-render right away!
|
||||||
|
ObserveV2.getObserve().updateDirty2(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onGetKeys4Range = (from: number, to: number): Array<string> => {
|
||||||
|
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl: onGetKeys4Range from ${from} to ${to} - start`);
|
||||||
|
|
||||||
|
const result = new Array<string>();
|
||||||
|
for (let i = from; i <= to && i < this.arr_.length; i++) {
|
||||||
|
// create dependency array item [i] -> Repeat
|
||||||
|
// so Repeat updates when the array item changes
|
||||||
|
// FIXME observe dependencies, adding the array is insurgent for Array of objects
|
||||||
|
ObserveV2.getObserve().addRef4Id(repeatElmtId1, this.arr_, i.toString());
|
||||||
|
result.push(this.keyGenFunc_(this.arr_[i], i));
|
||||||
|
}
|
||||||
|
|
||||||
|
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl: onGetKeys4Range from ${from} to ${to} - returns ${result.toString()}`);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onGetTypes4Range = (from: number, to: number): Array<string> => {
|
||||||
|
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl: onGetTypes4Range from ${from} to ${to} - start`);
|
||||||
|
const result = new Array<string>();
|
||||||
|
// FIXME observe dependencies, adding the array is insurgent for Array of objects
|
||||||
|
for (let i = from; i <= to && i < this.arr_.length; i++) {
|
||||||
|
// ObserveV2.getObserve().addRef4Id(repeatElmtId1, this.arr_, i.toString());
|
||||||
|
result.push(this.typeGenFunc_(this.arr_[i], i) ?? '');
|
||||||
|
}
|
||||||
|
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl: onGetTypes4Range from ${from} to ${to} - returns ${result.toString()}`);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl: initialRenderVirtualScroll`);
|
||||||
|
|
||||||
|
RepeatVirtualScrollNative.create(this.totalCount_, Object.entries(this.templateOptions_), {
|
||||||
|
onCreateNode,
|
||||||
|
onUpdateNode,
|
||||||
|
onGetKeys4Range,
|
||||||
|
onGetTypes4Range
|
||||||
|
});
|
||||||
|
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl: initialRenderVirtualScroll 2`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private reRender() {
|
||||||
|
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl: reRender ...`);
|
||||||
|
RepeatVirtualScrollNative.invalidateKeyCache(this.totalCount_);
|
||||||
|
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl: reRender - done`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private initialRenderItem(repeatItem: __RepeatItemFactoryReturn<T>): void {
|
||||||
|
// execute the itemGen function
|
||||||
|
const itemType = this.typeGenFunc_(repeatItem.item, repeatItem.index) ?? '';
|
||||||
|
const itemFunc = this.itemGenFuncs_[itemType] ?? this.itemGenFuncs_[''];
|
||||||
|
itemFunc(repeatItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
@ -23,7 +23,9 @@ interface RepeatItem<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RepeatItemGenFunc<T> = (i: RepeatItem<T>) => void;
|
type RepeatItemGenFunc<T> = (i: RepeatItem<T>) => void;
|
||||||
|
type RepeatTypeGenFunc<T> = (item: T, index: number) => string;
|
||||||
type RepeatKeyGenFunc<T> = (item: T, index?: number) => string;
|
type RepeatKeyGenFunc<T> = (item: T, index?: number) => string;
|
||||||
|
type RepeatTemplateOptions = { cachedCount?: number };
|
||||||
type OnMoveHandler = (from: number, to: number) => void;
|
type OnMoveHandler = (from: number, to: number) => void;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -43,11 +45,26 @@ const Repeat: <T>(arr: Array<T>, owningView?: PUV2ViewBase) => RepeatAPI<T> =
|
|||||||
repeat attribute function and internal function render()
|
repeat attribute function and internal function render()
|
||||||
*/
|
*/
|
||||||
interface RepeatAPI<T> {
|
interface RepeatAPI<T> {
|
||||||
each: (itemGenFunc: RepeatItemGenFunc<T>) => RepeatAPI<T>; // chainable, call in this order
|
// unique key from array item, to identify changed, added array items
|
||||||
key: (keyGenFunc: RepeatKeyGenFunc<T>) => RepeatAPI<T>;
|
key: (keyGenFunc: RepeatKeyGenFunc<T>) => RepeatAPI<T>;
|
||||||
virtualScroll: () => RepeatAPI<T>;
|
|
||||||
onMove: (handler: OnMoveHandler) => RepeatAPI<T>;
|
// default render function for data items, framework will use when no template found
|
||||||
|
each: (itemGenFunc: RepeatItemGenFunc<T>) => RepeatAPI<T>;
|
||||||
|
|
||||||
|
// when call virtualScroll, framework will use virtual scroll
|
||||||
|
// totalCount: number of logical items, can be larger than number of loaded
|
||||||
|
// data items of lazy loading array source, can be larger than that array.length
|
||||||
|
virtualScroll: (options?: { totalCount?: number }) => RepeatAPI<T>;
|
||||||
|
|
||||||
|
// function to decide which template to use, each template has an id
|
||||||
|
templateId: (typeFunc: RepeatTypeGenFunc<T>) => RepeatAPI<T>;
|
||||||
|
|
||||||
|
// template: id + builder function to render specific type of data item
|
||||||
|
template: (type: string, itemGenFunc: RepeatItemGenFunc<T>, options?: RepeatTemplateOptions) => RepeatAPI<T>;
|
||||||
|
|
||||||
// do NOT use in app, transpiler adds as last of chained call
|
// do NOT use in app, transpiler adds as last of chained call
|
||||||
render(isInitialRender: boolean): void;
|
render(isInitialRender: boolean): void;
|
||||||
|
|
||||||
|
// not used by Repeat
|
||||||
|
onMove: (handler: OnMoveHandler) => RepeatAPI<T>;
|
||||||
}
|
}
|
||||||
|
@ -125,6 +125,11 @@ class ObserveV2 {
|
|||||||
return (value && typeof (value) === 'object' && value[ObserveV2.V2_DECO_META]);
|
return (value && typeof (value) === 'object' && value[ObserveV2.V2_DECO_META]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static getCurrentRecordedId(): number {
|
||||||
|
const bound = ObserveV2.getObserve().stackOfRenderedComponents_.top();
|
||||||
|
return bound ? bound[0] : -1;
|
||||||
|
}
|
||||||
|
|
||||||
// At the start of observeComponentCreation or
|
// At the start of observeComponentCreation or
|
||||||
// MonitorV2 observeObjectAccess
|
// MonitorV2 observeObjectAccess
|
||||||
public startRecordDependencies(cmp: IView | MonitorV2 | ComputedV2 | PersistenceV2Impl, id: number): void {
|
public startRecordDependencies(cmp: IView | MonitorV2 | ComputedV2 | PersistenceV2Impl, id: number): void {
|
||||||
@ -291,8 +296,15 @@ class ObserveV2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stateMgmtConsole.propertyAccess(`ObserveV2.addRef '${attrName}' for id ${bound[0]}...`);
|
stateMgmtConsole.propertyAccess(`ObserveV2.addRef '${attrName}' for id ${bound[0]}...`);
|
||||||
const id = bound[0];
|
this.addRef4IdInternal(bound[0], target, attrName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public addRef4Id(id: number, target: object, attrName: string): void {
|
||||||
|
stateMgmtConsole.propertyAccess(`ObserveV2.addRef4Id '${attrName}' for id ${id} ...`);
|
||||||
|
this.addRef4IdInternal(id, target, attrName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private addRef4IdInternal(id: number, target: object, attrName: string): void {
|
||||||
// Map: attribute/symbol -> dependent id
|
// Map: attribute/symbol -> dependent id
|
||||||
const symRefs = target[ObserveV2.SYMBOL_REFS] ??= {};
|
const symRefs = target[ObserveV2.SYMBOL_REFS] ??= {};
|
||||||
symRefs[attrName] ??= new Set();
|
symRefs[attrName] ??= new Set();
|
||||||
@ -411,15 +423,29 @@ class ObserveV2 {
|
|||||||
} // for
|
} // for
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateDirty(): void {
|
public updateDirty(): void {
|
||||||
this.startDirty_ = true;
|
this.startDirty_ = true;
|
||||||
this.updateDirty2();
|
this.updateDirty2(false);
|
||||||
this.startDirty_ = false;
|
this.startDirty_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateDirty2(): void {
|
/**
|
||||||
|
* execute /update in this order
|
||||||
|
* - @Computed variables
|
||||||
|
* - @Monitor functions
|
||||||
|
* - UINode re-render
|
||||||
|
* three nested loops, means:
|
||||||
|
* process @Computed until no more @Computed need update
|
||||||
|
* process @Monitor until no more @Computed and @Monitor
|
||||||
|
* process UINode update until no more @Computed and @Monitor and UINode rerender
|
||||||
|
*
|
||||||
|
* @param updateUISynchronously should be set to true if called during VSYNC only
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
public updateDirty2(updateUISynchronously: boolean = false): void {
|
||||||
aceTrace.begin('updateDirty2');
|
aceTrace.begin('updateDirty2');
|
||||||
stateMgmtConsole.debug(`ObservedV3.updateDirty2 ... `);
|
stateMgmtConsole.debug(`ObservedV3.updateDirty2 updateUISynchronously=${updateUISynchronously} ... `);
|
||||||
// obtain and unregister the removed elmtIds
|
// obtain and unregister the removed elmtIds
|
||||||
UINodeRegisterProxy.obtainDeletedElmtIds();
|
UINodeRegisterProxy.obtainDeletedElmtIds();
|
||||||
UINodeRegisterProxy.unregisterElmtIdsFromIViews();
|
UINodeRegisterProxy.unregisterElmtIdsFromIViews();
|
||||||
@ -456,9 +482,11 @@ class ObserveV2 {
|
|||||||
if (this.elmtIdsChanged_.size) {
|
if (this.elmtIdsChanged_.size) {
|
||||||
const elmtIds = Array.from(this.elmtIdsChanged_).sort((elmtId1, elmtId2) => elmtId1 - elmtId2);
|
const elmtIds = Array.from(this.elmtIdsChanged_).sort((elmtId1, elmtId2) => elmtId1 - elmtId2);
|
||||||
this.elmtIdsChanged_ = new Set<number>();
|
this.elmtIdsChanged_ = new Set<number>();
|
||||||
this.updateUINodes(elmtIds);
|
updateUISynchronously ? this.updateUINodesSynchronously(elmtIds) : this.updateUINodes(elmtIds);
|
||||||
}
|
}
|
||||||
} while (this.elmtIdsChanged_.size + this.monitorIdsChanged_.size + this.computedPropIdsChanged_.size > 0);
|
} while (this.elmtIdsChanged_.size + this.monitorIdsChanged_.size + this.computedPropIdsChanged_.size > 0);
|
||||||
|
|
||||||
|
stateMgmtConsole.debug(`ObservedV3.updateDirty2 updateUISynchronously=${updateUISynchronously} - DONE `);
|
||||||
aceTrace.end();
|
aceTrace.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,10 +536,11 @@ class ObserveV2 {
|
|||||||
* FlushDirtyNodesUpdate to CustomNode to ViewV2.updateDirtyElements to UpdateElement
|
* FlushDirtyNodesUpdate to CustomNode to ViewV2.updateDirtyElements to UpdateElement
|
||||||
* Code left here to reproduce benchmark measurements, compare with future optimisation
|
* Code left here to reproduce benchmark measurements, compare with future optimisation
|
||||||
* @param elmtIds
|
* @param elmtIds
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
private updateUINodesWithoutVSync(elmtIds: Array<number>): void {
|
private updateUINodesSynchronously(elmtIds: Array<number>): void {
|
||||||
stateMgmtConsole.debug(`ObserveV2.updateUINodes: ${elmtIds.length} elmtIds: ${JSON.stringify(elmtIds)} ...`);
|
stateMgmtConsole.debug(`ObserveV2.updateUINodesSynchronously: ${elmtIds.length} elmtIds: ${JSON.stringify(elmtIds)} ...`);
|
||||||
aceTrace.begin(`ObserveV2.updateUINodes: ${elmtIds.length} elmtId`);
|
aceTrace.begin(`ObserveV2.updateUINodesSynchronously: ${elmtIds.length} elmtId`);
|
||||||
let view: Object;
|
let view: Object;
|
||||||
let weak: any;
|
let weak: any;
|
||||||
elmtIds.forEach((elmtId) => {
|
elmtIds.forEach((elmtId) => {
|
||||||
@ -534,8 +563,8 @@ class ObserveV2 {
|
|||||||
// On next VSYNC runs FlushDirtyNodesUpdate to call rerender to call UpdateElement. Much longer code path
|
// On next VSYNC runs FlushDirtyNodesUpdate to call rerender to call UpdateElement. Much longer code path
|
||||||
// much slower
|
// much slower
|
||||||
private updateUINodes(elmtIds: Array<number>): void {
|
private updateUINodes(elmtIds: Array<number>): void {
|
||||||
stateMgmtConsole.debug(`ObserveV2.updateUINodesSlow: ${elmtIds.length} elmtIds need rerender: ${JSON.stringify(elmtIds)} ...`);
|
stateMgmtConsole.debug(`ObserveV2.updateUINodes: ${elmtIds.length} elmtIds need rerender: ${JSON.stringify(elmtIds)} ...`);
|
||||||
aceTrace.begin(`ObserveV2.updateUINodesSlow: ${elmtIds.length} elmtId`);
|
aceTrace.begin(`ObserveV2.updateUINodes: ${elmtIds.length} elmtId`);
|
||||||
let viewWeak: WeakRef<Object>;
|
let viewWeak: WeakRef<Object>;
|
||||||
let view: Object | undefined;
|
let view: Object | undefined;
|
||||||
elmtIds.forEach((elmtId) => {
|
elmtIds.forEach((elmtId) => {
|
||||||
@ -874,5 +903,3 @@ const trackInternal = (
|
|||||||
// used by IsObservedObjectV2
|
// used by IsObservedObjectV2
|
||||||
target[ObserveV2.V2_DECO_META] ??= {};
|
target[ObserveV2.V2_DECO_META] ??= {};
|
||||||
}; // trackInternal
|
}; // trackInternal
|
||||||
|
|
||||||
|
|
||||||
|
@ -88,6 +88,8 @@
|
|||||||
|
|
||||||
// partial_update specific
|
// partial_update specific
|
||||||
"src/lib/partial_update/pu_repeat.ts",
|
"src/lib/partial_update/pu_repeat.ts",
|
||||||
|
"src/lib/partial_update/pu_repeat_impl.ts",
|
||||||
|
"src/lib/partial_update/pu_repeat_virtual_scroll_impl.ts",
|
||||||
|
|
||||||
// sdk
|
// sdk
|
||||||
"src/lib/sdk/v2_persistence.ts",
|
"src/lib/sdk/v2_persistence.ts",
|
||||||
|
@ -56,7 +56,6 @@
|
|||||||
"src/lib/full_update/fu_synced_property_object_nested.ts",
|
"src/lib/full_update/fu_synced_property_object_nested.ts",
|
||||||
"src/lib/full_update/fu_view.ts",
|
"src/lib/full_update/fu_view.ts",
|
||||||
|
|
||||||
|
|
||||||
// partial update and v2 common
|
// partial update and v2 common
|
||||||
"src/lib/puv2_common/puv2_viewstack_processor.d.ts",
|
"src/lib/puv2_common/puv2_viewstack_processor.d.ts",
|
||||||
"src/lib/puv2_common/iview.ts",
|
"src/lib/puv2_common/iview.ts",
|
||||||
@ -77,6 +76,8 @@
|
|||||||
"src/lib/partial_update/pu_view.ts",
|
"src/lib/partial_update/pu_view.ts",
|
||||||
"src/lib/partial_update/pu_recycle_manager.ts",
|
"src/lib/partial_update/pu_recycle_manager.ts",
|
||||||
"src/lib/partial_update/pu_builder_proxy.ts",
|
"src/lib/partial_update/pu_builder_proxy.ts",
|
||||||
|
"src/lib/partial_update/pu_repeat_impl.ts",
|
||||||
|
"src/lib/partial_update/pu_repeat_virtual_scroll_impl.ts",
|
||||||
|
|
||||||
// 'new' stateMgmt vers 2
|
// 'new' stateMgmt vers 2
|
||||||
"src/lib/v2/v2_change_observation.ts",
|
"src/lib/v2/v2_change_observation.ts",
|
||||||
|
@ -88,6 +88,8 @@
|
|||||||
|
|
||||||
// partial_update specific
|
// partial_update specific
|
||||||
"src/lib/partial_update/pu_repeat.ts",
|
"src/lib/partial_update/pu_repeat.ts",
|
||||||
|
"src/lib/partial_update/pu_repeat_impl.ts",
|
||||||
|
"src/lib/partial_update/pu_repeat_virtual_scroll_impl.ts",
|
||||||
|
|
||||||
// sdk
|
// sdk
|
||||||
"src/lib/sdk/v2_persistence.ts",
|
"src/lib/sdk/v2_persistence.ts",
|
||||||
|
@ -76,6 +76,8 @@
|
|||||||
|
|
||||||
// partial_update specific
|
// partial_update specific
|
||||||
"src/lib/partial_update/pu_repeat.ts",
|
"src/lib/partial_update/pu_repeat.ts",
|
||||||
|
"src/lib/partial_update/pu_repeat_impl.ts",
|
||||||
|
"src/lib/partial_update/pu_repeat_virtual_scroll_impl.ts",
|
||||||
|
|
||||||
// sdk
|
// sdk
|
||||||
"src/lib/sdk/v2_persistence.ts",
|
"src/lib/sdk/v2_persistence.ts",
|
||||||
|
@ -56,6 +56,7 @@
|
|||||||
#include "core/components_ng/property/property.h"
|
#include "core/components_ng/property/property.h"
|
||||||
#include "core/components_ng/render/paint_wrapper.h"
|
#include "core/components_ng/render/paint_wrapper.h"
|
||||||
#include "core/components_ng/syntax/lazy_for_each_node.h"
|
#include "core/components_ng/syntax/lazy_for_each_node.h"
|
||||||
|
#include "core/components_ng/syntax/repeat_virtual_scroll_node.h"
|
||||||
#include "core/components_v2/inspector/inspector_constants.h"
|
#include "core/components_v2/inspector/inspector_constants.h"
|
||||||
#include "core/event/touch_event.h"
|
#include "core/event/touch_event.h"
|
||||||
#include "core/gestures/gesture_info.h"
|
#include "core/gestures/gesture_info.h"
|
||||||
@ -142,9 +143,7 @@ public:
|
|||||||
cursor_ = children_.end();
|
cursor_ = children_.end();
|
||||||
if (prevFrameProxy_->cursor_ != prevFrameProxy_->children_.end()) {
|
if (prevFrameProxy_->cursor_ != prevFrameProxy_->children_.end()) {
|
||||||
cursor_ = std::find_if(children_.begin(), children_.end(),
|
cursor_ = std::find_if(children_.begin(), children_.end(),
|
||||||
[this](FrameChildNode& node) {
|
[this](FrameChildNode& node) { return prevFrameProxy_->cursor_->node == node.node; });
|
||||||
return prevFrameProxy_->cursor_->node == node.node;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,8 +177,11 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto lazyForEachNode = AceType::DynamicCast<LazyForEachNode>(UiNode);
|
auto lazyForEachNode = AceType::DynamicCast<LazyForEachNode>(UiNode);
|
||||||
|
auto repeatVirtualScrollNode = AceType::DynamicCast<RepeatVirtualScrollNode>(UiNode);
|
||||||
if (lazyForEachNode) {
|
if (lazyForEachNode) {
|
||||||
lazyForEachNode->BuildAllChildren();
|
lazyForEachNode->BuildAllChildren();
|
||||||
|
} else if (repeatVirtualScrollNode) {
|
||||||
|
TAG_LOGE(AceLogTag::ACE_REPEAT, "repeatVirtualScroll not support in non scoll container!");
|
||||||
} else {
|
} else {
|
||||||
auto customNode = AceType::DynamicCast<CustomNode>(UiNode);
|
auto customNode = AceType::DynamicCast<CustomNode>(UiNode);
|
||||||
if (customNode) {
|
if (customNode) {
|
||||||
@ -225,9 +227,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cursor_->startIndex + cursor_->count > index) {
|
if (cursor_->startIndex + cursor_->count > index) {
|
||||||
auto frameNode = AceType::DynamicCast<FrameNode>(
|
auto frameNode = AceType::DynamicCast<FrameNode>(cursor_->node->GetFrameChildByIndex(
|
||||||
cursor_->node->GetFrameChildByIndex(index - cursor_->startIndex,
|
index - cursor_->startIndex, needBuild, isCache, addToRenderTree));
|
||||||
needBuild, isCache, addToRenderTree));
|
|
||||||
return frameNode;
|
return frameNode;
|
||||||
}
|
}
|
||||||
cursor_++;
|
cursor_++;
|
||||||
@ -272,8 +273,8 @@ public:
|
|||||||
void ResetChildren(bool needResetChild = false)
|
void ResetChildren(bool needResetChild = false)
|
||||||
{
|
{
|
||||||
if (inUse_) {
|
if (inUse_) {
|
||||||
LOGF("[%{public}d:%{public}s] reset children while in use",
|
LOGF(
|
||||||
hostNode_->GetId(), hostNode_->GetTag().c_str());
|
"[%{public}d:%{public}s] reset children while in use", hostNode_->GetId(), hostNode_->GetTag().c_str());
|
||||||
if (SystemProperties::GetLayoutDetectEnabled()) {
|
if (SystemProperties::GetLayoutDetectEnabled()) {
|
||||||
abort();
|
abort();
|
||||||
} else {
|
} else {
|
||||||
@ -320,12 +321,11 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetActiveChildRange(int32_t start, int32_t end)
|
void SetActiveChildRange(int32_t start, int32_t end, int32_t cacheStart, int32_t cacheEnd)
|
||||||
{
|
{
|
||||||
for (auto itor = partFrameNodeChildren_.begin(); itor != partFrameNodeChildren_.end();) {
|
for (auto itor = partFrameNodeChildren_.begin(); itor != partFrameNodeChildren_.end();) {
|
||||||
int32_t index = itor->first;
|
int32_t index = itor->first;
|
||||||
if ((start <= end && index >= start && index <= end) ||
|
if ((start <= end && index >= start && index <= end) || (start > end && (index <= end || start <= index))) {
|
||||||
(start > end && (index <= end || start <= index))) {
|
|
||||||
itor++;
|
itor++;
|
||||||
} else {
|
} else {
|
||||||
partFrameNodeChildren_.erase(itor++);
|
partFrameNodeChildren_.erase(itor++);
|
||||||
@ -333,7 +333,7 @@ public:
|
|||||||
}
|
}
|
||||||
auto guard = GetGuard();
|
auto guard = GetGuard();
|
||||||
for (const auto& child : children_) {
|
for (const auto& child : children_) {
|
||||||
child.node->DoSetActiveChildRange(start - child.startIndex, end - child.startIndex);
|
child.node->DoSetActiveChildRange(start - child.startIndex, end - child.startIndex, cacheStart, cacheEnd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -652,11 +652,11 @@ void FrameNode::DumpSafeAreaInfo()
|
|||||||
auto manager = pipeline->GetSafeAreaManager();
|
auto manager = pipeline->GetSafeAreaManager();
|
||||||
CHECK_NULL_VOID(manager);
|
CHECK_NULL_VOID(manager);
|
||||||
DumpLog::GetInstance().AddDesc(std::string("ignoreSafeArea: ")
|
DumpLog::GetInstance().AddDesc(std::string("ignoreSafeArea: ")
|
||||||
.append(std::to_string(manager->IsIgnoreAsfeArea()))
|
.append(std::to_string(manager->IsIgnoreAsfeArea()))
|
||||||
.append(std::string(", isNeedAvoidWindow: ").c_str())
|
.append(std::string(", isNeedAvoidWindow: ").c_str())
|
||||||
.append(std::to_string(manager->IsNeedAvoidWindow()))
|
.append(std::to_string(manager->IsNeedAvoidWindow()))
|
||||||
.append(std::string(", isFullScreen: ").c_str())
|
.append(std::string(", isFullScreen: ").c_str())
|
||||||
.append(std::to_string(manager->IsFullScreen())));
|
.append(std::to_string(manager->IsFullScreen())));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameNode::DumpAlignRulesInfo()
|
void FrameNode::DumpAlignRulesInfo()
|
||||||
@ -665,8 +665,7 @@ void FrameNode::DumpAlignRulesInfo()
|
|||||||
CHECK_NULL_VOID(flexItemProperties);
|
CHECK_NULL_VOID(flexItemProperties);
|
||||||
auto rulesToString = flexItemProperties->AlignRulesToString();
|
auto rulesToString = flexItemProperties->AlignRulesToString();
|
||||||
CHECK_NULL_VOID(!rulesToString.empty());
|
CHECK_NULL_VOID(!rulesToString.empty());
|
||||||
DumpLog::GetInstance().AddDesc(std::string("AlignRules: ")
|
DumpLog::GetInstance().AddDesc(std::string("AlignRules: ").append(rulesToString));
|
||||||
.append(rulesToString));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameNode::DumpExtensionHandlerInfo()
|
void FrameNode::DumpExtensionHandlerInfo()
|
||||||
@ -674,11 +673,10 @@ void FrameNode::DumpExtensionHandlerInfo()
|
|||||||
if (!extensionHandler_) {
|
if (!extensionHandler_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DumpLog::GetInstance().AddDesc(
|
DumpLog::GetInstance().AddDesc(std::string("ExtensionHandler: HasCustomerMeasure: ")
|
||||||
std::string("ExtensionHandler: HasCustomerMeasure: ")
|
.append(extensionHandler_->HasCustomerMeasure() ? "true" : "false")
|
||||||
.append(extensionHandler_->HasCustomerMeasure() ? "true" : "false")
|
.append(", HasCustomerLayout: ")
|
||||||
.append(", HasCustomerLayout: ")
|
.append(extensionHandler_->HasCustomerLayout() ? "true" : "false"));
|
||||||
.append(extensionHandler_->HasCustomerLayout() ? "true" : "false"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameNode::DumpCommonInfo()
|
void FrameNode::DumpCommonInfo()
|
||||||
@ -692,7 +690,7 @@ void FrameNode::DumpCommonInfo()
|
|||||||
}
|
}
|
||||||
if (geometryNode_->GetParentLayoutConstraint().has_value())
|
if (geometryNode_->GetParentLayoutConstraint().has_value())
|
||||||
DumpLog::GetInstance().AddDesc(std::string("ParentLayoutConstraint: ")
|
DumpLog::GetInstance().AddDesc(std::string("ParentLayoutConstraint: ")
|
||||||
.append(geometryNode_->GetParentLayoutConstraint().value().ToString()));
|
.append(geometryNode_->GetParentLayoutConstraint().value().ToString()));
|
||||||
if (!(NearZero(GetOffsetRelativeToWindow().GetY()) && NearZero(GetOffsetRelativeToWindow().GetX()))) {
|
if (!(NearZero(GetOffsetRelativeToWindow().GetY()) && NearZero(GetOffsetRelativeToWindow().GetX()))) {
|
||||||
DumpLog::GetInstance().AddDesc(std::string("top: ")
|
DumpLog::GetInstance().AddDesc(std::string("top: ")
|
||||||
.append(std::to_string(GetOffsetRelativeToWindow().GetY()))
|
.append(std::to_string(GetOffsetRelativeToWindow().GetY()))
|
||||||
@ -738,8 +736,9 @@ void FrameNode::DumpCommonInfo()
|
|||||||
layoutProperty_->GetMarginProperty() || layoutProperty_->GetCalcLayoutConstraint()) {
|
layoutProperty_->GetMarginProperty() || layoutProperty_->GetCalcLayoutConstraint()) {
|
||||||
DumpLog::GetInstance().AddDesc(
|
DumpLog::GetInstance().AddDesc(
|
||||||
std::string("ContentConstraint: ")
|
std::string("ContentConstraint: ")
|
||||||
.append(layoutProperty_->GetContentLayoutConstraint().has_value() ?
|
.append(layoutProperty_->GetContentLayoutConstraint().has_value()
|
||||||
layoutProperty_->GetContentLayoutConstraint().value().ToString() : "NA"));
|
? layoutProperty_->GetContentLayoutConstraint().value().ToString()
|
||||||
|
: "NA"));
|
||||||
}
|
}
|
||||||
DumpAlignRulesInfo();
|
DumpAlignRulesInfo();
|
||||||
DumpDragInfo();
|
DumpDragInfo();
|
||||||
@ -753,11 +752,11 @@ void FrameNode::DumpDragInfo()
|
|||||||
{
|
{
|
||||||
DumpLog::GetInstance().AddDesc("------------start print dragInfo");
|
DumpLog::GetInstance().AddDesc("------------start print dragInfo");
|
||||||
DumpLog::GetInstance().AddDesc(std::string("Draggable: ")
|
DumpLog::GetInstance().AddDesc(std::string("Draggable: ")
|
||||||
.append(draggable_ ? "true" : "false")
|
.append(draggable_ ? "true" : "false")
|
||||||
.append(" UserSet: ")
|
.append(" UserSet: ")
|
||||||
.append(userSet_ ? "true" : "false")
|
.append(userSet_ ? "true" : "false")
|
||||||
.append(" CustomerSet: ")
|
.append(" CustomerSet: ")
|
||||||
.append(customerSet_ ? "true" : "false"));
|
.append(customerSet_ ? "true" : "false"));
|
||||||
auto dragPreviewStr =
|
auto dragPreviewStr =
|
||||||
std::string("DragPreview: Has customNode: ").append(dragPreviewInfo_.customNode ? "YES" : "NO");
|
std::string("DragPreview: Has customNode: ").append(dragPreviewInfo_.customNode ? "YES" : "NO");
|
||||||
dragPreviewStr.append(" Has pixelMap: ").append(dragPreviewInfo_.pixelMap ? "YES" : "NO");
|
dragPreviewStr.append(" Has pixelMap: ").append(dragPreviewInfo_.pixelMap ? "YES" : "NO");
|
||||||
@ -766,30 +765,30 @@ void FrameNode::DumpDragInfo()
|
|||||||
DumpLog::GetInstance().AddDesc(dragPreviewStr);
|
DumpLog::GetInstance().AddDesc(dragPreviewStr);
|
||||||
auto eventHub = GetEventHub<EventHub>();
|
auto eventHub = GetEventHub<EventHub>();
|
||||||
DumpLog::GetInstance().AddDesc(std::string("Event: ")
|
DumpLog::GetInstance().AddDesc(std::string("Event: ")
|
||||||
.append("OnDragStart: ")
|
.append("OnDragStart: ")
|
||||||
.append(eventHub->HasOnDragStart() ? "YES" : "NO")
|
.append(eventHub->HasOnDragStart() ? "YES" : "NO")
|
||||||
.append(" OnDragEnter: ")
|
.append(" OnDragEnter: ")
|
||||||
.append(eventHub->HasOnDragEnter() ? "YES" : "NO")
|
.append(eventHub->HasOnDragEnter() ? "YES" : "NO")
|
||||||
.append(" OnDragLeave: ")
|
.append(" OnDragLeave: ")
|
||||||
.append(eventHub->HasOnDragLeave() ? "YES" : "NO")
|
.append(eventHub->HasOnDragLeave() ? "YES" : "NO")
|
||||||
.append(" OnDragMove: ")
|
.append(" OnDragMove: ")
|
||||||
.append(eventHub->HasOnDragMove() ? "YES" : "NO")
|
.append(eventHub->HasOnDragMove() ? "YES" : "NO")
|
||||||
.append(" OnDrop: ")
|
.append(" OnDrop: ")
|
||||||
.append(eventHub->HasOnDrop() ? "YES" : "NO")
|
.append(eventHub->HasOnDrop() ? "YES" : "NO")
|
||||||
.append(" OnDragEnd: ")
|
.append(" OnDragEnd: ")
|
||||||
.append(eventHub->HasOnDragEnd() ? "YES" : "NO"));
|
.append(eventHub->HasOnDragEnd() ? "YES" : "NO"));
|
||||||
DumpLog::GetInstance().AddDesc(std::string("DefaultOnDragStart: ")
|
DumpLog::GetInstance().AddDesc(std::string("DefaultOnDragStart: ")
|
||||||
.append(eventHub->HasDefaultOnDragStart() ? "YES" : "NO")
|
.append(eventHub->HasDefaultOnDragStart() ? "YES" : "NO")
|
||||||
.append(" CustomerOnDragEnter: ")
|
.append(" CustomerOnDragEnter: ")
|
||||||
.append(eventHub->HasCustomerOnDragEnter() ? "YES" : "NO")
|
.append(eventHub->HasCustomerOnDragEnter() ? "YES" : "NO")
|
||||||
.append(" CustomerOnDragLeave: ")
|
.append(" CustomerOnDragLeave: ")
|
||||||
.append(eventHub->HasCustomerOnDragLeave() ? "YES" : "NO")
|
.append(eventHub->HasCustomerOnDragLeave() ? "YES" : "NO")
|
||||||
.append(" CustomerOnDragMove: ")
|
.append(" CustomerOnDragMove: ")
|
||||||
.append(eventHub->HasCustomerOnDragMove() ? "YES" : "NO")
|
.append(eventHub->HasCustomerOnDragMove() ? "YES" : "NO")
|
||||||
.append(" CustomerOnDrop: ")
|
.append(" CustomerOnDrop: ")
|
||||||
.append(eventHub->HasCustomerOnDrop() ? "YES" : "NO")
|
.append(eventHub->HasCustomerOnDrop() ? "YES" : "NO")
|
||||||
.append(" CustomerOnDragEnd: ")
|
.append(" CustomerOnDragEnd: ")
|
||||||
.append(eventHub->HasCustomerOnDragEnd() ? "YES" : "NO"));
|
.append(eventHub->HasCustomerOnDragEnd() ? "YES" : "NO"));
|
||||||
DumpLog::GetInstance().AddDesc("------------end print dragInfo");
|
DumpLog::GetInstance().AddDesc("------------end print dragInfo");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -797,11 +796,11 @@ void FrameNode::DumpOnSizeChangeInfo()
|
|||||||
{
|
{
|
||||||
for (auto it = onSizeChangeDumpInfos.rbegin(); it != onSizeChangeDumpInfos.rend(); ++it) {
|
for (auto it = onSizeChangeDumpInfos.rbegin(); it != onSizeChangeDumpInfos.rend(); ++it) {
|
||||||
DumpLog::GetInstance().AddDesc(std::string("onSizeChange Time: ")
|
DumpLog::GetInstance().AddDesc(std::string("onSizeChange Time: ")
|
||||||
.append(ConvertTimestampToStr(it->onSizeChangeTimeStamp))
|
.append(ConvertTimestampToStr(it->onSizeChangeTimeStamp))
|
||||||
.append(" lastFrameRect: ")
|
.append(" lastFrameRect: ")
|
||||||
.append(it->lastFrameRect.ToString())
|
.append(it->lastFrameRect.ToString())
|
||||||
.append(" currFrameRect: ")
|
.append(" currFrameRect: ")
|
||||||
.append(it->currFrameRect.ToString()));
|
.append(it->currFrameRect.ToString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -963,8 +962,7 @@ void FrameNode::GeometryNodeToJsonValue(std::unique_ptr<JsonValue>& json, const
|
|||||||
auto jsonSize = json->GetValue("size");
|
auto jsonSize = json->GetValue("size");
|
||||||
if (!hasIdealWidth) {
|
if (!hasIdealWidth) {
|
||||||
auto idealWidthVpStr = std::to_string(Dimension(geometryNode_->GetFrameSize().Width()).ConvertToVp());
|
auto idealWidthVpStr = std::to_string(Dimension(geometryNode_->GetFrameSize().Width()).ConvertToVp());
|
||||||
auto widthStr =
|
auto widthStr = (idealWidthVpStr.substr(0, idealWidthVpStr.find(".") + SUBSTR_LENGTH) + DIMENSION_UNIT_VP);
|
||||||
(idealWidthVpStr.substr(0, idealWidthVpStr.find(".") + SUBSTR_LENGTH) + DIMENSION_UNIT_VP);
|
|
||||||
json->PutExtAttr("width", widthStr.c_str(), filter);
|
json->PutExtAttr("width", widthStr.c_str(), filter);
|
||||||
if (jsonSize) {
|
if (jsonSize) {
|
||||||
jsonSize->Put("width", widthStr.c_str());
|
jsonSize->Put("width", widthStr.c_str());
|
||||||
@ -973,8 +971,7 @@ void FrameNode::GeometryNodeToJsonValue(std::unique_ptr<JsonValue>& json, const
|
|||||||
|
|
||||||
if (!hasIdealHeight) {
|
if (!hasIdealHeight) {
|
||||||
auto idealHeightVpStr = std::to_string(Dimension(geometryNode_->GetFrameSize().Height()).ConvertToVp());
|
auto idealHeightVpStr = std::to_string(Dimension(geometryNode_->GetFrameSize().Height()).ConvertToVp());
|
||||||
auto heightStr =
|
auto heightStr = (idealHeightVpStr.substr(0, idealHeightVpStr.find(".") + SUBSTR_LENGTH) + DIMENSION_UNIT_VP);
|
||||||
(idealHeightVpStr.substr(0, idealHeightVpStr.find(".") + SUBSTR_LENGTH) + DIMENSION_UNIT_VP);
|
|
||||||
json->PutExtAttr("height", heightStr.c_str(), filter);
|
json->PutExtAttr("height", heightStr.c_str(), filter);
|
||||||
if (jsonSize) {
|
if (jsonSize) {
|
||||||
jsonSize->Put("height", heightStr.c_str());
|
jsonSize->Put("height", heightStr.c_str());
|
||||||
@ -1031,7 +1028,7 @@ void FrameNode::UpdateGeometryTransition()
|
|||||||
MarkDirtyNode();
|
MarkDirtyNode();
|
||||||
}
|
}
|
||||||
auto children = GetChildren();
|
auto children = GetChildren();
|
||||||
for (const auto& child: children) {
|
for (const auto& child : children) {
|
||||||
child->UpdateGeometryTransition();
|
child->UpdateGeometryTransition();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1123,8 +1120,7 @@ void FrameNode::NotifyVisibleChange(bool isVisible)
|
|||||||
void FrameNode::TryVisibleChangeOnDescendant(bool isVisible)
|
void FrameNode::TryVisibleChangeOnDescendant(bool isVisible)
|
||||||
{
|
{
|
||||||
auto layoutProperty = GetLayoutProperty();
|
auto layoutProperty = GetLayoutProperty();
|
||||||
if (layoutProperty &&
|
if (layoutProperty && layoutProperty->GetVisibilityValue(VisibleType::VISIBLE) != VisibleType::VISIBLE) {
|
||||||
layoutProperty->GetVisibilityValue(VisibleType::VISIBLE) != VisibleType::VISIBLE) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
NotifyVisibleChange(isVisible);
|
NotifyVisibleChange(isVisible);
|
||||||
@ -1357,8 +1353,8 @@ RectF FrameNode::GetRectWithRender()
|
|||||||
}
|
}
|
||||||
if (renderContext_ && renderContext_->GetPositionProperty()) {
|
if (renderContext_ && renderContext_->GetPositionProperty()) {
|
||||||
if (renderContext_->GetPositionProperty()->HasPosition()) {
|
if (renderContext_->GetPositionProperty()->HasPosition()) {
|
||||||
auto renderPosition = ContextPositionConvertToPX(
|
auto renderPosition =
|
||||||
renderContext_, layoutProperty_->GetLayoutConstraint()->percentReference);
|
ContextPositionConvertToPX(renderContext_, layoutProperty_->GetLayoutConstraint()->percentReference);
|
||||||
currFrameRect.SetOffset(
|
currFrameRect.SetOffset(
|
||||||
{ static_cast<float>(renderPosition.first), static_cast<float>(renderPosition.second) });
|
{ static_cast<float>(renderPosition.first), static_cast<float>(renderPosition.second) });
|
||||||
}
|
}
|
||||||
@ -1529,7 +1525,8 @@ void FrameNode::ThrottledVisibleTask()
|
|||||||
GetVisibleRect(visibleRect, frameRect);
|
GetVisibleRect(visibleRect, frameRect);
|
||||||
double ratio = IsFrameDisappear() ? VISIBLE_RATIO_MIN
|
double ratio = IsFrameDisappear() ? VISIBLE_RATIO_MIN
|
||||||
: std::clamp(CalculateCurrentVisibleRatio(visibleRect, frameRect),
|
: std::clamp(CalculateCurrentVisibleRatio(visibleRect, frameRect),
|
||||||
VISIBLE_RATIO_MIN, VISIBLE_RATIO_MAX);
|
VISIBLE_RATIO_MIN,
|
||||||
|
VISIBLE_RATIO_MAX);
|
||||||
if (NearEqual(ratio, lastThrottledVisibleRatio_)) {
|
if (NearEqual(ratio, lastThrottledVisibleRatio_)) {
|
||||||
throttledCallbackOnTheWay_ = false;
|
throttledCallbackOnTheWay_ = false;
|
||||||
return;
|
return;
|
||||||
@ -1800,7 +1797,7 @@ RefPtr<ContentModifier> FrameNode::GetContentModifier()
|
|||||||
auto wrapper = CreatePaintWrapper();
|
auto wrapper = CreatePaintWrapper();
|
||||||
CHECK_NULL_RETURN(wrapper, nullptr);
|
CHECK_NULL_RETURN(wrapper, nullptr);
|
||||||
auto paintMethod = pattern_->CreateNodePaintMethod();
|
auto paintMethod = pattern_->CreateNodePaintMethod();
|
||||||
if (!paintMethod || extensionHandler_ || renderContext_->GetAccessibilityFocus().value_or(false)) {
|
if (!paintMethod || extensionHandler_ || renderContext_->GetAccessibilityFocus().value_or(false)) {
|
||||||
paintMethod = pattern_->CreateDefaultNodePaintMethod();
|
paintMethod = pattern_->CreateDefaultNodePaintMethod();
|
||||||
}
|
}
|
||||||
CHECK_NULL_RETURN(paintMethod, nullptr);
|
CHECK_NULL_RETURN(paintMethod, nullptr);
|
||||||
@ -1969,8 +1966,8 @@ RefPtr<FrameNode> FrameNode::GetFirstAutoFillContainerNode()
|
|||||||
return AceType::DynamicCast<FrameNode>(parent);
|
return AceType::DynamicCast<FrameNode>(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameNode::NotifyFillRequestSuccess(RefPtr<ViewDataWrap> viewDataWrap,
|
void FrameNode::NotifyFillRequestSuccess(
|
||||||
RefPtr<PageNodeInfoWrap> nodeWrap, AceAutoFillType autoFillType)
|
RefPtr<ViewDataWrap> viewDataWrap, RefPtr<PageNodeInfoWrap> nodeWrap, AceAutoFillType autoFillType)
|
||||||
{
|
{
|
||||||
if (pattern_) {
|
if (pattern_) {
|
||||||
pattern_->NotifyFillRequestSuccess(viewDataWrap, nodeWrap, autoFillType);
|
pattern_->NotifyFillRequestSuccess(viewDataWrap, nodeWrap, autoFillType);
|
||||||
@ -2100,8 +2097,7 @@ void FrameNode::OnGenerateOneDepthVisibleFrameWithTransition(std::list<RefPtr<Fr
|
|||||||
visibleList.emplace_back(Claim(this));
|
visibleList.emplace_back(Claim(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameNode::OnGenerateOneDepthVisibleFrameWithOffset(
|
void FrameNode::OnGenerateOneDepthVisibleFrameWithOffset(std::list<RefPtr<FrameNode>>& visibleList, OffsetF& offset)
|
||||||
std::list<RefPtr<FrameNode>>& visibleList, OffsetF& offset)
|
|
||||||
{
|
{
|
||||||
if (isLayoutNode_) {
|
if (isLayoutNode_) {
|
||||||
isFind_ = true;
|
isFind_ = true;
|
||||||
@ -2189,8 +2185,7 @@ bool FrameNode::IsOutOfTouchTestRegion(const PointF& parentRevertPoint, int32_t
|
|||||||
auto subRevertPoint = revertPoint - paintRect.GetOffset();
|
auto subRevertPoint = revertPoint - paintRect.GetOffset();
|
||||||
auto clip = renderContext_->GetClipEdge().value_or(false);
|
auto clip = renderContext_->GetClipEdge().value_or(false);
|
||||||
if (!InResponseRegionList(revertPoint, responseRegionList) || !GetTouchable() ||
|
if (!InResponseRegionList(revertPoint, responseRegionList) || !GetTouchable() ||
|
||||||
NearZero(paintRectWithTransform.Width() ||
|
NearZero(paintRectWithTransform.Width() || NearZero(paintRectWithTransform.Height()))) {
|
||||||
NearZero(paintRectWithTransform.Height()))) {
|
|
||||||
if (clip) {
|
if (clip) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -2382,7 +2377,7 @@ HitTestResult FrameNode::TouchTest(const PointF& globalPoint, const PointF& pare
|
|||||||
if (childHitResult == HitTestResult::BUBBLING &&
|
if (childHitResult == HitTestResult::BUBBLING &&
|
||||||
((child->GetHitTestMode() == HitTestMode::HTMDEFAULT) ||
|
((child->GetHitTestMode() == HitTestMode::HTMDEFAULT) ||
|
||||||
(child->GetHitTestMode() == HitTestMode::HTMTRANSPARENT_SELF) ||
|
(child->GetHitTestMode() == HitTestMode::HTMTRANSPARENT_SELF) ||
|
||||||
((child->GetHitTestMode() != HitTestMode::HTMTRANSPARENT) && IsExclusiveEventForChild()))) {
|
((child->GetHitTestMode() != HitTestMode::HTMTRANSPARENT) && IsExclusiveEventForChild()))) {
|
||||||
consumed = true;
|
consumed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -3026,9 +3021,8 @@ RefPtr<FrameNode> FrameNode::FindChildByName(const RefPtr<FrameNode>& parentNode
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameNode::CreateAnimatablePropertyFloat(
|
void FrameNode::CreateAnimatablePropertyFloat(const std::string& propertyName, float value,
|
||||||
const std::string& propertyName, float value, const std::function<void(float)>& onCallbackEvent,
|
const std::function<void(float)>& onCallbackEvent, const PropertyUnit& propertyType)
|
||||||
const PropertyUnit& propertyType)
|
|
||||||
{
|
{
|
||||||
auto context = GetRenderContext();
|
auto context = GetRenderContext();
|
||||||
CHECK_NULL_VOID(context);
|
CHECK_NULL_VOID(context);
|
||||||
@ -3166,7 +3160,7 @@ int32_t FrameNode::GetNodeExpectedRate()
|
|||||||
void FrameNode::AddFRCSceneInfo(const std::string& scene, float speed, SceneStatus status)
|
void FrameNode::AddFRCSceneInfo(const std::string& scene, float speed, SceneStatus status)
|
||||||
{
|
{
|
||||||
if (SystemProperties::GetDebugEnabled()) {
|
if (SystemProperties::GetDebugEnabled()) {
|
||||||
const std::string sceneStatusStrs[] = {"START", "RUNNING", "END"};
|
const std::string sceneStatusStrs[] = { "START", "RUNNING", "END" };
|
||||||
LOGI("%{public}s AddFRCSceneInfo scene:%{public}s speed:%{public}f status:%{public}s", GetTag().c_str(),
|
LOGI("%{public}s AddFRCSceneInfo scene:%{public}s speed:%{public}f status:%{public}s", GetTag().c_str(),
|
||||||
scene.c_str(), std::abs(speed), sceneStatusStrs[static_cast<int32_t>(status)].c_str());
|
scene.c_str(), std::abs(speed), sceneStatusStrs[static_cast<int32_t>(status)].c_str());
|
||||||
}
|
}
|
||||||
@ -3299,8 +3293,8 @@ void FrameNode::UpdatePercentSensitive()
|
|||||||
// This will call child and self measure process.
|
// This will call child and self measure process.
|
||||||
void FrameNode::Measure(const std::optional<LayoutConstraintF>& parentConstraint)
|
void FrameNode::Measure(const std::optional<LayoutConstraintF>& parentConstraint)
|
||||||
{
|
{
|
||||||
ACE_LAYOUT_TRACE_BEGIN("Measure[%s][self:%d][parent:%d][key:%s]", GetTag().c_str(),
|
ACE_LAYOUT_TRACE_BEGIN("Measure[%s][self:%d][parent:%d][key:%s]", GetTag().c_str(), GetId(),
|
||||||
GetId(), GetAncestorNodeOfFrame() ? GetAncestorNodeOfFrame()->GetId() : 0, GetInspectorIdValue("").c_str());
|
GetAncestorNodeOfFrame() ? GetAncestorNodeOfFrame()->GetId() : 0, GetInspectorIdValue("").c_str());
|
||||||
ArkUIPerfMonitor::GetInstance().RecordLayoutNode();
|
ArkUIPerfMonitor::GetInstance().RecordLayoutNode();
|
||||||
isLayoutComplete_ = false;
|
isLayoutComplete_ = false;
|
||||||
if (!oldGeometryNode_) {
|
if (!oldGeometryNode_) {
|
||||||
@ -3406,8 +3400,8 @@ void FrameNode::Measure(const std::optional<LayoutConstraintF>& parentConstraint
|
|||||||
// Called to perform layout children.
|
// Called to perform layout children.
|
||||||
void FrameNode::Layout()
|
void FrameNode::Layout()
|
||||||
{
|
{
|
||||||
ACE_LAYOUT_TRACE_BEGIN("Layout[%s][self:%d][parent:%d][key:%s]", GetTag().c_str(),
|
ACE_LAYOUT_TRACE_BEGIN("Layout[%s][self:%d][parent:%d][key:%s]", GetTag().c_str(), GetId(),
|
||||||
GetId(), GetAncestorNodeOfFrame() ? GetAncestorNodeOfFrame()->GetId() : 0, GetInspectorIdValue("").c_str());
|
GetAncestorNodeOfFrame() ? GetAncestorNodeOfFrame()->GetId() : 0, GetInspectorIdValue("").c_str());
|
||||||
if (layoutProperty_->GetLayoutRect()) {
|
if (layoutProperty_->GetLayoutRect()) {
|
||||||
GetGeometryNode()->SetFrameOffset(layoutProperty_->GetLayoutRect().value().GetOffset());
|
GetGeometryNode()->SetFrameOffset(layoutProperty_->GetLayoutRect().value().GetOffset());
|
||||||
}
|
}
|
||||||
@ -3604,8 +3598,8 @@ bool FrameNode::OnLayoutFinish(bool& needSyncRsNode, DirtySwapConfig& config)
|
|||||||
void FrameNode::SyncGeometryNode(bool needSyncRsNode, const DirtySwapConfig& config)
|
void FrameNode::SyncGeometryNode(bool needSyncRsNode, const DirtySwapConfig& config)
|
||||||
{
|
{
|
||||||
if (SystemProperties::GetSyncDebugTraceEnabled()) {
|
if (SystemProperties::GetSyncDebugTraceEnabled()) {
|
||||||
ACE_LAYOUT_TRACE_BEGIN("SyncGeometryNode[%s][self:%d][parent:%d][key:%s]", GetTag().c_str(),
|
ACE_LAYOUT_TRACE_BEGIN("SyncGeometryNode[%s][self:%d][parent:%d][key:%s]", GetTag().c_str(), GetId(),
|
||||||
GetId(), GetParent() ? GetParent()->GetId() : 0, GetInspectorIdValue("").c_str());
|
GetParent() ? GetParent()->GetId() : 0, GetInspectorIdValue("").c_str());
|
||||||
ACE_LAYOUT_TRACE_END()
|
ACE_LAYOUT_TRACE_END()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3727,9 +3721,9 @@ void FrameNode::RemoveAllChildInRenderTree()
|
|||||||
frameProxy_->RemoveAllChildInRenderTree();
|
frameProxy_->RemoveAllChildInRenderTree();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameNode::SetActiveChildRange(int32_t start, int32_t end)
|
void FrameNode::SetActiveChildRange(int32_t start, int32_t end, int32_t cacheStart, int32_t cacheEnd)
|
||||||
{
|
{
|
||||||
frameProxy_->SetActiveChildRange(start, end);
|
frameProxy_->SetActiveChildRange(start, end, cacheStart, cacheEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameNode::RecycleItemsByIndex(int32_t start, int32_t end)
|
void FrameNode::RecycleItemsByIndex(int32_t start, int32_t end)
|
||||||
@ -3858,7 +3852,7 @@ void FrameNode::DoRemoveChildInRenderTree(uint32_t index, bool isAll)
|
|||||||
SetActive(false);
|
SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameNode::DoSetActiveChildRange(int32_t start, int32_t end)
|
void FrameNode::DoSetActiveChildRange(int32_t start, int32_t end, int32_t cacheStart, int32_t cacheEnd)
|
||||||
{
|
{
|
||||||
if (start <= end) {
|
if (start <= end) {
|
||||||
if (start > 0 || end < 0) {
|
if (start > 0 || end < 0) {
|
||||||
@ -3949,16 +3943,14 @@ void FrameNode::AddFrameNodeSnapshot(bool isHit, int32_t parentId, std::vector<R
|
|||||||
auto eventMgr = context->GetEventManager();
|
auto eventMgr = context->GetEventManager();
|
||||||
CHECK_NULL_VOID(eventMgr);
|
CHECK_NULL_VOID(eventMgr);
|
||||||
|
|
||||||
FrameNodeSnapshot info = {
|
FrameNodeSnapshot info = { .nodeId = GetId(),
|
||||||
.nodeId = GetId(),
|
|
||||||
.parentNodeId = parentId,
|
.parentNodeId = parentId,
|
||||||
.tag = GetTag(),
|
.tag = GetTag(),
|
||||||
.comId = propInspectorId_.value_or(""),
|
.comId = propInspectorId_.value_or(""),
|
||||||
.monopolizeEvents = GetMonopolizeEvents(),
|
.monopolizeEvents = GetMonopolizeEvents(),
|
||||||
.isHit = isHit,
|
.isHit = isHit,
|
||||||
.hitTestMode = static_cast<int32_t>(GetHitTestMode()),
|
.hitTestMode = static_cast<int32_t>(GetHitTestMode()),
|
||||||
.responseRegionList = responseRegionList
|
.responseRegionList = responseRegionList };
|
||||||
};
|
|
||||||
eventMgr->GetEventTreeRecord().AddFrameNodeSnapshot(std::move(info));
|
eventMgr->GetEventTreeRecord().AddFrameNodeSnapshot(std::move(info));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3978,40 +3970,40 @@ int64_t FrameNode::WrapExtensionAbilityId(int64_t extensionOffset, int64_t abili
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameNode::SearchExtensionElementInfoByAccessibilityIdNG(int64_t elementId, int32_t mode,
|
void FrameNode::SearchExtensionElementInfoByAccessibilityIdNG(
|
||||||
int64_t offset, std::list<Accessibility::AccessibilityElementInfo>& output)
|
int64_t elementId, int32_t mode, int64_t offset, std::list<Accessibility::AccessibilityElementInfo>& output)
|
||||||
{
|
{
|
||||||
if (pattern_) {
|
if (pattern_) {
|
||||||
pattern_->SearchExtensionElementInfoByAccessibilityId(elementId, mode, offset, output);
|
pattern_->SearchExtensionElementInfoByAccessibilityId(elementId, mode, offset, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameNode::SearchElementInfosByTextNG(int64_t elementId, const std::string& text,
|
void FrameNode::SearchElementInfosByTextNG(int64_t elementId, const std::string& text, int64_t offset,
|
||||||
int64_t offset, std::list<Accessibility::AccessibilityElementInfo>& output)
|
std::list<Accessibility::AccessibilityElementInfo>& output)
|
||||||
{
|
{
|
||||||
if (pattern_) {
|
if (pattern_) {
|
||||||
pattern_->SearchElementInfosByText(elementId, text, offset, output);
|
pattern_->SearchElementInfosByText(elementId, text, offset, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameNode::FindFocusedExtensionElementInfoNG(int64_t elementId, int32_t focusType,
|
void FrameNode::FindFocusedExtensionElementInfoNG(
|
||||||
int64_t offset, Accessibility::AccessibilityElementInfo& output)
|
int64_t elementId, int32_t focusType, int64_t offset, Accessibility::AccessibilityElementInfo& output)
|
||||||
{
|
{
|
||||||
if (pattern_) {
|
if (pattern_) {
|
||||||
pattern_->FindFocusedElementInfo(elementId, focusType, offset, output);
|
pattern_->FindFocusedElementInfo(elementId, focusType, offset, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameNode::FocusMoveSearchNG(int64_t elementId, int32_t direction,
|
void FrameNode::FocusMoveSearchNG(
|
||||||
int64_t offset, Accessibility::AccessibilityElementInfo& output)
|
int64_t elementId, int32_t direction, int64_t offset, Accessibility::AccessibilityElementInfo& output)
|
||||||
{
|
{
|
||||||
if (pattern_) {
|
if (pattern_) {
|
||||||
pattern_->FocusMoveSearch(elementId, direction, offset, output);
|
pattern_->FocusMoveSearch(elementId, direction, offset, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FrameNode::TransferExecuteAction(int64_t elementId, const std::map<std::string, std::string>& actionArguments,
|
bool FrameNode::TransferExecuteAction(
|
||||||
int32_t action, int64_t offset)
|
int64_t elementId, const std::map<std::string, std::string>& actionArguments, int32_t action, int64_t offset)
|
||||||
{
|
{
|
||||||
bool isExecuted = false;
|
bool isExecuted = false;
|
||||||
if (pattern_) {
|
if (pattern_) {
|
||||||
@ -4551,11 +4543,7 @@ OPINC_TYPE_E FrameNode::FindSuggestOpIncNode(std::string& path, const SizeF& bou
|
|||||||
path = path + " --> " + hostTag;
|
path = path + " --> " + hostTag;
|
||||||
LOGI("FindSuggestOpIncNode : %{public}s, with depth %{public}d, boundary: %{public}f, self: %{public}f, "
|
LOGI("FindSuggestOpIncNode : %{public}s, with depth %{public}d, boundary: %{public}f, self: %{public}f, "
|
||||||
"status: %{public}d",
|
"status: %{public}d",
|
||||||
path.c_str(),
|
path.c_str(), depth, boundary.Height(), GetGeometryNode()->GetFrameSize().Height(), status);
|
||||||
depth,
|
|
||||||
boundary.Height(),
|
|
||||||
GetGeometryNode()->GetFrameSize().Height(),
|
|
||||||
status);
|
|
||||||
}
|
}
|
||||||
if (status == OPINC_NODE) {
|
if (status == OPINC_NODE) {
|
||||||
MarkSuggestOpIncGroup(true, true);
|
MarkSuggestOpIncGroup(true, true);
|
||||||
|
@ -716,8 +716,8 @@ public:
|
|||||||
void RemoveChildInRenderTree(uint32_t index) override;
|
void RemoveChildInRenderTree(uint32_t index) override;
|
||||||
void RemoveAllChildInRenderTree() override;
|
void RemoveAllChildInRenderTree() override;
|
||||||
void DoRemoveChildInRenderTree(uint32_t index, bool isAll) override;
|
void DoRemoveChildInRenderTree(uint32_t index, bool isAll) override;
|
||||||
void SetActiveChildRange(int32_t start, int32_t end) override;
|
void SetActiveChildRange(int32_t start, int32_t end, int32_t cacheStart = 0, int32_t cacheEnd = 0) override;
|
||||||
void DoSetActiveChildRange(int32_t start, int32_t end) override;
|
void DoSetActiveChildRange(int32_t start, int32_t end, int32_t cacheStart, int32_t cacheEnd) override;
|
||||||
void RecycleItemsByIndex(int32_t start, int32_t end) override;
|
void RecycleItemsByIndex(int32_t start, int32_t end) override;
|
||||||
const std::string& GetHostTag() const override
|
const std::string& GetHostTag() const override
|
||||||
{
|
{
|
||||||
|
@ -1233,7 +1233,7 @@ RefPtr<UINode> UINode::GetFrameChildByIndexWithoutExpanded(uint32_t index)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t UINode::GetFrameNodeIndex(RefPtr<FrameNode> node, bool isExpanded)
|
int32_t UINode::GetFrameNodeIndex(const RefPtr<FrameNode>& node, bool isExpanded)
|
||||||
{
|
{
|
||||||
int32_t index = 0;
|
int32_t index = 0;
|
||||||
for (const auto& child : GetChildren()) {
|
for (const auto& child : GetChildren()) {
|
||||||
@ -1271,11 +1271,11 @@ void UINode::DoRemoveChildInRenderTree(uint32_t index, bool isAll)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UINode::DoSetActiveChildRange(int32_t start, int32_t end)
|
void UINode::DoSetActiveChildRange(int32_t start, int32_t end, int32_t cacheStart, int32_t cacheEnd)
|
||||||
{
|
{
|
||||||
for (const auto& child : children_) {
|
for (const auto& child : children_) {
|
||||||
uint32_t count = static_cast<uint32_t>(child->FrameCount());
|
uint32_t count = static_cast<uint32_t>(child->FrameCount());
|
||||||
child->DoSetActiveChildRange(start, end);
|
child->DoSetActiveChildRange(start, end, cacheStart, cacheEnd);
|
||||||
start -= static_cast<int32_t>(count);
|
start -= static_cast<int32_t>(count);
|
||||||
end -= static_cast<int32_t>(count);
|
end -= static_cast<int32_t>(count);
|
||||||
}
|
}
|
||||||
|
@ -420,7 +420,7 @@ public:
|
|||||||
bool addToRenderTree = false);
|
bool addToRenderTree = false);
|
||||||
virtual RefPtr<UINode> GetFrameChildByIndexWithoutExpanded(uint32_t index);
|
virtual RefPtr<UINode> GetFrameChildByIndexWithoutExpanded(uint32_t index);
|
||||||
// Get current frameNode index with or without expanded all LazyForEachNode;
|
// Get current frameNode index with or without expanded all LazyForEachNode;
|
||||||
virtual int32_t GetFrameNodeIndex(RefPtr<FrameNode> node, bool isExpanded = true);
|
virtual int32_t GetFrameNodeIndex(const RefPtr<FrameNode>& node, bool isExpanded = true);
|
||||||
void SetDebugLine(const std::string& line)
|
void SetDebugLine(const std::string& line)
|
||||||
{
|
{
|
||||||
debugLine_ = line;
|
debugLine_ = line;
|
||||||
@ -538,7 +538,7 @@ public:
|
|||||||
// --------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------
|
||||||
|
|
||||||
virtual void DoRemoveChildInRenderTree(uint32_t index, bool isAll = false);
|
virtual void DoRemoveChildInRenderTree(uint32_t index, bool isAll = false);
|
||||||
virtual void DoSetActiveChildRange(int32_t start, int32_t end);
|
virtual void DoSetActiveChildRange(int32_t start, int32_t end, int32_t cacheStart, int32_t cacheEnd);
|
||||||
virtual void OnSetCacheCount(int32_t cacheCount, const std::optional<LayoutConstraintF>& itemConstraint);
|
virtual void OnSetCacheCount(int32_t cacheCount, const std::optional<LayoutConstraintF>& itemConstraint);
|
||||||
|
|
||||||
// return value: true if the node can be removed immediately.
|
// return value: true if the node can be removed immediately.
|
||||||
|
@ -16,8 +16,10 @@
|
|||||||
#ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_LAYOUTS_LAYOUT_WRAPPER_H
|
#ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_LAYOUTS_LAYOUT_WRAPPER_H
|
||||||
#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_LAYOUTS_LAYOUT_WRAPPER_H
|
#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_LAYOUTS_LAYOUT_WRAPPER_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
@ -48,9 +50,19 @@ public:
|
|||||||
|
|
||||||
class RecursionGuard final {
|
class RecursionGuard final {
|
||||||
public:
|
public:
|
||||||
RecursionGuard(RecursiveLock& lock) : lock_(lock) { lock_.Lock(); }
|
RecursionGuard(RecursiveLock& lock) : lock_(lock)
|
||||||
~RecursionGuard() { lock_.Unlock(); }
|
{
|
||||||
RecursionGuard(const RecursionGuard& rhs) : lock_(rhs.lock_) { lock_.Lock(); }
|
lock_.Lock();
|
||||||
|
}
|
||||||
|
~RecursionGuard()
|
||||||
|
{
|
||||||
|
lock_.Unlock();
|
||||||
|
}
|
||||||
|
RecursionGuard(const RecursionGuard& rhs) : lock_(rhs.lock_)
|
||||||
|
{
|
||||||
|
lock_.Lock();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RecursiveLock& lock_;
|
RecursiveLock& lock_;
|
||||||
};
|
};
|
||||||
@ -58,16 +70,45 @@ private:
|
|||||||
class ChildrenListWithGuard final {
|
class ChildrenListWithGuard final {
|
||||||
public:
|
public:
|
||||||
ChildrenListWithGuard(const std::list<RefPtr<LayoutWrapper>>& children, RecursiveLock& lock)
|
ChildrenListWithGuard(const std::list<RefPtr<LayoutWrapper>>& children, RecursiveLock& lock)
|
||||||
: children_(children), guard_(lock) {}
|
: children_(children), guard_(lock)
|
||||||
auto begin() const { return children_.begin(); }
|
{}
|
||||||
auto end() const { return children_.end(); }
|
auto begin() const
|
||||||
auto rbegin() const { return children_.rbegin(); }
|
{
|
||||||
auto rend() const { return children_.rend(); }
|
return children_.begin();
|
||||||
auto empty() const { return children_.empty(); }
|
}
|
||||||
auto size() const { return children_.size(); }
|
auto end() const
|
||||||
auto& front() const { return children_.front(); }
|
{
|
||||||
auto& back() const { return children_.back(); }
|
return children_.end();
|
||||||
operator std::list<RefPtr<LayoutWrapper>>() const { return children_; }
|
}
|
||||||
|
auto rbegin() const
|
||||||
|
{
|
||||||
|
return children_.rbegin();
|
||||||
|
}
|
||||||
|
auto rend() const
|
||||||
|
{
|
||||||
|
return children_.rend();
|
||||||
|
}
|
||||||
|
auto empty() const
|
||||||
|
{
|
||||||
|
return children_.empty();
|
||||||
|
}
|
||||||
|
auto size() const
|
||||||
|
{
|
||||||
|
return children_.size();
|
||||||
|
}
|
||||||
|
auto& front() const
|
||||||
|
{
|
||||||
|
return children_.front();
|
||||||
|
}
|
||||||
|
auto& back() const
|
||||||
|
{
|
||||||
|
return children_.back();
|
||||||
|
}
|
||||||
|
operator std::list<RefPtr<LayoutWrapper>>() const
|
||||||
|
{
|
||||||
|
return children_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::list<RefPtr<LayoutWrapper>>& children_;
|
const std::list<RefPtr<LayoutWrapper>>& children_;
|
||||||
RecursionGuard guard_;
|
RecursionGuard guard_;
|
||||||
@ -96,9 +137,12 @@ public:
|
|||||||
virtual ChildrenListWithGuard GetAllChildrenWithBuild(bool addToRenderTree = true) = 0;
|
virtual ChildrenListWithGuard GetAllChildrenWithBuild(bool addToRenderTree = true) = 0;
|
||||||
virtual void RemoveChildInRenderTree(uint32_t index) = 0;
|
virtual void RemoveChildInRenderTree(uint32_t index) = 0;
|
||||||
virtual void RemoveAllChildInRenderTree() = 0;
|
virtual void RemoveAllChildInRenderTree() = 0;
|
||||||
virtual void SetActiveChildRange(int32_t start, int32_t end) = 0;
|
virtual void SetActiveChildRange(int32_t start, int32_t end, int32_t cacheStart = 0, int32_t cacheEnd = 0) = 0;
|
||||||
virtual void RecycleItemsByIndex(int32_t start, int32_t end) = 0;
|
virtual void RecycleItemsByIndex(int32_t start, int32_t end) = 0;
|
||||||
|
|
||||||
|
virtual void SetActiveChildRange(const std::set<int32_t>& activeIndexes, const std::set<int32_t>& cachedIndexes) {}
|
||||||
|
virtual void RecycleItemsByIndex(const std::set<int32_t>& indexes) {}
|
||||||
|
|
||||||
RefPtr<FrameNode> GetHostNode() const;
|
RefPtr<FrameNode> GetHostNode() const;
|
||||||
virtual const std::string& GetHostTag() const = 0;
|
virtual const std::string& GetHostTag() const = 0;
|
||||||
virtual bool IsActive() const = 0;
|
virtual bool IsActive() const = 0;
|
||||||
|
@ -119,7 +119,7 @@ public:
|
|||||||
|
|
||||||
void RemoveChildInRenderTree(uint32_t index) override;
|
void RemoveChildInRenderTree(uint32_t index) override;
|
||||||
void RemoveAllChildInRenderTree() override;
|
void RemoveAllChildInRenderTree() override;
|
||||||
void SetActiveChildRange(int32_t start, int32_t end) override {}
|
void SetActiveChildRange(int32_t start, int32_t end, int32_t cacheStart = 0, int32_t cacheEnd = 0) override {}
|
||||||
void RecycleItemsByIndex(int32_t start, int32_t end) override {}
|
void RecycleItemsByIndex(int32_t start, int32_t end) override {}
|
||||||
|
|
||||||
void ResetHostNode();
|
void ResetHostNode();
|
||||||
|
@ -172,7 +172,7 @@ RefPtr<UINode> CustomNode::GetFrameChildByIndex(uint32_t index, bool needBuild,
|
|||||||
return UINode::GetFrameChildByIndex(index, needBuild, isCache, addToRenderTree);
|
return UINode::GetFrameChildByIndex(index, needBuild, isCache, addToRenderTree);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CustomNode::DoSetActiveChildRange(int32_t start, int32_t end)
|
void CustomNode::DoSetActiveChildRange(int32_t start, int32_t end, int32_t cacheStart, int32_t cacheEnd)
|
||||||
{
|
{
|
||||||
if (start <= end) {
|
if (start <= end) {
|
||||||
if (start > 0 || end < 0) {
|
if (start > 0 || end < 0) {
|
||||||
|
@ -114,7 +114,7 @@ public:
|
|||||||
return extraInfos_;
|
return extraInfos_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoSetActiveChildRange(int32_t start, int32_t end) override;
|
void DoSetActiveChildRange(int32_t start, int32_t end, int32_t cacheStart, int32_t cacheEnd) override;
|
||||||
|
|
||||||
const WeakPtr<UINode>& GetNavigationNode() const
|
const WeakPtr<UINode>& GetNavigationNode() const
|
||||||
{
|
{
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "core/components_ng/base/ui_node.h"
|
#include "core/components_ng/base/ui_node.h"
|
||||||
#include "core/components_ng/syntax/lazy_for_each_node.h"
|
#include "core/components_ng/syntax/lazy_for_each_node.h"
|
||||||
|
#include "core/components_ng/syntax/repeat_virtual_scroll_node.h"
|
||||||
#include "core/components_ng/pattern/list/list_item_group_pattern.h"
|
#include "core/components_ng/pattern/list/list_item_group_pattern.h"
|
||||||
#include "core/components_ng/pattern/list/list_item_pattern.h"
|
#include "core/components_ng/pattern/list/list_item_pattern.h"
|
||||||
#include "core/components_ng/pattern/list/list_layout_algorithm.h"
|
#include "core/components_ng/pattern/list/list_layout_algorithm.h"
|
||||||
@ -95,16 +96,16 @@ public:
|
|||||||
if (AceType::InstanceOf<FrameNode>(child)) {
|
if (AceType::InstanceOf<FrameNode>(child)) {
|
||||||
auto frameNode = AceType::DynamicCast<FrameNode>(child);
|
auto frameNode = AceType::DynamicCast<FrameNode>(child);
|
||||||
CalculateFrameNode(frameNode);
|
CalculateFrameNode(frameNode);
|
||||||
} else if (AceType::InstanceOf<LazyForEachNode>(child)) {
|
} else if (AceType::InstanceOf<LazyForEachNode>(child) ||
|
||||||
auto lazyForEach = AceType::DynamicCast<LazyForEachNode>(child);
|
AceType::InstanceOf<RepeatVirtualScrollNode>(child)) {
|
||||||
CalculateLazyForEachNode(lazyForEach);
|
CalculateLazyForEachNode(child);
|
||||||
} else {
|
} else {
|
||||||
CalculateUINode(child);
|
CalculateUINode(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float GetLazyForEachIndexAverageHeight(RefPtr<LazyForEachNode> node,
|
float GetLazyForEachIndexAverageHeight(RefPtr<UINode> node,
|
||||||
int32_t startIndex, int32_t endIndex, bool &hasGroup)
|
int32_t startIndex, int32_t endIndex, bool &hasGroup)
|
||||||
{
|
{
|
||||||
auto itor = itemPosition_.find(startIndex);
|
auto itor = itemPosition_.find(startIndex);
|
||||||
@ -143,7 +144,7 @@ public:
|
|||||||
return totalHeight / itemCount;
|
return totalHeight / itemCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
float CalculateOffset(RefPtr<LazyForEachNode> node, float averageHeight)
|
float CalculateOffset(RefPtr<UINode> node, float averageHeight)
|
||||||
{
|
{
|
||||||
auto itor = itemPosition_.begin();
|
auto itor = itemPosition_.begin();
|
||||||
float skipHeight = 0.0f;
|
float skipHeight = 0.0f;
|
||||||
@ -163,7 +164,7 @@ public:
|
|||||||
return estimateOffset_ - targetPos_.first;
|
return estimateOffset_ - targetPos_.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalculateLazyForEachNode(RefPtr<LazyForEachNode> node)
|
void CalculateLazyForEachNode(RefPtr<UINode> node)
|
||||||
{
|
{
|
||||||
int32_t count = node->FrameCount();
|
int32_t count = node->FrameCount();
|
||||||
if (count <= 0) {
|
if (count <= 0) {
|
||||||
|
@ -33,18 +33,24 @@ void ListItemModelNG::Create(std::function<void(int32_t)>&& deepRenderFunc, V2::
|
|||||||
{
|
{
|
||||||
auto* stack = ViewStackProcessor::GetInstance();
|
auto* stack = ViewStackProcessor::GetInstance();
|
||||||
auto nodeId = stack->ClaimNodeId();
|
auto nodeId = stack->ClaimNodeId();
|
||||||
auto deepRender = [nodeId, deepRenderFunc = std::move(deepRenderFunc)]() -> RefPtr<UINode> {
|
|
||||||
CHECK_NULL_RETURN(deepRenderFunc, nullptr);
|
|
||||||
ScopedViewStackProcessor scopedViewStackProcessor;
|
|
||||||
deepRenderFunc(nodeId);
|
|
||||||
return ViewStackProcessor::GetInstance()->Finish();
|
|
||||||
};
|
|
||||||
ACE_LAYOUT_SCOPED_TRACE("Create[%s][self:%d]", V2::LIST_ITEM_ETS_TAG, nodeId);
|
ACE_LAYOUT_SCOPED_TRACE("Create[%s][self:%d]", V2::LIST_ITEM_ETS_TAG, nodeId);
|
||||||
auto frameNode = ScrollableItemPool::GetInstance().Allocate(V2::LIST_ITEM_ETS_TAG, nodeId,
|
if (deepRenderFunc) {
|
||||||
[shallowBuilder = AceType::MakeRefPtr<ShallowBuilder>(std::move(deepRender)), itemStyle = listItemStyle]() {
|
auto deepRender = [nodeId, deepRenderFunc = std::move(deepRenderFunc)]() -> RefPtr<UINode> {
|
||||||
return AceType::MakeRefPtr<ListItemPattern>(shallowBuilder, itemStyle);
|
CHECK_NULL_RETURN(deepRenderFunc, nullptr);
|
||||||
});
|
ScopedViewStackProcessor scopedViewStackProcessor;
|
||||||
stack->Push(frameNode);
|
deepRenderFunc(nodeId);
|
||||||
|
return ViewStackProcessor::GetInstance()->Finish();
|
||||||
|
};
|
||||||
|
auto frameNode = ScrollableItemPool::GetInstance().Allocate(V2::LIST_ITEM_ETS_TAG, nodeId,
|
||||||
|
[shallowBuilder = AceType::MakeRefPtr<ShallowBuilder>(std::move(deepRender)), itemStyle = listItemStyle]() {
|
||||||
|
return AceType::MakeRefPtr<ListItemPattern>(shallowBuilder, itemStyle);
|
||||||
|
});
|
||||||
|
stack->Push(frameNode);
|
||||||
|
} else {
|
||||||
|
auto frameNode = FrameNode::GetOrCreateFrameNode(V2::LIST_ITEM_ETS_TAG, nodeId,
|
||||||
|
[listItemStyle]() { return AceType::MakeRefPtr<ListItemPattern>(nullptr, listItemStyle); });
|
||||||
|
stack->Push(frameNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListItemModelNG::Create()
|
void ListItemModelNG::Create()
|
||||||
|
@ -348,9 +348,18 @@ float ListLanesLayoutAlgorithm::CalculateLaneCrossOffset(float crossSize, float
|
|||||||
int32_t ListLanesLayoutAlgorithm::GetLazyForEachIndex(const RefPtr<FrameNode>& host)
|
int32_t ListLanesLayoutAlgorithm::GetLazyForEachIndex(const RefPtr<FrameNode>& host)
|
||||||
{
|
{
|
||||||
CHECK_NULL_RETURN(host, -1);
|
CHECK_NULL_RETURN(host, -1);
|
||||||
auto lazyForEach = AceType::DynamicCast<LazyForEachNode>(host->GetParent());
|
auto parent = host->GetParent();
|
||||||
CHECK_NULL_RETURN(lazyForEach, -1);
|
auto lazyForEach = AceType::DynamicCast<LazyForEachNode>(parent);
|
||||||
return lazyForEach->GetIndexByUINode(host);
|
if (lazyForEach) {
|
||||||
|
return lazyForEach->GetIndexByUINode(host);
|
||||||
|
}
|
||||||
|
while (parent && !AceType::InstanceOf<FrameNode>(parent)) {
|
||||||
|
if (AceType::InstanceOf<RepeatVirtualScrollNode>(parent)) {
|
||||||
|
return parent->GetFrameNodeIndex(host);
|
||||||
|
}
|
||||||
|
parent = parent->GetParent();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t ListLanesLayoutAlgorithm::FindLanesStartIndex(LayoutWrapper* layoutWrapper, int32_t startIndex, int32_t index)
|
int32_t ListLanesLayoutAlgorithm::FindLanesStartIndex(LayoutWrapper* layoutWrapper, int32_t startIndex, int32_t index)
|
||||||
|
@ -162,10 +162,11 @@ void ListLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto cacheCount = listLayoutProperty->GetCachedCountValue(1) * GetLanes();
|
||||||
if (itemPosition_.empty()) {
|
if (itemPosition_.empty()) {
|
||||||
layoutWrapper->SetActiveChildRange(-1, -1);
|
layoutWrapper->SetActiveChildRange(-1, -1);
|
||||||
} else {
|
} else {
|
||||||
layoutWrapper->SetActiveChildRange(itemPosition_.begin()->first, itemPosition_.rbegin()->first);
|
layoutWrapper->SetActiveChildRange(itemPosition_.begin()->first, itemPosition_.rbegin()->first, cacheCount, cacheCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto crossSize = contentIdealSize.CrossSize(axis_);
|
auto crossSize = contentIdealSize.CrossSize(axis_);
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "base/utils/utils.h"
|
#include "base/utils/utils.h"
|
||||||
#include "core/components_ng/base/ui_node.h"
|
#include "core/components_ng/base/ui_node.h"
|
||||||
#include "core/components_ng/syntax/lazy_for_each_node.h"
|
#include "core/components_ng/syntax/lazy_for_each_node.h"
|
||||||
|
#include "core/components_ng/syntax/repeat_virtual_scroll_node.h"
|
||||||
#include "core/components_ng/pattern/list/list_children_main_size.h"
|
#include "core/components_ng/pattern/list/list_children_main_size.h"
|
||||||
#include "core/components_ng/pattern/list/list_item_group_pattern.h"
|
#include "core/components_ng/pattern/list/list_item_group_pattern.h"
|
||||||
#include "core/components_ng/pattern/list/list_layout_algorithm.h"
|
#include "core/components_ng/pattern/list/list_layout_algorithm.h"
|
||||||
@ -162,28 +163,17 @@ public:
|
|||||||
if (AceType::InstanceOf<FrameNode>(child)) {
|
if (AceType::InstanceOf<FrameNode>(child)) {
|
||||||
auto frameNode = AceType::DynamicCast<FrameNode>(child);
|
auto frameNode = AceType::DynamicCast<FrameNode>(child);
|
||||||
CalculateFrameNode(frameNode);
|
CalculateFrameNode(frameNode);
|
||||||
} else if (AceType::InstanceOf<LazyForEachNode>(child)) {
|
} else if (AceType::InstanceOf<LazyForEachNode>(child) ||
|
||||||
auto lazyForEach = AceType::DynamicCast<LazyForEachNode>(child);
|
AceType::InstanceOf<RepeatVirtualScrollNode>(child)) {
|
||||||
// Rules: only one type node(ListItem or ListItemGroup) can exist in LazyForEach.
|
// Rules: only one type node(ListItem or ListItemGroup) can exist in LazyForEach.
|
||||||
CalculateLazyForEachNode(lazyForEach);
|
CalculateLazyForEachNode(child);
|
||||||
} else {
|
} else {
|
||||||
CalculateUINode(child);
|
CalculateUINode(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<FrameNode> GetListFrameNode(RefPtr<LazyForEachNode> node) const
|
std::optional<bool> GetLazyForEachChildIsGroup(RefPtr<UINode> node)
|
||||||
{
|
|
||||||
auto parent = node->GetParent();
|
|
||||||
RefPtr<FrameNode> frameNode = AceType::DynamicCast<FrameNode>(parent);
|
|
||||||
while (parent && (!frameNode)) {
|
|
||||||
parent = parent->GetParent();
|
|
||||||
frameNode = AceType::DynamicCast<FrameNode>(parent);
|
|
||||||
}
|
|
||||||
return frameNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<bool> GetLazyForEachChildIsGroup(RefPtr<LazyForEachNode> node)
|
|
||||||
{
|
{
|
||||||
std::optional<bool> isGroup;
|
std::optional<bool> isGroup;
|
||||||
auto children = node->GetChildren();
|
auto children = node->GetChildren();
|
||||||
@ -198,7 +188,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!(isGroup.has_value())) {
|
if (!(isGroup.has_value())) {
|
||||||
auto listNode = GetListFrameNode(node);
|
auto listNode = node->GetParentFrameNode();
|
||||||
CHECK_NULL_RETURN(listNode, isGroup);
|
CHECK_NULL_RETURN(listNode, isGroup);
|
||||||
auto wrapper = listNode->GetOrCreateChildByIndex(curIndex_);
|
auto wrapper = listNode->GetOrCreateChildByIndex(curIndex_);
|
||||||
CHECK_NULL_RETURN(wrapper, isGroup);
|
CHECK_NULL_RETURN(wrapper, isGroup);
|
||||||
@ -207,7 +197,7 @@ public:
|
|||||||
return isGroup;
|
return isGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalculateLazyForEachNode(RefPtr<LazyForEachNode> node)
|
void CalculateLazyForEachNode(RefPtr<UINode> node)
|
||||||
{
|
{
|
||||||
int32_t count = node->FrameCount();
|
int32_t count = node->FrameCount();
|
||||||
if (count <= 0) {
|
if (count <= 0) {
|
||||||
|
@ -13,24 +13,26 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
#include "core/components_ng/pattern/scrollable/scrollable_utils.h"
|
#include "core/components_ng/pattern/scrollable/scrollable_utils.h"
|
||||||
|
|
||||||
|
#include "core/components_ng/syntax/for_each_base_node.h"
|
||||||
#include "core/components_ng/syntax/lazy_for_each_node.h"
|
#include "core/components_ng/syntax/lazy_for_each_node.h"
|
||||||
#include "core/pipeline_ng/pipeline_context.h"
|
#include "core/pipeline_ng/pipeline_context.h"
|
||||||
namespace OHOS::Ace::NG {
|
namespace OHOS::Ace::NG {
|
||||||
namespace {
|
namespace {
|
||||||
std::vector<RefPtr<LazyForEachNode>> GetLazyForEachNodes(RefPtr<FrameNode>& host)
|
std::vector<RefPtr<ForEachBaseNode>> GetForEachNodes(RefPtr<FrameNode>& host)
|
||||||
{
|
{
|
||||||
std::vector<RefPtr<LazyForEachNode>> lazyNodes;
|
std::vector<RefPtr<ForEachBaseNode>> foreachNodes;
|
||||||
for (const auto& child : host->GetChildren()) {
|
for (const auto& child : host->GetChildren()) {
|
||||||
if (!AceType::InstanceOf<LazyForEachNode>(child)) {
|
if (!AceType::InstanceOf<ForEachBaseNode>(child)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto lazyNode = AceType::DynamicCast<LazyForEachNode>(child);
|
auto node = AceType::DynamicCast<ForEachBaseNode>(child);
|
||||||
if (!lazyNode) {
|
if (!node) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
lazyNodes.push_back(lazyNode);
|
foreachNodes.push_back(node);
|
||||||
}
|
}
|
||||||
return lazyNodes;
|
return foreachNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OutOfBottomOrRightBoundary(
|
bool OutOfBottomOrRightBoundary(
|
||||||
@ -93,8 +95,8 @@ int32_t GetScrollUpOrLeftItemIndex(Axis axis, float offset, int32_t start, int32
|
|||||||
return outIndex;
|
return outIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecycleItemsByIndex(int32_t start, int32_t end,
|
void RecycleItemsByIndex(
|
||||||
std::vector<RefPtr<LazyForEachNode>>& lazyNodes, LayoutWrapper* wrapper)
|
int32_t start, int32_t end, std::vector<RefPtr<ForEachBaseNode>>& lazyNodes, LayoutWrapper* wrapper)
|
||||||
{
|
{
|
||||||
wrapper->RecycleItemsByIndex(start, end);
|
wrapper->RecycleItemsByIndex(start, end);
|
||||||
for (const auto& node : lazyNodes) {
|
for (const auto& node : lazyNodes) {
|
||||||
@ -129,8 +131,8 @@ void ScrollableUtils::RecycleItemsOutOfBoundary(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto host = wrapper->GetHostNode();
|
auto host = wrapper->GetHostNode();
|
||||||
std::vector<RefPtr<LazyForEachNode>> lazyNodes = GetLazyForEachNodes(host);
|
std::vector<RefPtr<ForEachBaseNode>> foreachNodes = GetForEachNodes(host);
|
||||||
if (lazyNodes.empty()) {
|
if (foreachNodes.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (offset >= 0) {
|
if (offset >= 0) {
|
||||||
@ -138,13 +140,13 @@ void ScrollableUtils::RecycleItemsOutOfBoundary(
|
|||||||
if (inIndex >= end) {
|
if (inIndex >= end) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
RecycleItemsByIndex(inIndex + 1, end + 1, lazyNodes, wrapper);
|
RecycleItemsByIndex(inIndex + 1, end + 1, foreachNodes, wrapper);
|
||||||
} else {
|
} else {
|
||||||
int32_t outIndex = GetScrollUpOrLeftItemIndex(axis, offset, start, end, host);
|
int32_t outIndex = GetScrollUpOrLeftItemIndex(axis, offset, start, end, host);
|
||||||
if (outIndex <= start) {
|
if (outIndex <= start) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
RecycleItemsByIndex(start, outIndex, lazyNodes, wrapper);
|
RecycleItemsByIndex(start, outIndex, foreachNodes, wrapper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,5 +27,9 @@ build_component_ng("syntax_ng") {
|
|||||||
"lazy_layout_wrapper_builder.cpp",
|
"lazy_layout_wrapper_builder.cpp",
|
||||||
"node_content.cpp",
|
"node_content.cpp",
|
||||||
"repeat_model_ng.cpp",
|
"repeat_model_ng.cpp",
|
||||||
|
"repeat_node.cpp",
|
||||||
|
"repeat_virtual_scroll_caches.cpp",
|
||||||
|
"repeat_virtual_scroll_model_ng.cpp",
|
||||||
|
"repeat_virtual_scroll_node.cpp",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -23,16 +23,17 @@ class ACE_EXPORT ForEachBaseNode : public UINode {
|
|||||||
DECLARE_ACE_TYPE(ForEachBaseNode, UINode);
|
DECLARE_ACE_TYPE(ForEachBaseNode, UINode);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ForEachBaseNode(const std::string& tag, int32_t nodeId, bool isRoot = false)
|
ForEachBaseNode(const std::string& tag, int32_t nodeId, bool isRoot = false) : UINode(tag, nodeId, isRoot) {}
|
||||||
: UINode(tag, nodeId, isRoot) {}
|
|
||||||
virtual void MoveData(int32_t from, int32_t to) = 0;
|
virtual void MoveData(int32_t from, int32_t to) = 0;
|
||||||
virtual RefPtr<FrameNode> GetFrameNode(int32_t index) = 0;
|
virtual RefPtr<FrameNode> GetFrameNode(int32_t index) = 0;
|
||||||
|
virtual void RecycleItems(int32_t from, int32_t to) {}
|
||||||
virtual void FireOnMove(int32_t from, int32_t to)
|
virtual void FireOnMove(int32_t from, int32_t to)
|
||||||
{
|
{
|
||||||
if (onMoveEvent_) {
|
if (onMoveEvent_) {
|
||||||
onMoveEvent_(from, to);
|
onMoveEvent_(from, to);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::function<void(int32_t, int32_t)> onMoveEvent_;
|
std::function<void(int32_t, int32_t)> onMoveEvent_;
|
||||||
};
|
};
|
||||||
|
@ -388,7 +388,7 @@ void LazyForEachNode::DoRemoveChildInRenderTree(uint32_t index, bool isAll)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LazyForEachNode::DoSetActiveChildRange(int32_t start, int32_t end)
|
void LazyForEachNode::DoSetActiveChildRange(int32_t start, int32_t end, int32_t cacheStart, int32_t cacheEnd)
|
||||||
{
|
{
|
||||||
if (!builder_) {
|
if (!builder_) {
|
||||||
return;
|
return;
|
||||||
@ -473,7 +473,7 @@ RefPtr<FrameNode> LazyForEachNode::GetFrameNode(int32_t index)
|
|||||||
return AceType::DynamicCast<FrameNode>(child.second->GetFrameChildByIndex(0, true));
|
return AceType::DynamicCast<FrameNode>(child.second->GetFrameChildByIndex(0, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t LazyForEachNode::GetFrameNodeIndex(RefPtr<FrameNode> node, bool isExpanded)
|
int32_t LazyForEachNode::GetFrameNodeIndex(const RefPtr<FrameNode>& node, bool isExpanded)
|
||||||
{
|
{
|
||||||
if (!isExpanded) {
|
if (!isExpanded) {
|
||||||
return UINode::GetFrameNodeIndex(node, false);
|
return UINode::GetFrameNodeIndex(node, false);
|
||||||
|
@ -112,7 +112,7 @@ public:
|
|||||||
RefPtr<UINode> GetFrameChildByIndex(uint32_t index, bool needBuild, bool isCache = false,
|
RefPtr<UINode> GetFrameChildByIndex(uint32_t index, bool needBuild, bool isCache = false,
|
||||||
bool addToRenderTree = false) override;
|
bool addToRenderTree = false) override;
|
||||||
void DoRemoveChildInRenderTree(uint32_t index, bool isAll) override;
|
void DoRemoveChildInRenderTree(uint32_t index, bool isAll) override;
|
||||||
void DoSetActiveChildRange(int32_t start, int32_t end) override;
|
void DoSetActiveChildRange(int32_t start, int32_t end, int32_t cacheStart, int32_t cacheEnd) override;
|
||||||
|
|
||||||
const std::list<RefPtr<UINode>>& GetChildren() const override;
|
const std::list<RefPtr<UINode>>& GetChildren() const override;
|
||||||
void OnSetCacheCount(int32_t cacheCount, const std::optional<LayoutConstraintF>& itemConstraint) override
|
void OnSetCacheCount(int32_t cacheCount, const std::optional<LayoutConstraintF>& itemConstraint) override
|
||||||
@ -141,7 +141,7 @@ public:
|
|||||||
startIndex_ = start;
|
startIndex_ = start;
|
||||||
count_ = count;
|
count_ = count;
|
||||||
}
|
}
|
||||||
void RecycleItems(int32_t from, int32_t to);
|
void RecycleItems(int32_t from, int32_t to) override;
|
||||||
|
|
||||||
const RefPtr<LazyForEachBuilder>& GetBuilder() const
|
const RefPtr<LazyForEachBuilder>& GetBuilder() const
|
||||||
{
|
{
|
||||||
@ -160,7 +160,7 @@ public:
|
|||||||
void MoveData(int32_t from, int32_t to) override;
|
void MoveData(int32_t from, int32_t to) override;
|
||||||
void FireOnMove(int32_t from, int32_t to) override;
|
void FireOnMove(int32_t from, int32_t to) override;
|
||||||
RefPtr<FrameNode> GetFrameNode(int32_t index) override;
|
RefPtr<FrameNode> GetFrameNode(int32_t index) override;
|
||||||
int32_t GetFrameNodeIndex(RefPtr<FrameNode> node, bool isExpanded = true) override;
|
int32_t GetFrameNodeIndex(const RefPtr<FrameNode>& node, bool isExpanded = true) override;
|
||||||
void InitDragManager(const RefPtr<FrameNode>& childNode);
|
void InitDragManager(const RefPtr<FrameNode>& childNode);
|
||||||
void InitAllChilrenDragManager(bool init);
|
void InitAllChilrenDragManager(bool init);
|
||||||
private:
|
private:
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
#include "base/utils/utils.h"
|
#include "base/utils/utils.h"
|
||||||
#include "core/common/container.h"
|
#include "core/common/container.h"
|
||||||
#include "core/components_ng/base/view_stack_processor.h"
|
#include "core/components_ng/base/view_stack_processor.h"
|
||||||
#include "core/components_ng/syntax/for_each_node.h"
|
#include "core/components_ng/syntax/repeat_node.h"
|
||||||
#include "core/components_ng/syntax/syntax_item.h"
|
#include "core/components_ng/syntax/syntax_item.h"
|
||||||
|
|
||||||
namespace OHOS::Ace::NG {
|
namespace OHOS::Ace::NG {
|
||||||
@ -29,7 +29,7 @@ void RepeatModelNG::StartRender()
|
|||||||
ACE_SCOPED_TRACE("RepeatModelNG::StartRender");
|
ACE_SCOPED_TRACE("RepeatModelNG::StartRender");
|
||||||
auto* stack = ViewStackProcessor::GetInstance();
|
auto* stack = ViewStackProcessor::GetInstance();
|
||||||
auto nodeId = stack->ClaimNodeId();
|
auto nodeId = stack->ClaimNodeId();
|
||||||
auto repeatNode = ForEachNode::GetOrCreateRepeatNode(nodeId);
|
auto repeatNode = RepeatNode::GetOrCreateRepeatNode(nodeId);
|
||||||
stack->Push(repeatNode);
|
stack->Push(repeatNode);
|
||||||
|
|
||||||
// move current id array and children to temp
|
// move current id array and children to temp
|
||||||
@ -39,7 +39,7 @@ void RepeatModelNG::StartRender()
|
|||||||
void RepeatModelNG::FinishRender(std::list<int32_t>& removedElmtId)
|
void RepeatModelNG::FinishRender(std::list<int32_t>& removedElmtId)
|
||||||
{
|
{
|
||||||
auto* stack = ViewStackProcessor::GetInstance();
|
auto* stack = ViewStackProcessor::GetInstance();
|
||||||
auto repeatNode = AceType::DynamicCast<ForEachNode>(stack->GetMainElementNode());
|
auto repeatNode = AceType::DynamicCast<RepeatNode>(stack->GetMainElementNode());
|
||||||
CHECK_NULL_VOID(repeatNode);
|
CHECK_NULL_VOID(repeatNode);
|
||||||
repeatNode->FinishRepeatRender(removedElmtId);
|
repeatNode->FinishRepeatRender(removedElmtId);
|
||||||
stack->PopContainer();
|
stack->PopContainer();
|
||||||
@ -49,7 +49,7 @@ void RepeatModelNG::MoveChild(uint32_t fromIndex)
|
|||||||
{
|
{
|
||||||
ACE_SCOPED_TRACE("RepeatModelNG::MoveChild()");
|
ACE_SCOPED_TRACE("RepeatModelNG::MoveChild()");
|
||||||
auto* stack = ViewStackProcessor::GetInstance();
|
auto* stack = ViewStackProcessor::GetInstance();
|
||||||
auto repeatNode = AceType::DynamicCast<ForEachNode>(stack->GetMainElementNode());
|
auto repeatNode = AceType::DynamicCast<RepeatNode>(stack->GetMainElementNode());
|
||||||
CHECK_NULL_VOID(repeatNode);
|
CHECK_NULL_VOID(repeatNode);
|
||||||
repeatNode->MoveChild(fromIndex);
|
repeatNode->MoveChild(fromIndex);
|
||||||
}
|
}
|
||||||
@ -76,7 +76,7 @@ void RepeatModelNG::CreateNewChildFinish(const std::string& key)
|
|||||||
void RepeatModelNG::OnMove(std::function<void(int32_t, int32_t)>&& onMove)
|
void RepeatModelNG::OnMove(std::function<void(int32_t, int32_t)>&& onMove)
|
||||||
{
|
{
|
||||||
auto* stack = ViewStackProcessor::GetInstance();
|
auto* stack = ViewStackProcessor::GetInstance();
|
||||||
auto node = AceType::DynamicCast<ForEachNode>(stack->GetMainElementNode());
|
auto node = AceType::DynamicCast<RepeatNode>(stack->GetMainElementNode());
|
||||||
CHECK_NULL_VOID(node);
|
CHECK_NULL_VOID(node);
|
||||||
node->SetOnMove(std::move(onMove));
|
node->SetOnMove(std::move(onMove));
|
||||||
}
|
}
|
||||||
|
133
frameworks/core/components_ng/syntax/repeat_node.cpp
Normal file
133
frameworks/core/components_ng/syntax/repeat_node.cpp
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-2024 Huawei Device Co., Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "core/components_ng/syntax/repeat_node.h"
|
||||||
|
|
||||||
|
#include "base/log/ace_trace.h"
|
||||||
|
#include "core/components_ng/base/frame_node.h"
|
||||||
|
#include "core/components_ng/pattern/list/list_item_pattern.h"
|
||||||
|
#include "core/pipeline/base/element_register.h"
|
||||||
|
#include "core/pipeline_ng/pipeline_context.h"
|
||||||
|
|
||||||
|
namespace OHOS::Ace::NG {
|
||||||
|
|
||||||
|
// REPEAT
|
||||||
|
RefPtr<RepeatNode> RepeatNode::GetOrCreateRepeatNode(int32_t nodeId)
|
||||||
|
{
|
||||||
|
auto node = ElementRegister::GetInstance()->GetSpecificItemById<RepeatNode>(nodeId);
|
||||||
|
if (node) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
node = MakeRefPtr<RepeatNode>(nodeId);
|
||||||
|
ElementRegister::GetInstance()->AddUINode(node);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// REPEAT
|
||||||
|
void RepeatNode::CreateTempItems()
|
||||||
|
{
|
||||||
|
std::swap(ids_, tempIds_);
|
||||||
|
std::swap(ModifyChildren(), tempChildren_);
|
||||||
|
|
||||||
|
tempChildrenOfRepeat_ = std::vector<RefPtr<UINode>>(tempChildren_.begin(), tempChildren_.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// RepeatNode only
|
||||||
|
void RepeatNode::FinishRepeatRender(std::list<int32_t>& removedElmtId)
|
||||||
|
{
|
||||||
|
ACE_SCOPED_TRACE("RepeatNode::FinishRepeatRender");
|
||||||
|
|
||||||
|
// Required to build unordered_set of RefPtr<UINodes>
|
||||||
|
struct Hash {
|
||||||
|
size_t operator()(const RefPtr<UINode>& node) const
|
||||||
|
{
|
||||||
|
return node->GetId();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// includes "newly-added" and "reused" children
|
||||||
|
const auto& children = GetChildren();
|
||||||
|
|
||||||
|
std::unordered_set<RefPtr<UINode>, Hash>
|
||||||
|
newNodeSet(children.begin(), children.end());
|
||||||
|
|
||||||
|
// remove "unused" children
|
||||||
|
for (const auto& oldNode: tempChildrenOfRepeat_) {
|
||||||
|
if (newNodeSet.find(oldNode) == newNodeSet.end()) {
|
||||||
|
// Adding silently, so that upon removal node is a part the tree.
|
||||||
|
AddChild(oldNode, DEFAULT_NODE_SLOT, true);
|
||||||
|
// Remove and trigger all Detach callback.
|
||||||
|
RemoveChild(oldNode, true);
|
||||||
|
// Collect IDs of removed nodes starting from 'oldNode' (incl.)
|
||||||
|
CollectRemovedChildren({ oldNode }, removedElmtId, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tempChildren_.clear();
|
||||||
|
tempChildrenOfRepeat_.clear();
|
||||||
|
|
||||||
|
auto frameNode = GetParentFrameNode();
|
||||||
|
if (frameNode) {
|
||||||
|
frameNode->ChildrenUpdatedFrom(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RepeatNode only
|
||||||
|
void RepeatNode::MoveChild(uint32_t fromIndex)
|
||||||
|
{
|
||||||
|
// copy child from tempChildrenOfRepeat_[fromIndex] and append to children_
|
||||||
|
if (fromIndex < tempChildrenOfRepeat_.size()) {
|
||||||
|
auto& node = tempChildrenOfRepeat_.at(fromIndex);
|
||||||
|
AddChild(node, DEFAULT_NODE_SLOT, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repeat
|
||||||
|
void RepeatNode::SetOnMove(std::function<void(int32_t, int32_t)>&& onMove)
|
||||||
|
{
|
||||||
|
// To do
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOREACH
|
||||||
|
void RepeatNode::MoveData(int32_t from, int32_t to)
|
||||||
|
{
|
||||||
|
// To do
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepeatNode::FlushUpdateAndMarkDirty()
|
||||||
|
{
|
||||||
|
tempIds_.clear();
|
||||||
|
// mark parent dirty to flush measure.
|
||||||
|
MarkNeedSyncRenderTree(true);
|
||||||
|
MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT | PROPERTY_UPDATE_BY_CHILD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME called from where ?
|
||||||
|
RefPtr<FrameNode> RepeatNode::GetFrameNode(int32_t index)
|
||||||
|
{
|
||||||
|
return AceType::DynamicCast<FrameNode>(GetFrameChildByIndex(index, false, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepeatNode::InitDragManager(const RefPtr<UINode>& child)
|
||||||
|
{
|
||||||
|
// To do
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepeatNode::InitAllChildrenDragManager(bool init)
|
||||||
|
{
|
||||||
|
// To do
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace OHOS::Ace::NG
|
87
frameworks/core/components_ng/syntax/repeat_node.h
Normal file
87
frameworks/core/components_ng/syntax/repeat_node.h
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Huawei Device Co., Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_REPEAT_NODE_H
|
||||||
|
#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_REPEAT_NODE_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <list>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "base/utils/macros.h"
|
||||||
|
#include "core/components_ng/base/ui_node.h"
|
||||||
|
#include "core/components_ng/syntax/for_each_base_node.h"
|
||||||
|
#include "core/components_v2/inspector/inspector_constants.h"
|
||||||
|
|
||||||
|
namespace OHOS::Ace::NG {
|
||||||
|
|
||||||
|
class ACE_EXPORT RepeatNode : public ForEachBaseNode {
|
||||||
|
DECLARE_ACE_TYPE(RepeatNode, ForEachBaseNode);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static RefPtr<RepeatNode> GetOrCreateRepeatNode(int32_t nodeId);
|
||||||
|
|
||||||
|
explicit RepeatNode(int32_t nodeId) : ForEachBaseNode(V2::JS_REPEAT_ETS_TAG, nodeId) {}
|
||||||
|
|
||||||
|
~RepeatNode() override = default;
|
||||||
|
|
||||||
|
bool IsAtomicNode() const override
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateTempItems();
|
||||||
|
|
||||||
|
// RepeatNode only
|
||||||
|
void FinishRepeatRender(std::list<int32_t>& removedElmtId);
|
||||||
|
|
||||||
|
void FlushUpdateAndMarkDirty() override;
|
||||||
|
|
||||||
|
// RepeatNode only
|
||||||
|
void MoveChild(uint32_t fromIndex);
|
||||||
|
|
||||||
|
const std::list<std::string>& GetTempIds() const
|
||||||
|
{
|
||||||
|
return tempIds_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetIds(std::list<std::string>&& ids)
|
||||||
|
{
|
||||||
|
ids_ = std::move(ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetOnMove(std::function<void(int32_t, int32_t)>&& onMove);
|
||||||
|
void MoveData(int32_t from, int32_t to) override;
|
||||||
|
RefPtr<FrameNode> GetFrameNode(int32_t index) override;
|
||||||
|
void InitDragManager(const RefPtr<UINode>& childNode);
|
||||||
|
void InitAllChildrenDragManager(bool init);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::list<std::string> ids_;
|
||||||
|
|
||||||
|
// temp items use to compare each update.
|
||||||
|
std::list<std::string> tempIds_;
|
||||||
|
std::list<RefPtr<UINode>> tempChildren_;
|
||||||
|
|
||||||
|
// RepeatNode only
|
||||||
|
std::vector<RefPtr<UINode>> tempChildrenOfRepeat_;
|
||||||
|
|
||||||
|
ACE_DISALLOW_COPY_AND_MOVE(RepeatNode);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace OHOS::Ace::NG
|
||||||
|
|
||||||
|
#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_REPEAT_NODE_H
|
@ -0,0 +1,647 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Huawei Device Co., Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "core/components_ng/syntax/repeat_virtual_scroll_caches.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <optional>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
#include "base/log/log_wrapper.h"
|
||||||
|
#include "core/components_ng/base/frame_node.h"
|
||||||
|
#include "core/components_ng/base/view_stack_processor.h"
|
||||||
|
#include "core/pipeline/base/element_register.h"
|
||||||
|
|
||||||
|
namespace OHOS::Ace::NG {
|
||||||
|
|
||||||
|
bool KeySorterClass::operator()(const std::string& left, const std::string& right) const
|
||||||
|
{
|
||||||
|
return virtualScroll_->CompareKeyByIndexDistance(left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
RepeatVirtualScrollCaches::RepeatVirtualScrollCaches(const std::map<std::string, uint32_t>& cacheCountL24ttype,
|
||||||
|
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)
|
||||||
|
: // each ttype incl default has own L2 cache size
|
||||||
|
cacheCountL24ttype_(cacheCountL24ttype),
|
||||||
|
// request TS to create new sub-tree for given index or update existing
|
||||||
|
// update subtree cached for (old) index
|
||||||
|
// API might need to change to tell which old item to update
|
||||||
|
onCreateNode_(onCreateNode),
|
||||||
|
onUpdateNode_(onUpdateNode),
|
||||||
|
onGetTypes4Range_(onGetTypes4Range),
|
||||||
|
onGetKeys4Range_(onGetKeys4Range)
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::optional<std::string> RepeatVirtualScrollCaches::GetKey4Index(uint32_t index)
|
||||||
|
{
|
||||||
|
auto it = key4index_.find(index);
|
||||||
|
if (it == key4index_.end()) {
|
||||||
|
// request more keys from TS key gen
|
||||||
|
if (!FetchMoreKeysTTypes(index, index)) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return key4index_[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get more index -> key and index -> ttype from TS side
|
||||||
|
*/
|
||||||
|
bool RepeatVirtualScrollCaches::FetchMoreKeysTTypes(uint32_t from, uint32_t to)
|
||||||
|
{
|
||||||
|
// always request the same range for keys and ttype
|
||||||
|
// optimism by merging the two calls into one
|
||||||
|
std::list<std::string> keysFrom = onGetKeys4Range_(from, to);
|
||||||
|
std::list<std::string> ttypesFrom = onGetTypes4Range_(from, to);
|
||||||
|
auto requestCount = to - from + 1;
|
||||||
|
auto keySize = keysFrom.size();
|
||||||
|
if (keySize != requestCount) {
|
||||||
|
TAG_LOGE(AceLogTag::ACE_REPEAT, "fail to fetch keys: requst %{public}d, fetch %{public}d",
|
||||||
|
static_cast<int32_t>(keySize), requestCount);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto ttypeSize = ttypesFrom.size();
|
||||||
|
if (ttypeSize != requestCount) {
|
||||||
|
TAG_LOGE(AceLogTag::ACE_REPEAT, "fail to fetch keys: requst %{public}d, fetch %{public}d",
|
||||||
|
static_cast<int32_t>(keySize), requestCount);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill-in index maps
|
||||||
|
auto from1 = from;
|
||||||
|
for (const auto& key : keysFrom) {
|
||||||
|
key4index_[from1] = key;
|
||||||
|
index4Key_[key] = from1;
|
||||||
|
from1++;
|
||||||
|
}
|
||||||
|
from1 = from;
|
||||||
|
for (const auto& ttype : ttypesFrom) {
|
||||||
|
ttype4index_[from1] = ttype;
|
||||||
|
index4ttype_[ttype] = from1;
|
||||||
|
from1++;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get UINode for given index without create.
|
||||||
|
RefPtr<UINode> RepeatVirtualScrollCaches::GetNode4Index(uint32_t index) const
|
||||||
|
{
|
||||||
|
const auto it = key4index_.find(index);
|
||||||
|
if (it == key4index_.end()) {
|
||||||
|
TAG_LOGD(AceLogTag::ACE_REPEAT, "no UINode for index %{public}d", static_cast<int32_t>(index));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& key4Index = it->second;
|
||||||
|
const auto nodeIter = node4key_.find(key4Index);
|
||||||
|
if (nodeIter == node4key_.end()) {
|
||||||
|
TAG_LOGE(AceLogTag::ACE_REPEAT, "no UINode for index %{public}d and key %{public}s",
|
||||||
|
static_cast<int32_t>(index), key4Index.c_str());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return nodeIter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** scenario:
|
||||||
|
* Repeat gets updated due to data change.
|
||||||
|
* 1. TS calls RepeatVirtualScrollNode,
|
||||||
|
* then calls this function.
|
||||||
|
* 2. RepeatVirtualScrollNode requests layout to rebuild the UI
|
||||||
|
* 3. layout sends RepeatVirtualScrollNode::GetFrameChild calls
|
||||||
|
* 4. how to service GetFrameChild call:
|
||||||
|
* - first check for L1 keys (same template type) if any can be updated.
|
||||||
|
* These UINodes remain in the render tree.
|
||||||
|
* - if no L1 item, the look for L2 keys (same template type)
|
||||||
|
*/
|
||||||
|
void RepeatVirtualScrollCaches::InvalidateKeyAndTTypeCaches()
|
||||||
|
{
|
||||||
|
key4index_.clear();
|
||||||
|
index4Key_.clear();
|
||||||
|
ttype4index_.clear();
|
||||||
|
index4ttype_.clear();
|
||||||
|
|
||||||
|
// request new index -> key and index -> ttype
|
||||||
|
// only fetch keys for the active range
|
||||||
|
// note the updated key -> index reverse lookup is used
|
||||||
|
// to decide if a key is still used and its UINode
|
||||||
|
// should be packed. If key is not in the reverse map then
|
||||||
|
// its UINode is subject to update.
|
||||||
|
//
|
||||||
|
FetchMoreKeysTTypes(lastActiveRanges_[0].first, lastActiveRanges_[0].second);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* scenario: scroll, try to update an existing UINode
|
||||||
|
*
|
||||||
|
* find an key / UINode in L2 and update the UINode to
|
||||||
|
* render the data source item 'forIndex'.
|
||||||
|
*/
|
||||||
|
RefPtr<UINode> RepeatVirtualScrollCaches::UpdateFromL2(uint32_t forIndex)
|
||||||
|
{
|
||||||
|
const auto iterTType = ttype4index_.find(forIndex);
|
||||||
|
if (iterTType == ttype4index_.end()) {
|
||||||
|
TAG_LOGD(AceLogTag::ACE_REPEAT, "no ttype for index %{public}d", forIndex);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const auto& ttype = iterTType->second;
|
||||||
|
const auto iterNewKey = key4index_.find(forIndex);
|
||||||
|
if (iterNewKey == key4index_.end()) {
|
||||||
|
TAG_LOGW(AceLogTag::ACE_REPEAT, "no key for index %{public}d", forIndex);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const std::string& forKey = iterNewKey->second;
|
||||||
|
|
||||||
|
const auto& oldKey = GetL2KeyToUpdate(ttype);
|
||||||
|
if (!oldKey) {
|
||||||
|
// no key for this ttype available to update
|
||||||
|
TAG_LOGW(AceLogTag::ACE_REPEAT, "for index %{public}d, ttype %{public}s, no UINode found to update", forIndex,
|
||||||
|
ttype.c_str());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// call TS to do the RepeatItem update
|
||||||
|
onUpdateNode_(oldKey.value(), forIndex);
|
||||||
|
|
||||||
|
return HasUINodeBeenUpdated(ttype, oldKey.value(), forKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<UINode> RepeatVirtualScrollCaches::CreateNewNode(uint32_t forIndex)
|
||||||
|
{
|
||||||
|
// key key
|
||||||
|
const auto iter = key4index_.find(forIndex);
|
||||||
|
if (iter == key4index_.end()) {
|
||||||
|
TAG_LOGE(AceLogTag::ACE_REPEAT, "fail to create node of %{public}d", forIndex);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const auto& forKey = iter->second;
|
||||||
|
|
||||||
|
// see if node already created, just for safety
|
||||||
|
const auto nodeIter = node4key_.find(forKey);
|
||||||
|
if (nodeIter != node4key_.end()) {
|
||||||
|
// have a node for this key already, just return
|
||||||
|
return nodeIter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// need to create a new node for key
|
||||||
|
|
||||||
|
// get ttype
|
||||||
|
const auto ttypeIter = ttype4index_.find(forIndex);
|
||||||
|
if (ttypeIter == ttype4index_.end()) {
|
||||||
|
TAG_LOGE(AceLogTag::ACE_REPEAT, "fail to create %{public}d node due to type is missing", forIndex);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const auto& ttype = ttypeIter->second;
|
||||||
|
|
||||||
|
// swap the ViewStackProcessor instance for secondary while we run the item builder function
|
||||||
|
// so that its results can easily be obtained from it, does not disturb main ViewStackProcessor
|
||||||
|
NG::ScopedViewStackProcessor scopedViewStackProcessor;
|
||||||
|
auto* viewStack = NG::ViewStackProcessor::GetInstance();
|
||||||
|
|
||||||
|
// call TS side
|
||||||
|
onCreateNode_(forIndex);
|
||||||
|
|
||||||
|
const auto& node4Index = viewStack->Finish();
|
||||||
|
|
||||||
|
if (!node4Index) {
|
||||||
|
TAG_LOGE(AceLogTag::ACE_REPEAT,
|
||||||
|
"New Node create: For index %{public}d -> key %{public}s -> ttype %{public}s item builder FAILED to gen "
|
||||||
|
"FrameNode. ERROR",
|
||||||
|
forIndex, forKey.c_str(), ttype.c_str());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add node to node4key4ttype_
|
||||||
|
const auto node4KeyIter = node4key4ttype_.find(ttype);
|
||||||
|
if (node4KeyIter != node4key4ttype_.end()) {
|
||||||
|
node4KeyIter->second.emplace(forKey, node4Index);
|
||||||
|
} else {
|
||||||
|
std::unordered_map<std::string, RefPtr<UINode>> node4Key;
|
||||||
|
node4Key[forKey] = node4Index;
|
||||||
|
node4key4ttype_.emplace(ttype, std::move(node4Key));
|
||||||
|
}
|
||||||
|
|
||||||
|
// add node to node4key_
|
||||||
|
node4key_.emplace(forKey, node4Index);
|
||||||
|
return node4Index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepeatVirtualScrollCaches::ForEachL1IndexUINode(
|
||||||
|
const std::function<void(uint32_t index, const RefPtr<UINode>& node)>& cbFunc)
|
||||||
|
{
|
||||||
|
for (const auto& key : activeNodeKeysInL1_) {
|
||||||
|
const RefPtr<UINode>& node = node4key_[key];
|
||||||
|
const auto& indexIter = index4Key_.find(key);
|
||||||
|
if (indexIter == index4Key_.end()) {
|
||||||
|
TAG_LOGE(AceLogTag::ACE_REPEAT, "fail to get index for %{public}s key", key.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cbFunc(indexIter->second, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepeatVirtualScrollCaches::RecycleItemsByIndex(int32_t index)
|
||||||
|
{
|
||||||
|
auto keyIter = key4index_.find(index);
|
||||||
|
if (keyIter != key4index_.end()) {
|
||||||
|
activeNodeKeysInL1_.erase(keyIter->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iterate over all entries of L1 and call function for each entry
|
||||||
|
* if function returns true, entry is added to rebuild L1
|
||||||
|
* cbFunc return true, key
|
||||||
|
* cbFunc returns false drop from L1
|
||||||
|
*/
|
||||||
|
bool RepeatVirtualScrollCaches::RebuildL1(const std::function<bool(int32_t index, const RefPtr<UINode>& node)>& cbFunc)
|
||||||
|
{
|
||||||
|
std::unordered_set<std::string> l1Copy;
|
||||||
|
std::swap(l1Copy, activeNodeKeysInL1_);
|
||||||
|
bool modified = false;
|
||||||
|
for (const auto& key : l1Copy) {
|
||||||
|
const auto& indexIter = index4Key_.find(key);
|
||||||
|
if (indexIter == index4Key_.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto& node = node4key_[key];
|
||||||
|
int32_t index = indexIter->second;
|
||||||
|
if (cbFunc(index, node)) {
|
||||||
|
activeNodeKeysInL1_.emplace(key);
|
||||||
|
} else {
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepeatVirtualScrollCaches::SetLastActiveRange(uint32_t from, uint32_t to)
|
||||||
|
{
|
||||||
|
lastActiveRanges_[1] = lastActiveRanges_[0];
|
||||||
|
lastActiveRanges_[0] = { from, to };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Index of frameNode
|
||||||
|
* return -1 if not find the frameNode
|
||||||
|
*/
|
||||||
|
int32_t RepeatVirtualScrollCaches::GetFrameNodeIndex(const RefPtr<FrameNode>& frameNode) const
|
||||||
|
{
|
||||||
|
for (const auto& key : activeNodeKeysInL1_) {
|
||||||
|
const auto nodeIter = node4key_.find(key);
|
||||||
|
if (nodeIter == node4key_.end() || !nodeIter->second) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto& node = nodeIter->second->GetFrameChildByIndex(0, true);
|
||||||
|
if (node != frameNode) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto& indexIter = index4Key_.find(key);
|
||||||
|
if (indexIter == index4Key_.end()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return indexIter->second;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* intended scenario: scroll
|
||||||
|
* servicing GetFrameChild, search for key that can be updated.
|
||||||
|
*
|
||||||
|
* return a key whose UINode can be updated
|
||||||
|
* the key must not be in L1, i.e. activeNodeKeysInL1_
|
||||||
|
* the given ttype must match the template type the UINode for this key
|
||||||
|
* has been rendered for (this info is available from node4key4ttype_)
|
||||||
|
*/
|
||||||
|
std::optional<std::string> RepeatVirtualScrollCaches::GetL2KeyToUpdate(const std::string& ttype) const
|
||||||
|
{
|
||||||
|
const auto itNodes = node4key4ttype_.find(ttype);
|
||||||
|
if (itNodes == node4key4ttype_.end()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
const auto& keys2UINode = itNodes->second;
|
||||||
|
std::set<std::string, KeySorterClass> l2Keys = GetSortedL2KeysForTType(keys2UINode);
|
||||||
|
auto keyIter = l2Keys.rbegin();
|
||||||
|
if (keyIter == l2Keys.rend()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return *keyIter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* scenario: UI rebuild following key invalidation by TS side
|
||||||
|
* L1 includes keys that are no longer used, the linked UINodes
|
||||||
|
* should be updated.
|
||||||
|
*
|
||||||
|
* This function checks all L1 keys (of active UINodes) if the key
|
||||||
|
* can still be found from
|
||||||
|
* (previously updated following invalidation) key -> index map and
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
std::optional<std::string> RepeatVirtualScrollCaches::GetL1KeyToUpdate(const std::string& ttype) const
|
||||||
|
{
|
||||||
|
for (const auto& keyIter : activeNodeKeysInL1_) {
|
||||||
|
const std::string& key = keyIter;
|
||||||
|
if (index4Key_.find(key) == index4Key_.end()) {
|
||||||
|
// key is no longer used
|
||||||
|
// check if key rendered the expected ttype
|
||||||
|
const auto ttypeIter = node4key4ttype_.find(ttype);
|
||||||
|
if (ttypeIter != node4key4ttype_.end()) {
|
||||||
|
const std::unordered_map<std::string, RefPtr<UINode>>& node4Key = ttypeIter->second;
|
||||||
|
if (node4Key.find(key) != node4Key.end()) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* scenario: UINode of fromKey has been updated to render data for 'forKey'
|
||||||
|
* the template type (ttype) remains unchanged
|
||||||
|
* update node4key4ttype_ and node4key_ entries to use new key point to same UINode
|
||||||
|
*/
|
||||||
|
RefPtr<UINode> RepeatVirtualScrollCaches::HasUINodeBeenUpdated(
|
||||||
|
const std::string& ttype, const std::string& fromKey, const std::string& forKey)
|
||||||
|
{
|
||||||
|
// 1. update fromKey -> forKey in node4key4ttype_
|
||||||
|
const auto nodesIter = node4key4ttype_.find(ttype);
|
||||||
|
if (nodesIter != node4key4ttype_.end()) {
|
||||||
|
auto& node4key = nodesIter->second;
|
||||||
|
auto iter = node4key.find(fromKey);
|
||||||
|
if (iter != node4key.end()) {
|
||||||
|
const auto& node = iter->second;
|
||||||
|
node4key.erase(iter);
|
||||||
|
node4key.emplace(forKey, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. update the key: fromKey to forKey in node4key_
|
||||||
|
auto iter = node4key_.find(fromKey);
|
||||||
|
if (iter != node4key_.end()) {
|
||||||
|
const auto& node = iter->second;
|
||||||
|
node4key_.erase(iter);
|
||||||
|
node4key_.emplace(forKey, node);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** scenario: keys cache has been updated
|
||||||
|
*
|
||||||
|
* find which keys in key -> UINode map are no longer used
|
||||||
|
* returned set entries are pairs:
|
||||||
|
* pair.first: is this key a L1 item,
|
||||||
|
* pair.second: key
|
||||||
|
*/
|
||||||
|
void RepeatVirtualScrollCaches::FindUnusedKeys(std::set<std::pair<bool, std::string>>& result) const
|
||||||
|
{
|
||||||
|
for (const auto& iter : node4key_) {
|
||||||
|
const std::string key = iter.first;
|
||||||
|
const auto indexIter = index4Key_.find(key);
|
||||||
|
if (indexIter == index4Key_.end()) {
|
||||||
|
// key is no longer used
|
||||||
|
// is it in L1 ?
|
||||||
|
const bool keyInL1 = (index4Key_.find(key) != index4Key_.end());
|
||||||
|
result.emplace(keyInL1, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* scenario: in idle process , following GetChildren()
|
||||||
|
* execute purge()
|
||||||
|
*
|
||||||
|
* enforce L2 cacheCount for each ttype
|
||||||
|
* logical L2 cache is map key->UINode map filtered out L1 keys
|
||||||
|
* purge by by deleting UINodes, delete their entry from
|
||||||
|
* node4key4ttype_ and node4key_
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool RepeatVirtualScrollCaches::Purge()
|
||||||
|
{
|
||||||
|
bool didMakeChanges = false;
|
||||||
|
for (auto& itTType : node4key4ttype_) {
|
||||||
|
const auto& ttype = itTType.first;
|
||||||
|
auto& uiNode4Key = itTType.second;
|
||||||
|
const uint32_t cacheCount = cacheCountL24ttype_[ttype];
|
||||||
|
std::set<std::string, KeySorterClass> l2Keys = GetSortedL2KeysForTType(uiNode4Key);
|
||||||
|
|
||||||
|
// l2_keys is sorted by increasing distance from lastActiveRange
|
||||||
|
// will drop those keys and their UINodes with largest distance
|
||||||
|
// improvement idea: in addition to distance from range use the
|
||||||
|
// scroll direction for selecting these keys
|
||||||
|
auto safeDist = std::min(cacheCount, static_cast<uint32_t>(l2Keys.size()));
|
||||||
|
auto itL2Key = std::next(l2Keys.begin(), safeDist);
|
||||||
|
|
||||||
|
while (itL2Key != l2Keys.end()) {
|
||||||
|
// delete remaining keys
|
||||||
|
uiNode4Key.erase(*itL2Key);
|
||||||
|
node4key_.erase(*itL2Key);
|
||||||
|
// check out transition case.
|
||||||
|
itL2Key++;
|
||||||
|
didMakeChanges = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return didMakeChanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* given key return the index position (reverse lookup)
|
||||||
|
* invalidated keys (after Repeat rerender/ data change)
|
||||||
|
* are keys for which no index exists anymore,
|
||||||
|
* method returns int max value for these.
|
||||||
|
* int max value causes that distance from active range is max
|
||||||
|
* these keys will be selected for update first.
|
||||||
|
*/
|
||||||
|
uint32_t RepeatVirtualScrollCaches::GetIndex4Key(const std::string& key) const
|
||||||
|
{
|
||||||
|
auto it = index4Key_.find(key);
|
||||||
|
if (it != index4Key_.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
// key is no longer used
|
||||||
|
// return max uint32_t value
|
||||||
|
return std::numeric_limits<uint32_t>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* for given index return distance from active range,
|
||||||
|
* or 0 if within active range
|
||||||
|
* distance is int max for invalidated keys
|
||||||
|
*
|
||||||
|
* instead of just using previous active range
|
||||||
|
* use the ranges informed by previous two SetActiveRaneg calls.
|
||||||
|
* Obtain the scroll direction and use it to calc the distance.
|
||||||
|
* Items left 'behind' when scrolling get larger distance and are more
|
||||||
|
* likely updated or purged from L2 cache.
|
||||||
|
*/
|
||||||
|
int32_t RepeatVirtualScrollCaches::GetDistanceFromRange(uint32_t index) const
|
||||||
|
{
|
||||||
|
int32_t last[2] = { lastActiveRanges_[0].first, lastActiveRanges_[0].second };
|
||||||
|
int32_t prev[2] = { lastActiveRanges_[1].first, lastActiveRanges_[1].second };
|
||||||
|
|
||||||
|
// this is experimental optimization, based on scrolling detection
|
||||||
|
// here we assume this is a scrolling, if previous range and last range has
|
||||||
|
// not empty intersection
|
||||||
|
|
||||||
|
// if scrolling up, return 0 for any lower index
|
||||||
|
if (last[0] < prev[0] && prev[0] < last[1]) {
|
||||||
|
if (index < last[0]) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if scrolling down, return 0 for any greater index
|
||||||
|
if (last[0] < prev[1] && prev[1] < last[1]) {
|
||||||
|
if (index > last[1]) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is not scrolling
|
||||||
|
if (index < last[0]) {
|
||||||
|
return last[0] - index;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index > last[1]) {
|
||||||
|
return index - last[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* scenario: find L1 key that should be updated
|
||||||
|
* choose the key whose index is the furthest away from active range
|
||||||
|
* given two keys compare their distFromRange
|
||||||
|
*/
|
||||||
|
bool RepeatVirtualScrollCaches::CompareKeyByIndexDistance(const std::string& key1, const std::string& key2) const
|
||||||
|
{
|
||||||
|
return GetDistanceFromRange(GetIndex4Key(key1)) < GetDistanceFromRange(GetIndex4Key(key2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* scenario: find L1 key(s) that should be updated
|
||||||
|
*
|
||||||
|
* return a sorted set of L2 keys, sorted by increasing distance from active range
|
||||||
|
*/
|
||||||
|
std::set<std::string, KeySorterClass> RepeatVirtualScrollCaches::GetSortedL2KeysForTType(
|
||||||
|
const std::unordered_map<std::string, RefPtr<UINode>>& uiNode4Key) const
|
||||||
|
{
|
||||||
|
KeySorterClass sorter(this);
|
||||||
|
std::set<std::string, KeySorterClass> l2Keys(sorter);
|
||||||
|
for (const auto& itUINode : uiNode4Key) {
|
||||||
|
const auto& key = itUINode.first;
|
||||||
|
if (activeNodeKeysInL1_.find(key) == activeNodeKeysInL1_.end()) {
|
||||||
|
// key is not in L1
|
||||||
|
// add to l2Keys
|
||||||
|
l2Keys.emplace(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l2Keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RepeatVirtualScrollCaches::DumpL1() const
|
||||||
|
{
|
||||||
|
std::string result = "activeNodeKeysInL1_: size=" + std::to_string(activeNodeKeysInL1_.size()) + "--------------\n";
|
||||||
|
for (const auto& it : activeNodeKeysInL1_) {
|
||||||
|
const std::string& key = it;
|
||||||
|
result += "\", node: " + DumpUINodeWithKey(key) + "\n";
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RepeatVirtualScrollCaches::DumpL2() const
|
||||||
|
{
|
||||||
|
std::set<std::string, KeySorterClass> l2KeyResult = GetSortedL2KeysForTType(node4key_);
|
||||||
|
|
||||||
|
std::string result =
|
||||||
|
"l2_keys (sorted by distance): size=" + std::to_string(l2KeyResult.size()) + "--------------\n";
|
||||||
|
for (const auto& it : l2KeyResult) {
|
||||||
|
result += " \"" + it + "\", node: " + DumpUINodeWithKey(it) + "\n";
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RepeatVirtualScrollCaches::DumpKey4Index() const
|
||||||
|
{
|
||||||
|
std::string result = "key4index_: size=" + std::to_string(key4index_.size()) + "--------------\n";
|
||||||
|
for (const auto& it : key4index_) {
|
||||||
|
result += " " + std::to_string(it.first) + " -> \"" + it.second +
|
||||||
|
"\", node: " + DumpUINodeWithKey(it.second) + "\n";
|
||||||
|
}
|
||||||
|
result += "index4Key_: size=" + std::to_string(index4Key_.size()) + "--------------\n";
|
||||||
|
for (const auto& it : index4Key_) {
|
||||||
|
result += " \"" + it.first + "\" -> " + std::to_string(it.second) + "\n";
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RepeatVirtualScrollCaches::DumpTType4Index() const
|
||||||
|
{
|
||||||
|
std::string result = "ttype4index_: size=" + std::to_string(ttype4index_.size()) + "--------------\n";
|
||||||
|
for (const auto& it : ttype4index_) {
|
||||||
|
result += " " + std::to_string(it.first) + " -> \"" + it.second + "\n";
|
||||||
|
}
|
||||||
|
result += "index4ttype_: size=" + std::to_string(index4ttype_.size()) + "--------------\n";
|
||||||
|
for (const auto& it : index4ttype_) {
|
||||||
|
result += " \"" + it.first + "\" -> " + std::to_string(it.second) + "\n";
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RepeatVirtualScrollCaches::DumpUINode4Key4TType() const
|
||||||
|
{
|
||||||
|
std::string result = "node4key_: size=" + std::to_string(node4key_.size()) + "--------------\n";
|
||||||
|
for (const auto& it : node4key_) {
|
||||||
|
result += " \"" + it.first + "\" -> node: " + it.second->GetTag() + "(" + std::to_string(it.second->GetId()) +
|
||||||
|
") \n";
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RepeatVirtualScrollCaches::DumpUINode4Key() const
|
||||||
|
{
|
||||||
|
std::string result = "node4key4ttype_: size=" + std::to_string(node4key4ttype_.size()) + "--------------\n";
|
||||||
|
for (const auto& ttypeIter : node4key4ttype_) {
|
||||||
|
const auto& ttype = ttypeIter.first;
|
||||||
|
const auto& node4key = ttypeIter.second;
|
||||||
|
result += "ttype " + ttype + ": node4key: size=" + std::to_string(node4key.size()) + "--------------\n";
|
||||||
|
for (const auto& it : node4key) {
|
||||||
|
result += " \"" + it.first + "\" -> node: " + it.second->GetTag() + "(" +
|
||||||
|
std::to_string(it.second->GetId()) + ") \n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RepeatVirtualScrollCaches::DumpUINodeWithKey(const std::string& key) const
|
||||||
|
{
|
||||||
|
const auto it = node4key_.find(key);
|
||||||
|
return (it == node4key_.end()) ? "no UINode on file"
|
||||||
|
: it->second->GetTag() + "(" + std::to_string(it->second->GetId()) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RepeatVirtualScrollCaches::DumpUINode(const RefPtr<UINode>& node) const
|
||||||
|
{
|
||||||
|
return (node == nullptr) ? "UINode nullptr" : node->GetTag() + "(" + std::to_string(node->GetId()) + ")";
|
||||||
|
}
|
||||||
|
} // namespace OHOS::Ace::NG
|
@ -0,0 +1,269 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Huawei Device Co., Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_REPEAT_VIRTUAL_SCROLL_CACHES_H
|
||||||
|
#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_REPEAT_VIRTUAL_SCROLL_CACHES_H
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
#include "core/components_ng/base/ui_node.h"
|
||||||
|
|
||||||
|
namespace OHOS::Ace::NG {
|
||||||
|
|
||||||
|
// custom sorting for std::set only works with struct
|
||||||
|
// with operator() inside
|
||||||
|
class RepeatVirtualScrollCaches;
|
||||||
|
struct KeySorterClass {
|
||||||
|
const RepeatVirtualScrollCaches* virtualScroll_;
|
||||||
|
|
||||||
|
explicit KeySorterClass(const RepeatVirtualScrollCaches* virtualScroll) : virtualScroll_(virtualScroll) {}
|
||||||
|
bool operator()(const std::string& left, const std::string& right) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RepeatVirtualScrollCaches {
|
||||||
|
friend struct KeySorterClass;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RepeatVirtualScrollCaches(const std::map<std::string, uint32_t>& cacheCountL24ttype,
|
||||||
|
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);
|
||||||
|
|
||||||
|
/** scenario:
|
||||||
|
* Repeat gets updated due to data change.
|
||||||
|
* 1. TS calls RepeatVirtualScrollNode,
|
||||||
|
* then calls this function.
|
||||||
|
* 2. RepeatVirtualScrollNode requests layout to rebuild the UI
|
||||||
|
* 3. layout sends RepeatVirtualScrollNode::GetFrameChild calls
|
||||||
|
* 4. how to service GetFrameChild call:
|
||||||
|
* - first check for L1 keys (same template type) if any can be updated.
|
||||||
|
* These UINodes remain in the render tree.
|
||||||
|
* - if no L1 item, the look for L2 keys (same template type)
|
||||||
|
*/
|
||||||
|
void InvalidateKeyAndTTypeCaches();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* scenario: scroll, try to update an existing UINode
|
||||||
|
*
|
||||||
|
* find an key / UINode in L2 and update the UINode to
|
||||||
|
* render the data source item 'forIndex'.
|
||||||
|
*/
|
||||||
|
RefPtr<UINode> UpdateFromL2(uint32_t forIndex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* request TS to create a new node for given index / key/
|
||||||
|
*/
|
||||||
|
RefPtr<UINode> CreateNewNode(uint32_t forIndex);
|
||||||
|
|
||||||
|
// iterate over L1 keys, not allowed to modify L1
|
||||||
|
void ForEachL1IndexUINode(const std::function<void(uint32_t index, const RefPtr<UINode>& node)>& cbFunc);
|
||||||
|
|
||||||
|
void RecycleItemsByIndex(int32_t index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* for given index get key
|
||||||
|
* fetch from TS if not in cache
|
||||||
|
* return false if index out of range
|
||||||
|
*/
|
||||||
|
std::optional<std::string> GetKey4Index(uint32_t index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iterate over all entries of L1 and call function for each entry
|
||||||
|
* if function returns true, entry is added to rebuild L1, otherwise it is moved to L2
|
||||||
|
* cbFunc return true, [index, key] pair stays in L1 (index remains unchanged)
|
||||||
|
* cbFunc returns false, enqueue key in L2
|
||||||
|
*/
|
||||||
|
bool RebuildL1(const std::function<bool(int32_t index, const RefPtr<UINode>& node)>& cbFunc);
|
||||||
|
|
||||||
|
int32_t GetFrameNodeIndex(const RefPtr<FrameNode>& frameNode) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* scenario: in idle process , following GetChildren()
|
||||||
|
*
|
||||||
|
* enforce L2 cacheCount for each ttype
|
||||||
|
* by deleting UINodes, delete their entry from
|
||||||
|
* node4key4ttype_ and node4key_
|
||||||
|
* any other processing steps needed before UINode
|
||||||
|
* tree can be deleted
|
||||||
|
*/
|
||||||
|
bool Purge();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return the UINode for given index
|
||||||
|
* bool return indicates if in L1
|
||||||
|
*
|
||||||
|
* resolve index -> key -> UINode
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
RefPtr<UINode> GetNode4Index(uint32_t forIndex) const;
|
||||||
|
|
||||||
|
void AddKeyToL1(const std::string& key)
|
||||||
|
{
|
||||||
|
activeNodeKeysInL1_.emplace(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsInL1Cache(const std::string& key) const
|
||||||
|
{
|
||||||
|
return activeNodeKeysInL1_.find(key) != activeNodeKeysInL1_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::unordered_map<std::string, RefPtr<UINode>>& GetAllNodes() const
|
||||||
|
{
|
||||||
|
return node4key_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* memorize last active range(s)
|
||||||
|
*/
|
||||||
|
void SetLastActiveRange(uint32_t from, uint32_t to);
|
||||||
|
|
||||||
|
// formatting internal structures to string for debug output
|
||||||
|
// and possibly in modified form for DFX in the future
|
||||||
|
std::string DumpL1() const;
|
||||||
|
std::string DumpL2() const;
|
||||||
|
std::string DumpKey4Index() const;
|
||||||
|
std::string DumpTType4Index() const;
|
||||||
|
std::string DumpUINode4Key4TType() const;
|
||||||
|
std::string DumpUINode4Key() const;
|
||||||
|
|
||||||
|
std::string DumpUINodeWithKey(const std::string& key) const;
|
||||||
|
std::string DumpUINode(const RefPtr<UINode>& node) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* intended scenario: scroll
|
||||||
|
* servicing GetFrameChild, search for key that can be updated.
|
||||||
|
*
|
||||||
|
* return a key whose UINode can be updated
|
||||||
|
* the key must not be in L1, i.e. activeNodeKeysInL1_
|
||||||
|
* the given ttype must match the template type the UINode for this key
|
||||||
|
* has been rendered for (this info is available from node4key4ttype_)
|
||||||
|
*/
|
||||||
|
std::optional<std::string> GetL2KeyToUpdate(const std::string& ttype) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* scenario: UI rebuild following key invalidation by TS side
|
||||||
|
* L1 includes keys that are no longer used, the linked UINodes
|
||||||
|
* should be updated.
|
||||||
|
*
|
||||||
|
* This function checks all L1 keys (of active UINodes) if the key
|
||||||
|
* can still be found from
|
||||||
|
* (previously updated following invalidation) key -> index map and
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
std::optional<std::string> GetL1KeyToUpdate(const std::string& ttype) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* scenario: UINode of fromKey has been updated to render data for 'forKey'
|
||||||
|
* the template type (ttype) remains unchanged
|
||||||
|
* update node4key4ttype_ and node4key_ entries to use new key point to same UINode
|
||||||
|
*/
|
||||||
|
RefPtr<UINode> HasUINodeBeenUpdated(
|
||||||
|
const std::string& ttype, const std::string& fromKey, const std::string& forKey);
|
||||||
|
|
||||||
|
/** scenario: keys cache has been updated
|
||||||
|
*
|
||||||
|
* find which keys in key -> UINode map are no longer used
|
||||||
|
* returned set entries are pairs:
|
||||||
|
* pair.first: is this key a L1 item,
|
||||||
|
* pair.second: key
|
||||||
|
*/
|
||||||
|
void FindUnusedKeys(std::set<std::pair<bool, std::string>>& result) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* given key return the index position (reverse lookup)
|
||||||
|
* invalidated keys (after Repeat rerender/ data change)
|
||||||
|
* are keys for which no index exists anymore,
|
||||||
|
* method returns int max value for these.
|
||||||
|
* int max value causes that distance from active range is max
|
||||||
|
* these keys will be selected for update first.
|
||||||
|
*/
|
||||||
|
uint32_t GetIndex4Key(const std::string& key) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* for given index return distance from active range,
|
||||||
|
* or 0 if within active range
|
||||||
|
* distance is int max for invalidated keys
|
||||||
|
*/
|
||||||
|
int32_t GetDistanceFromRange(uint32_t index) const;
|
||||||
|
/**
|
||||||
|
* scenario: find L1 key that should be updated
|
||||||
|
* choose the key whose index is the furthest away from active range
|
||||||
|
* given two keys compare their distFromRange
|
||||||
|
*/
|
||||||
|
bool CompareKeyByIndexDistance(const std::string& key1, const std::string& key2) const;
|
||||||
|
|
||||||
|
std::set<std::string, KeySorterClass> GetSortedL2KeysForTType(
|
||||||
|
const std::unordered_map<std::string, RefPtr<UINode>>& uiNode4Key) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get more index -> key and index -> ttype from TS side
|
||||||
|
*/
|
||||||
|
bool FetchMoreKeysTTypes(uint32_t from, uint32_t to);
|
||||||
|
|
||||||
|
// Map ttype -> cacheSize. Each ttype incl default has own L2 size
|
||||||
|
std::map<std::string, uint32_t> cacheCountL24ttype_;
|
||||||
|
|
||||||
|
// request TS to create new sub-tree for given index or update existing
|
||||||
|
// update subtree cached for (old) index
|
||||||
|
// API might need to change to tell which old item to update
|
||||||
|
std::function<void(uint32_t)> onCreateNode_;
|
||||||
|
std::function<void(const std::string&, uint32_t)> onUpdateNode_;
|
||||||
|
|
||||||
|
// get index -> key for given range
|
||||||
|
// resulting list starts with 'from' but might end before 'to' if Array shorter
|
||||||
|
std::function<std::list<std::string>(uint32_t, uint32_t)> onGetTypes4Range_;
|
||||||
|
|
||||||
|
// get index -> ttype for given range
|
||||||
|
// resulting list starts with 'from' but might end before 'to' if Array shorter
|
||||||
|
std::function<std::list<std::string>(uint32_t, uint32_t)> onGetKeys4Range_;
|
||||||
|
|
||||||
|
// memorize active ranges of past 2 (last, prev)
|
||||||
|
// SetActiveRange calls and use to calc scroll direction
|
||||||
|
std::pair<uint32_t, uint32_t> lastActiveRanges_[2] = { { 0, 0 }, { 0, 0 } };
|
||||||
|
|
||||||
|
// keys of active nodes, UINodes must be on the UI tree,
|
||||||
|
// this list is also known as L1
|
||||||
|
// all keys not in this set are in "L2"
|
||||||
|
std::unordered_set<std::string> activeNodeKeysInL1_;
|
||||||
|
|
||||||
|
// L1
|
||||||
|
// index -> key and reverse
|
||||||
|
// lazy request from TS side can be invalidated
|
||||||
|
std::unordered_map<uint32_t, std::string> key4index_;
|
||||||
|
std::unordered_map<std::string, uint32_t> index4Key_;
|
||||||
|
|
||||||
|
// index -> ttype
|
||||||
|
// lazy request from TS side can be invalidated
|
||||||
|
std::unordered_map<uint32_t, std::string> ttype4index_;
|
||||||
|
std::unordered_map<std::string, uint32_t> index4ttype_;
|
||||||
|
|
||||||
|
// Map ttype -> Map key -> UINode
|
||||||
|
std::unordered_map<std::string, std::unordered_map<std::string, RefPtr<UINode>>> node4key4ttype_;
|
||||||
|
|
||||||
|
// Map Map key -> UINode
|
||||||
|
std::unordered_map<std::string, RefPtr<UINode>> node4key_;
|
||||||
|
}; // class NodeCache
|
||||||
|
|
||||||
|
} // namespace OHOS::Ace::NG
|
||||||
|
|
||||||
|
#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_REPEAT_VIRTUAL_SCROLL_CACHES_H
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Huawei Device Co., Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_SYNTAX_REPEAT_MODEL_H
|
||||||
|
#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_SYNTAX_REPEAT_MODEL_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "base/utils/macros.h"
|
||||||
|
|
||||||
|
namespace OHOS::Ace {
|
||||||
|
|
||||||
|
class ACE_EXPORT RepeatVirtualScrollModel {
|
||||||
|
public:
|
||||||
|
RepeatVirtualScrollModel() = default;
|
||||||
|
virtual ~RepeatVirtualScrollModel() = default;
|
||||||
|
|
||||||
|
static RepeatVirtualScrollModel* GetInstance();
|
||||||
|
virtual void Create(
|
||||||
|
uint32_t totalCount,
|
||||||
|
const std::map<std::string, uint32_t>& templateCachedCountMap,
|
||||||
|
const std::function<void(uint32_t forIndex)>& onCreateNode,
|
||||||
|
const std::function<void(const std::string& fromKey, uint32_t forIndex)>& onUpdateNode,
|
||||||
|
const std::function<std::list<std::string>(uint32_t from, uint32_t to)>& onGetKeys4Range,
|
||||||
|
const std::function<std::list<std::string>(uint32_t from, uint32_t to)>& onGetTypes4Range) = 0;
|
||||||
|
virtual void InvalidateKeyCache(uint32_t totalCount) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::unique_ptr<RepeatVirtualScrollModel> instance_;
|
||||||
|
};
|
||||||
|
} // namespace OHOS::Ace
|
||||||
|
|
||||||
|
#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_REPEAT_H
|
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Huawei Device Co., Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "core/components_ng/syntax/repeat_virtual_scroll_model_ng.h"
|
||||||
|
|
||||||
|
#include "base/utils/utils.h"
|
||||||
|
#include "core/common/container.h"
|
||||||
|
#include "core/components_ng/base/view_stack_processor.h"
|
||||||
|
#include "core/components_ng/syntax/repeat_virtual_scroll_node.h"
|
||||||
|
#include "core/components_ng/syntax/syntax_item.h"
|
||||||
|
|
||||||
|
namespace OHOS::Ace::NG {
|
||||||
|
|
||||||
|
void RepeatVirtualScrollModelNG::Create(
|
||||||
|
uint32_t totalCount,
|
||||||
|
const std::map<std::string, uint32_t>& templateCachedCountMap,
|
||||||
|
const std::function<void(uint32_t forIndex)>& onCreateNode,
|
||||||
|
const std::function<void(const std::string& fromKey, uint32_t forIndex)>& onUpdateNode,
|
||||||
|
const std::function<std::list<std::string>(uint32_t from, uint32_t to)>& onGetKeys4Range,
|
||||||
|
const std::function<std::list<std::string>(uint32_t from, uint32_t to)>& onGetTypes4Range)
|
||||||
|
{
|
||||||
|
ACE_SCOPED_TRACE("RepeatVirtualScrollModelNG::Create");
|
||||||
|
auto* stack = ViewStackProcessor::GetInstance();
|
||||||
|
auto nodeId = stack->ClaimNodeId();
|
||||||
|
auto repeatNode = RepeatVirtualScrollNode::GetOrCreateRepeatNode(
|
||||||
|
nodeId,
|
||||||
|
totalCount,
|
||||||
|
templateCachedCountMap,
|
||||||
|
onCreateNode,
|
||||||
|
onUpdateNode,
|
||||||
|
onGetKeys4Range,
|
||||||
|
onGetTypes4Range
|
||||||
|
);
|
||||||
|
stack->Push(repeatNode);
|
||||||
|
stack->PopContainer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepeatVirtualScrollModelNG::InvalidateKeyCache(uint32_t totalCount)
|
||||||
|
{
|
||||||
|
auto* stack = ViewStackProcessor::GetInstance();
|
||||||
|
auto nodeId = stack->ClaimNodeId();
|
||||||
|
auto repeatNode = ElementRegister::GetInstance()->GetSpecificItemById<RepeatVirtualScrollNode>(nodeId);
|
||||||
|
CHECK_NULL_VOID(repeatNode);
|
||||||
|
repeatNode->UpdateTotalCount(totalCount);
|
||||||
|
repeatNode->InvalidateKeyCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace OHOS::Ace::NG
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Huawei Device Co., Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_REPEAT_H
|
||||||
|
#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_REPEAT_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
|
#include <list>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base/utils/macros.h"
|
||||||
|
#include "core/components_ng/syntax/repeat_virtual_scroll_model.h"
|
||||||
|
|
||||||
|
namespace OHOS::Ace::NG {
|
||||||
|
|
||||||
|
class ACE_EXPORT RepeatVirtualScrollModelNG : public RepeatVirtualScrollModel {
|
||||||
|
public:
|
||||||
|
void Create(
|
||||||
|
uint32_t totalCount,
|
||||||
|
const std::map<std::string, uint32_t>& templateCachedCountMap,
|
||||||
|
const std::function<void(uint32_t forIndex)>& onCreateNode,
|
||||||
|
const std::function<void(const std::string& fromKey, uint32_t forIndex)>& onUpdateNode,
|
||||||
|
const std::function<std::list<std::string>(uint32_t from, uint32_t to)>& onGetKeys4Range,
|
||||||
|
const std::function<std::list<std::string>(uint32_t from, uint32_t to)>& onGetTypes4Range) override;
|
||||||
|
|
||||||
|
void InvalidateKeyCache(uint32_t totalCount) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace OHOS::Ace::NG
|
||||||
|
|
||||||
|
#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_REPEAT_H
|
@ -0,0 +1,321 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-2024 Huawei Device Co., Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "core/components_ng/syntax/repeat_virtual_scroll_node.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "base/log/ace_trace.h"
|
||||||
|
#include "base/log/log_wrapper.h"
|
||||||
|
#include "core/components_ng/base/frame_node.h"
|
||||||
|
#include "core/components_ng/pattern/list/list_item_pattern.h"
|
||||||
|
#include "core/pipeline/base/element_register.h"
|
||||||
|
#include "core/pipeline_ng/pipeline_context.h"
|
||||||
|
|
||||||
|
namespace OHOS::Ace::NG {
|
||||||
|
|
||||||
|
// REPEAT
|
||||||
|
RefPtr<RepeatVirtualScrollNode> RepeatVirtualScrollNode::GetOrCreateRepeatNode(int32_t nodeId, uint32_t totalCount,
|
||||||
|
const std::map<std::string, 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)
|
||||||
|
{
|
||||||
|
auto node = ElementRegister::GetInstance()->GetSpecificItemById<RepeatVirtualScrollNode>(nodeId);
|
||||||
|
if (node) {
|
||||||
|
node->UpdateTotalCount(totalCount);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
node = MakeRefPtr<RepeatVirtualScrollNode>(
|
||||||
|
nodeId, totalCount, templateCachedCountMap, onCreateNode, onUpdateNode, onGetKeys4Range, onGetTypes4Range);
|
||||||
|
|
||||||
|
ElementRegister::GetInstance()->AddUINode(node);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
RepeatVirtualScrollNode::RepeatVirtualScrollNode(int32_t nodeId, int32_t totalCount,
|
||||||
|
const std::map<std::string, 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)
|
||||||
|
: ForEachBaseNode(V2::JS_REPEAT_ETS_TAG, nodeId), totalCount_(totalCount),
|
||||||
|
caches_(templateCachedCountMap, onCreateNode, onUpdateNode, onGetKeys4Range, onGetTypes4Range),
|
||||||
|
postUpdateTaskHasBeenScheduled_(false)
|
||||||
|
{
|
||||||
|
// no preduct task scheduled
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepeatVirtualScrollNode::DoSetActiveChildRange(int32_t start, int32_t end, int32_t cacheStart, int32_t cacheEnd)
|
||||||
|
{
|
||||||
|
TAG_LOGD(AceLogTag::ACE_REPEAT,
|
||||||
|
"DoSetActiveChildRange: nodeId: %{public}d: start: %{public}d, end: %{public}d, cacheStart: %{public}d, "
|
||||||
|
"cacheEnd: %{public}d",
|
||||||
|
GetId(), start, end, cacheStart, cacheEnd);
|
||||||
|
|
||||||
|
// memorize active range
|
||||||
|
caches_.SetLastActiveRange(static_cast<uint32_t>(start), static_cast<uint32_t>(end));
|
||||||
|
|
||||||
|
bool needSync =
|
||||||
|
caches_.RebuildL1([start, end, cacheStart, cacheEnd, this](int32_t index, const RefPtr<UINode>& node) -> bool {
|
||||||
|
if (node == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Get the first child of FrameNode.
|
||||||
|
auto frameNode = AceType::DynamicCast<FrameNode>(node->GetFrameChildByIndex(0, true));
|
||||||
|
if (!frameNode) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// DoSetActiveChildRange uses int32_t , while other functions use uint32_t
|
||||||
|
// need to convert
|
||||||
|
if ((start <= index) && (index <= end)) {
|
||||||
|
frameNode->SetActive(true);
|
||||||
|
} else {
|
||||||
|
frameNode->SetActive(false);
|
||||||
|
}
|
||||||
|
if ((start - cacheStart <= index) && (index <= end + cacheEnd)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// active node moved into L2 cached.
|
||||||
|
// check transition flag.
|
||||||
|
if (node->OnRemoveFromParent(true)) {
|
||||||
|
// OnRemoveFromParent returns true means the child can be removed from tree immediately.
|
||||||
|
RemoveDisappearingChild(node);
|
||||||
|
} else {
|
||||||
|
// else move child into disappearing children, skip syncing render tree
|
||||||
|
AddDisappearingChild(node);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO see if loop leads to any changes to active states
|
||||||
|
// only in that case do the re-sync , re-assembly of children
|
||||||
|
if (needSync) {
|
||||||
|
UINode::MarkNeedSyncRenderTree(false);
|
||||||
|
children_.clear();
|
||||||
|
// re-assemble children_
|
||||||
|
PostIdleTask();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepeatVirtualScrollNode::InvalidateKeyCache()
|
||||||
|
{
|
||||||
|
// empty the cache index -> key
|
||||||
|
// C++ will need to ask all new keys from JS side
|
||||||
|
caches_.InvalidateKeyAndTTypeCaches();
|
||||||
|
children_.clear();
|
||||||
|
|
||||||
|
auto frameNode = GetParentFrameNode();
|
||||||
|
if (frameNode) {
|
||||||
|
frameNode->ChildrenUpdatedFrom(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
MarkNeedSyncRenderTree(true);
|
||||||
|
MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT | PROPERTY_UPDATE_BY_CHILD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a index -> key -> node does not exists, caller has verified before calling this function
|
||||||
|
*
|
||||||
|
* Ask TS to update a Node, if possible
|
||||||
|
* If no suitable node, request to crete a new node
|
||||||
|
*/
|
||||||
|
RefPtr<UINode> RepeatVirtualScrollNode::CreateOrUpdateFrameChild4Index(uint32_t forIndex, const std::string& forKey)
|
||||||
|
{
|
||||||
|
RefPtr<UINode> node4Index = caches_.UpdateFromL2(forIndex);
|
||||||
|
if (!node4Index) {
|
||||||
|
return node4Index;
|
||||||
|
}
|
||||||
|
|
||||||
|
return caches_.CreateNewNode(forIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME added
|
||||||
|
// index N-th item
|
||||||
|
// needBuild: true - if found in cache, then return, if not in cache then return newly build
|
||||||
|
// false: - if found in cache, then return, if not found in cache then return nullptr
|
||||||
|
// isCache: true indicates prebuild item (only used by List/Grid/Waterflow, this item should go to L2 cache,
|
||||||
|
// do not add to the tree,
|
||||||
|
// isCache==false this item is for display or near display area
|
||||||
|
// addToRenderTree: true - set it to active state, call SetActive
|
||||||
|
RefPtr<UINode> RepeatVirtualScrollNode::GetFrameChildByIndex(
|
||||||
|
uint32_t index, bool needBuild, bool isCache, bool addToRenderTree)
|
||||||
|
{
|
||||||
|
// It will get or create new key.
|
||||||
|
const auto& key = caches_.GetKey4Index(index);
|
||||||
|
if (!key) {
|
||||||
|
TAG_LOGE(AceLogTag::ACE_REPEAT, "fail to get key for %{public}d", index);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
// search if index -> key -> Node exist
|
||||||
|
// pair.first tells of key is in L1
|
||||||
|
auto node4Index = GetFromCaches(index);
|
||||||
|
if (!node4Index && !needBuild) {
|
||||||
|
TAG_LOGD(AceLogTag::ACE_REPEAT,
|
||||||
|
"index %{public}d not in caches && needBuild==false, GetFrameChildByIndex returns nullptr .", index);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// node4Index needs to be created or updated on JS side
|
||||||
|
if (!node4Index) {
|
||||||
|
// TS to either make new or update existing nodes
|
||||||
|
node4Index = CreateOrUpdateFrameChild4Index(index, key.value());
|
||||||
|
|
||||||
|
if (!node4Index) {
|
||||||
|
TAG_LOGW(AceLogTag::ACE_REPEAT, "index %{public}d not in caches and failed to build.", index);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
// move item to L1 cache.
|
||||||
|
caches_.AddKeyToL1(key.value());
|
||||||
|
} else {
|
||||||
|
// TODO need update existed node4Index with new index.
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the item was in L2 cache, remove it from there
|
||||||
|
|
||||||
|
if (isActive_) {
|
||||||
|
node4Index->SetJSViewActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addToRenderTree && !isCache) {
|
||||||
|
node4Index->SetActive(true);
|
||||||
|
}
|
||||||
|
if (node4Index->GetDepth() != GetDepth() + 1) {
|
||||||
|
node4Index->SetDepth(GetDepth() + 1);
|
||||||
|
}
|
||||||
|
// attach to repeat node and pass context to it.
|
||||||
|
node4Index->SetParent(WeakClaim(this));
|
||||||
|
if (IsOnMainTree()) {
|
||||||
|
node4Index->AttachToMainTree(false, GetContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
MarkNeedSyncRenderTree();
|
||||||
|
children_.clear();
|
||||||
|
// re-assemble children_
|
||||||
|
PostIdleTask();
|
||||||
|
|
||||||
|
auto childNode = node4Index->GetFrameChildByIndex(0, needBuild);
|
||||||
|
if (onMoveEvent_) {
|
||||||
|
InitDragManager(AceType::DynamicCast<FrameNode>(childNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
return childNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t RepeatVirtualScrollNode::GetFrameNodeIndex(const RefPtr<FrameNode>& node, bool /*isExpanded*/)
|
||||||
|
{
|
||||||
|
return caches_.GetFrameNodeIndex(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::list<RefPtr<UINode>>& RepeatVirtualScrollNode::GetChildren() const
|
||||||
|
{
|
||||||
|
if (!children_.empty()) {
|
||||||
|
return children_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// can not modify l1_cache while iterating
|
||||||
|
// GetChildren is overloaded, can not change it to non-const
|
||||||
|
// need to order the child.
|
||||||
|
std::map<int32_t, RefPtr<UINode>> children;
|
||||||
|
caches_.ForEachL1IndexUINode(
|
||||||
|
[&children](int32_t index, const RefPtr<UINode>& node) -> void { children.emplace(index, node); });
|
||||||
|
for (const auto& [index, child] : children) {
|
||||||
|
children_.emplace_back(child);
|
||||||
|
}
|
||||||
|
return children_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepeatVirtualScrollNode::RecycleItems(int32_t from, int32_t to)
|
||||||
|
{
|
||||||
|
offscreenItems_.from = from;
|
||||||
|
offscreenItems_.to = to;
|
||||||
|
for (auto i = from; i < to; i++) {
|
||||||
|
if (i >= startIndex_ && i < startIndex_ + totalCount_) {
|
||||||
|
caches_.RecycleItemsByIndex(i - startIndex_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepeatVirtualScrollNode::SetNodeIndexOffset(int32_t start, int32_t /*count*/)
|
||||||
|
{
|
||||||
|
startIndex_ = start;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t RepeatVirtualScrollNode::FrameCount() const
|
||||||
|
{
|
||||||
|
return totalCount_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepeatVirtualScrollNode::PostIdleTask()
|
||||||
|
{
|
||||||
|
if (postUpdateTaskHasBeenScheduled_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
postUpdateTaskHasBeenScheduled_ = true;
|
||||||
|
auto* context = GetContext();
|
||||||
|
CHECK_NULL_VOID(context);
|
||||||
|
|
||||||
|
context->AddPredictTask([weak = AceType::WeakClaim(this)](int64_t /*deadline*/, bool /*canUseLongPredictTask*/) {
|
||||||
|
ACE_SCOPED_TRACE("RepeatVirtualScrollNode predict");
|
||||||
|
auto node = weak.Upgrade();
|
||||||
|
CHECK_NULL_VOID(node);
|
||||||
|
node->postUpdateTaskHasBeenScheduled_ = false;
|
||||||
|
node->GetChildren();
|
||||||
|
node->caches_.Purge();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepeatVirtualScrollNode::OnConfigurationUpdate(const ConfigurationChange& configurationChange)
|
||||||
|
{
|
||||||
|
if ((configurationChange.colorModeUpdate || configurationChange.fontUpdate)) {
|
||||||
|
const auto& children = caches_.GetAllNodes();
|
||||||
|
for (const auto& [key, child] : children) {
|
||||||
|
if (child) {
|
||||||
|
child->UpdateConfigurationUpdate(configurationChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME Which of the following methods are actually needed ?
|
||||||
|
|
||||||
|
void RepeatVirtualScrollNode::SetOnMove(std::function<void(int32_t, int32_t)>&& onMove)
|
||||||
|
{
|
||||||
|
// To do
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOREAch
|
||||||
|
void RepeatVirtualScrollNode::MoveData(int32_t from, int32_t to)
|
||||||
|
{
|
||||||
|
// To do
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<FrameNode> RepeatVirtualScrollNode::GetFrameNode(int32_t index)
|
||||||
|
{
|
||||||
|
return AceType::DynamicCast<FrameNode>(GetFrameChildByIndex(index, false, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepeatVirtualScrollNode::InitDragManager(const RefPtr<UINode>& child)
|
||||||
|
{
|
||||||
|
// To do
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepeatVirtualScrollNode::InitAllChildrenDragManager(bool init)
|
||||||
|
{
|
||||||
|
// To do
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace OHOS::Ace::NG
|
@ -0,0 +1,183 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Huawei Device Co., Ltd.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_REPEAT_VIRTUAL_SCROLL_NODE_H
|
||||||
|
#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_REPEAT_VIRTUAL_SCROLL_NODE_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <list>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "base/memory/referenced.h"
|
||||||
|
#include "base/utils/macros.h"
|
||||||
|
#include "core/components_ng/base/ui_node.h"
|
||||||
|
#include "core/components_ng/syntax/for_each_base_node.h"
|
||||||
|
#include "core/components_ng/syntax/repeat_virtual_scroll_caches.h"
|
||||||
|
|
||||||
|
namespace OHOS::Ace::NG {
|
||||||
|
struct OffscreenItems {
|
||||||
|
int32_t from = -1;
|
||||||
|
int32_t to = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ACE_EXPORT RepeatVirtualScrollNode : public ForEachBaseNode {
|
||||||
|
DECLARE_ACE_TYPE(RepeatVirtualScrollNode, ForEachBaseNode);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static RefPtr<RepeatVirtualScrollNode> GetOrCreateRepeatNode(int32_t nodeId,
|
||||||
|
uint32_t totalCount,
|
||||||
|
const std::map<std::string, 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);
|
||||||
|
|
||||||
|
RepeatVirtualScrollNode(int32_t nodeId, int32_t totalCount,
|
||||||
|
const std::map<std::string, uint32_t>& templateCacheCountMap, 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);
|
||||||
|
|
||||||
|
~RepeatVirtualScrollNode() override = default;
|
||||||
|
|
||||||
|
void UpdateTotalCount(uint32_t totalCount)
|
||||||
|
{
|
||||||
|
totalCount_ = totalCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number of children that Repeat can product
|
||||||
|
// returns TotalCount
|
||||||
|
int32_t FrameCount() const override;
|
||||||
|
|
||||||
|
// called from TS upon Repeat rerender
|
||||||
|
void InvalidateKeyCache();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetChildren re-assembles children_ and cleanup the L1 cache
|
||||||
|
* active items remain in L1 cache and are added to RepeatVirtualScroll.children_
|
||||||
|
* inactive items are moved from L1 to L2 cache, not added to children_
|
||||||
|
* function returns children_
|
||||||
|
* function runs as part of idle task
|
||||||
|
*/
|
||||||
|
const std::list<RefPtr<UINode>>& GetChildren() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update range of Active items inside Repeat:
|
||||||
|
* iterative L1 cache entries
|
||||||
|
* those items with index in range [ start ... end ] are marked active
|
||||||
|
* those out of range marked inactive.
|
||||||
|
* retests idle task
|
||||||
|
*/
|
||||||
|
void DoSetActiveChildRange(int32_t start, int32_t end, int32_t cacheStart, int32_t cacheEnd) override;
|
||||||
|
|
||||||
|
// largely unknown when it is expected to be called
|
||||||
|
// meant to inform which items with index [ from .. to ] can be recycled / updated
|
||||||
|
void RecycleItems(int32_t from, int32_t to) override;
|
||||||
|
|
||||||
|
// Called by parent generate frame child.
|
||||||
|
void SetNodeIndexOffset(int32_t start, int32_t count) override;
|
||||||
|
|
||||||
|
/** Called by Layout to request ListItem and child subtree
|
||||||
|
for given index
|
||||||
|
either returns existing item for index from L1 or L2 cache, or gets a new item created
|
||||||
|
update of L2 cache item to new index)
|
||||||
|
result is in L1 cache if isCache is false, and L2 cache if isCache is true.
|
||||||
|
|
||||||
|
meaning of parameters
|
||||||
|
needBuild: true - if found in cache, then return, if not in cache then return newly build
|
||||||
|
false: - if found in cache, then return, if not found in cache then return nullptr
|
||||||
|
isCache: true indicates prebuild item (only used by List/Grid/Waterflow, this item should go to L2 cache,
|
||||||
|
do not add to the tree,
|
||||||
|
isCaxche==false this item is for display or near display area
|
||||||
|
addToRenderTree: true - set it to active state, call SetActive
|
||||||
|
*/
|
||||||
|
RefPtr<UINode> GetFrameChildByIndex(
|
||||||
|
uint32_t index, bool needBuild, bool isCache = false, bool addToRenderTree = false) override;
|
||||||
|
|
||||||
|
bool IsAtomicNode() const override
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// used for drag move operation.
|
||||||
|
void SetOnMove(std::function<void(int32_t, int32_t)>&& onMove);
|
||||||
|
void MoveData(int32_t from, int32_t to) override;
|
||||||
|
RefPtr<FrameNode> GetFrameNode(int32_t index) override;
|
||||||
|
int32_t GetFrameNodeIndex(const RefPtr<FrameNode>& node, bool isExpanded = true) override;
|
||||||
|
void InitDragManager(const RefPtr<UINode>& childNode);
|
||||||
|
void InitAllChildrenDragManager(bool init);
|
||||||
|
|
||||||
|
void OnConfigurationUpdate(const ConfigurationChange& configurationChange) override;
|
||||||
|
|
||||||
|
void SetJSViewActive(bool active = true, bool isLazyForEachNode = false) override
|
||||||
|
{
|
||||||
|
const auto& children = caches_.GetAllNodes();
|
||||||
|
for (const auto& [key, child] : children) {
|
||||||
|
child->SetJSViewActive(active);
|
||||||
|
}
|
||||||
|
isActive_ = active;
|
||||||
|
}
|
||||||
|
void PaintDebugBoundaryTreeAll(bool flag) override
|
||||||
|
{
|
||||||
|
const auto& children = caches_.GetAllNodes();
|
||||||
|
for (const auto& [key, child] : children) {
|
||||||
|
child->PaintDebugBoundaryTreeAll(flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void PostIdleTask();
|
||||||
|
|
||||||
|
// try to find entry for given index in L1 or L2 cache
|
||||||
|
RefPtr<UINode> GetFromCaches(uint32_t forIndex)
|
||||||
|
{
|
||||||
|
return caches_.GetNode4Index(forIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// index is not in L1 or L2 cache, need to make it
|
||||||
|
// either by TS rendering new children or by TS updating
|
||||||
|
// a L2 cache item from old to new index
|
||||||
|
RefPtr<UINode> CreateOrUpdateFrameChild4Index(uint32_t index, const std::string& forKey);
|
||||||
|
|
||||||
|
// get farthest (from L1 indexes) index in L2 cache or -1
|
||||||
|
int32_t GetFarthestL2CacheIndex();
|
||||||
|
|
||||||
|
// RepeatVirtualScrollNode is not instance of FrameNode
|
||||||
|
// needs to propagate active state to all items inside
|
||||||
|
bool isActive_ = true;
|
||||||
|
|
||||||
|
// size of data source when all data items loaded
|
||||||
|
uint32_t totalCount_ = 0;
|
||||||
|
|
||||||
|
// caches:
|
||||||
|
mutable RepeatVirtualScrollCaches caches_;
|
||||||
|
|
||||||
|
// used by one of the unknown functions
|
||||||
|
std::list<std::string> ids_;
|
||||||
|
|
||||||
|
// re-assembled by GetChildren called from idle task
|
||||||
|
mutable std::list<RefPtr<UINode>> children_;
|
||||||
|
|
||||||
|
// true in the time from requesting idle / predict task until exec predict tsk.
|
||||||
|
bool postUpdateTaskHasBeenScheduled_;
|
||||||
|
|
||||||
|
OffscreenItems offscreenItems_;
|
||||||
|
int32_t startIndex_ = 0;
|
||||||
|
|
||||||
|
ACE_DISALLOW_COPY_AND_MOVE(RepeatVirtualScrollNode);
|
||||||
|
};
|
||||||
|
} // namespace OHOS::Ace::NG
|
||||||
|
|
||||||
|
#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_REPEAT_VIRTUAL_SCROLL_NODE_H
|
@ -70,7 +70,9 @@ const char JS_LAZY_FOR_EACH_ETS_TAG[] = "LazyForEach";
|
|||||||
const char JS_FOR_EACH_ETS_TAG[] = "ForEach";
|
const char JS_FOR_EACH_ETS_TAG[] = "ForEach";
|
||||||
// js lazy foreach node
|
// js lazy foreach node
|
||||||
const char JS_SYNTAX_ITEM_ETS_TAG[] = "SyntaxItem";
|
const char JS_SYNTAX_ITEM_ETS_TAG[] = "SyntaxItem";
|
||||||
// js if lese node
|
// js repeat node
|
||||||
|
const char JS_REPEAT_ETS_TAG[] = "Repeat";
|
||||||
|
// js if else node
|
||||||
const char JS_IF_ELSE_ETS_TAG[] = "IfElse";
|
const char JS_IF_ELSE_ETS_TAG[] = "IfElse";
|
||||||
// js node slot
|
// js node slot
|
||||||
const char JS_NODE_SLOT_ETS_TAG[] = "NodeSlot";
|
const char JS_NODE_SLOT_ETS_TAG[] = "NodeSlot";
|
||||||
|
@ -75,7 +75,9 @@ ACE_EXPORT extern const char JS_LAZY_FOR_EACH_ETS_TAG[];
|
|||||||
ACE_EXPORT extern const char JS_FOR_EACH_ETS_TAG[];
|
ACE_EXPORT extern const char JS_FOR_EACH_ETS_TAG[];
|
||||||
// js lazy foreach node
|
// js lazy foreach node
|
||||||
ACE_EXPORT extern const char JS_SYNTAX_ITEM_ETS_TAG[];
|
ACE_EXPORT extern const char JS_SYNTAX_ITEM_ETS_TAG[];
|
||||||
// js if lese node
|
// js repeat node
|
||||||
|
ACE_EXPORT extern const char JS_REPEAT_ETS_TAG[];
|
||||||
|
// js if else node
|
||||||
ACE_EXPORT extern const char JS_IF_ELSE_ETS_TAG[];
|
ACE_EXPORT extern const char JS_IF_ELSE_ETS_TAG[];
|
||||||
// js node slot node
|
// js node slot node
|
||||||
ACE_EXPORT extern const char JS_NODE_SLOT_ETS_TAG[];
|
ACE_EXPORT extern const char JS_NODE_SLOT_ETS_TAG[];
|
||||||
|
@ -1346,7 +1346,7 @@ HWTEST_F(LazyForEachSyntaxTestNg, ForEachSyntaxDoSetActiveChildRangeTest001, Tes
|
|||||||
* @tc.steps: step3. Invoke DoSetActiveChildRange.
|
* @tc.steps: step3. Invoke DoSetActiveChildRange.
|
||||||
* @tc.expected: LazyForEachNode ids_ will be cleared.
|
* @tc.expected: LazyForEachNode ids_ will be cleared.
|
||||||
*/
|
*/
|
||||||
lazyForEachNode->DoSetActiveChildRange(0, 0);
|
lazyForEachNode->DoSetActiveChildRange(0, 0, 0, 0);
|
||||||
EXPECT_TRUE(lazyForEachNode->ids_.empty());
|
EXPECT_TRUE(lazyForEachNode->ids_.empty());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1354,7 +1354,7 @@ HWTEST_F(LazyForEachSyntaxTestNg, ForEachSyntaxDoSetActiveChildRangeTest001, Tes
|
|||||||
* @tc.expected: LazyForEachNode ids_ will be cleared.
|
* @tc.expected: LazyForEachNode ids_ will be cleared.
|
||||||
*/
|
*/
|
||||||
lazyForEachNode->builder_ = nullptr;
|
lazyForEachNode->builder_ = nullptr;
|
||||||
lazyForEachNode->DoSetActiveChildRange(0, 0);
|
lazyForEachNode->DoSetActiveChildRange(0, 0, 0, 0);
|
||||||
EXPECT_TRUE(lazyForEachNode->ids_.empty());
|
EXPECT_TRUE(lazyForEachNode->ids_.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user