mirror of
https://gitee.com/openharmony/arkui_ace_engine
synced 2024-11-30 10:43:03 +00:00
!39387 RepeatVirtualScroll reRender optimization
Merge pull request !39387 from denis.pikalov/arkui-34-rerender-optim
This commit is contained in:
commit
cb6244f9e3
@ -7453,27 +7453,29 @@ class ObserveV2 {
|
||||
// clear any previously created dependency view model object to elmtId
|
||||
// find these view model objects with the reverse map id2targets_
|
||||
clearBinding(id) {
|
||||
const targetSet = this.id2targets_[id];
|
||||
let target;
|
||||
if (targetSet && targetSet instanceof Set) {
|
||||
targetSet.forEach((weakTarget) => {
|
||||
var _a, _b;
|
||||
if ((target = weakTarget.deref()) && target instanceof Object) {
|
||||
const idRefs = target[ObserveV2.ID_REFS];
|
||||
const symRefs = target[ObserveV2.SYMBOL_REFS];
|
||||
if (idRefs) {
|
||||
(_a = idRefs[id]) === null || _a === void 0 ? void 0 : _a.forEach(key => { var _a; return (_a = symRefs === null || symRefs === void 0 ? void 0 : symRefs[key]) === null || _a === void 0 ? void 0 : _a.delete(id); });
|
||||
delete idRefs[id];
|
||||
}
|
||||
else {
|
||||
for (let key in symRefs) {
|
||||
(_b = symRefs[key]) === null || _b === void 0 ? void 0 : _b.delete(id);
|
||||
}
|
||||
;
|
||||
}
|
||||
var _a;
|
||||
// multiple weakRefs might point to the same target - here we get Set of unique targets
|
||||
const targetSet = new Set();
|
||||
(_a = this.id2targets_[id]) === null || _a === void 0 ? void 0 : _a.forEach((weak) => {
|
||||
if (weak.deref() instanceof Object) {
|
||||
targetSet.add(weak.deref());
|
||||
}
|
||||
});
|
||||
targetSet.forEach((target) => {
|
||||
var _a, _b;
|
||||
const idRefs = target[ObserveV2.ID_REFS];
|
||||
const symRefs = target[ObserveV2.SYMBOL_REFS];
|
||||
if (idRefs) {
|
||||
(_a = idRefs[id]) === null || _a === void 0 ? void 0 : _a.forEach(key => { var _a; return (_a = symRefs === null || symRefs === void 0 ? void 0 : symRefs[key]) === null || _a === void 0 ? void 0 : _a.delete(id); });
|
||||
delete idRefs[id];
|
||||
}
|
||||
else {
|
||||
for (let key in symRefs) {
|
||||
(_b = symRefs[key]) === null || _b === void 0 ? void 0 : _b.delete(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
;
|
||||
}
|
||||
});
|
||||
delete this.id2targets_[id];
|
||||
delete this.id2cmp_[id];
|
||||
|
||||
@ -10126,8 +10128,18 @@ class __Repeat {
|
||||
return this;
|
||||
}
|
||||
// function to decide which template to use, each template has an id
|
||||
templateId(typeFunc) {
|
||||
this.config.typeGenFunc = typeFunc;
|
||||
templateId(typeGenFunc) {
|
||||
// typeGenFunc wrapper with ttype validation
|
||||
const typeGenFuncSafe = (item, index) => {
|
||||
const itemType = typeGenFunc(item, index);
|
||||
const itemFunc = this.config.itemGenFuncs[itemType];
|
||||
if (typeof itemFunc != 'function') {
|
||||
stateMgmtConsole.applicationError(`Repeat with virtual scroll. Missing Repeat.template for id '${itemType}'`);
|
||||
return '';
|
||||
}
|
||||
return itemType;
|
||||
};
|
||||
this.config.typeGenFunc = typeGenFuncSafe;
|
||||
return this;
|
||||
}
|
||||
// template: id + builder function to render specific type of data item
|
||||
@ -10358,6 +10370,8 @@ class __RepeatVirtualScrollImpl {
|
||||
this.repeatItem4Key_ = new Map();
|
||||
// RepeatVirtualScrollNode elmtId
|
||||
this.repeatElmtId_ = -1;
|
||||
// Last known active range (as sparse array)
|
||||
this.lastActiveRangeData_ = [];
|
||||
}
|
||||
render(config, isInitialRender) {
|
||||
this.arr_ = config.arr;
|
||||
@ -10473,7 +10487,6 @@ class __RepeatVirtualScrollImpl {
|
||||
return result;
|
||||
}; // const onGetKeys4Range
|
||||
const onGetTypes4Range = (from, to) => {
|
||||
var _a;
|
||||
if (to > this.totalCount_ || to > this.arr_.length) {
|
||||
stateMgmtConsole.applicationError(`Repeat with virtualScroll elmtId: ${this.repeatElmtId_}: onGetTypes4Range from ${from} to ${to} \
|
||||
with data array length ${this.arr_.length}, totalCount=${this.totalCount_} \
|
||||
@ -10490,13 +10503,7 @@ class __RepeatVirtualScrollImpl {
|
||||
ViewStackProcessor.StartGetAccessRecordingFor(this.repeatElmtId_);
|
||||
ObserveV2.getObserve().startRecordDependencies(owningView, this.repeatElmtId_, false);
|
||||
for (let i = from; i <= to && i < this.arr_.length; i++) {
|
||||
let ttype = (_a = this.typeGenFunc_(this.arr_[i], i)) !== null && _a !== void 0 ? _a : '';
|
||||
if (!this.itemGenFuncs_[ttype]) {
|
||||
stateMgmtConsole.applicationError(`Repeat with virtual scroll elmtId: ${this.repeatElmtId_}. Factory function .templateId returns template id '${ttype}'.` +
|
||||
(ttype === '') ? 'Missing Repeat.each ' : `missing Repeat.template for id '${ttype}'` + '! Unrecoverable application error!');
|
||||
// fallback to use .each function and try to continue the app with it.
|
||||
ttype = '';
|
||||
}
|
||||
let ttype = this.typeGenFunc_(this.arr_[i], i);
|
||||
result.push(ttype);
|
||||
} // for
|
||||
ObserveV2.getObserve().stopRecordDependencies();
|
||||
@ -10504,36 +10511,68 @@ class __RepeatVirtualScrollImpl {
|
||||
|
||||
return result;
|
||||
}; // const onGetTypes4Range
|
||||
const onSetActiveRange = (from, to) => {
|
||||
|
||||
// make sparse copy of this.arr_
|
||||
this.lastActiveRangeData_ = new Array(this.arr_.length);
|
||||
for (let i = from; i <= to && i < this.arr_.length; i++) {
|
||||
const item = this.arr_[i];
|
||||
const ttype = this.typeGenFunc_(this.arr_[i], i);
|
||||
this.lastActiveRangeData_[i] = { item, ttype };
|
||||
}
|
||||
};
|
||||
|
||||
RepeatVirtualScrollNative.create(this.totalCount_, Object.entries(this.templateOptions_), {
|
||||
onCreateNode,
|
||||
onUpdateNode,
|
||||
onGetKeys4Range,
|
||||
onGetTypes4Range
|
||||
onGetTypes4Range,
|
||||
onSetActiveRange
|
||||
});
|
||||
RepeatVirtualScrollNative.onMove(this.onMoveHandler_);
|
||||
|
||||
}
|
||||
reRender() {
|
||||
|
||||
this.purgeKeyCache();
|
||||
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_[''];
|
||||
if (typeof itemFunc === 'function') {
|
||||
itemFunc(repeatItem);
|
||||
if (this.hasVisibleItemsChanged()) {
|
||||
this.purgeKeyCache();
|
||||
RepeatVirtualScrollNative.updateRenderState(this.totalCount_, true);
|
||||
|
||||
}
|
||||
else {
|
||||
stateMgmtConsole.applicationError(`Repeat with virtualScroll elmtId ${this.repeatElmtId_}: ` +
|
||||
(itemType === '') ? 'Missing Repeat.each ' : `missing Repeat.template for id '${itemType}'` +
|
||||
'! Unrecoverable application error!');
|
||||
// avoid re-render when data pushed outside visible area
|
||||
RepeatVirtualScrollNative.updateRenderState(this.totalCount_, false);
|
||||
|
||||
}
|
||||
}
|
||||
initialRenderItem(repeatItem) {
|
||||
// execute the itemGen function
|
||||
const itemType = this.typeGenFunc_(repeatItem.item, repeatItem.index);
|
||||
const itemFunc = this.itemGenFuncs_[itemType];
|
||||
itemFunc(repeatItem);
|
||||
}
|
||||
hasVisibleItemsChanged() {
|
||||
var _a, _b;
|
||||
let lastActiveRangeIndex = 0;
|
||||
// has any item or ttype in the active range changed?
|
||||
for (let i in this.lastActiveRangeData_) {
|
||||
const oldItem = (_a = this.lastActiveRangeData_[+i]) === null || _a === void 0 ? void 0 : _a.item;
|
||||
const oldType = (_b = this.lastActiveRangeData_[+i]) === null || _b === void 0 ? void 0 : _b.ttype;
|
||||
const newItem = this.arr_[+i];
|
||||
const newType = this.typeGenFunc_(this.arr_[+i], +i);
|
||||
if (oldItem !== newItem) {
|
||||
|
||||
return true;
|
||||
}
|
||||
if (oldType !== newType) {
|
||||
|
||||
return true;
|
||||
}
|
||||
lastActiveRangeIndex = +i;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* maintain: index <-> key mapping
|
||||
* create new key from keyGen function if not in cache
|
||||
|
@ -82,7 +82,8 @@ bool ParseAndVerifyParams(const JSCallbackInfo& info)
|
||||
auto handlers = JSRef<JSObject>::Cast(info[PARAM_HANDLERS]);
|
||||
if (!handlers->GetProperty("onCreateNode")->IsFunction() || !handlers->GetProperty("onUpdateNode")->IsFunction() ||
|
||||
!handlers->GetProperty("onGetKeys4Range")->IsFunction() ||
|
||||
!handlers->GetProperty("onGetTypes4Range")->IsFunction()) {
|
||||
!handlers->GetProperty("onGetTypes4Range")->IsFunction() ||
|
||||
!handlers->GetProperty("onSetActiveRange")->IsFunction()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -166,19 +167,28 @@ void JSRepeatVirtualScroll::Create(const JSCallbackInfo& info)
|
||||
return list;
|
||||
};
|
||||
|
||||
auto onSetActiveRange = [execCtx = info.GetExecutionContext(), func = JSFUNC(handlers, "onSetActiveRange")](
|
||||
uint32_t from, uint32_t to) -> void {
|
||||
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
|
||||
auto params = ConvertToJSValues(from, to);
|
||||
func->Call(JSRef<JSObject>(), params.size(), params.data());
|
||||
};
|
||||
|
||||
RepeatVirtualScrollModel::GetInstance()->Create(
|
||||
totalCount, templateCachedCountMap, onCreateNode, onUpdateNode, onGetKeys4Range, onGetTypes4Range);
|
||||
totalCount, templateCachedCountMap, onCreateNode, onUpdateNode, onGetKeys4Range, onGetTypes4Range,
|
||||
onSetActiveRange);
|
||||
}
|
||||
|
||||
void JSRepeatVirtualScroll::InvalidateKeyCache(const JSCallbackInfo& info)
|
||||
void JSRepeatVirtualScroll::UpdateRenderState(const JSCallbackInfo& info)
|
||||
{
|
||||
ACE_SCOPED_TRACE("RepeatVirtualScroll:InvalidateKeyCache");
|
||||
TAG_LOGD(AceLogTag::ACE_REPEAT, "JSRepeatVirtualScroll::InvalidateKeyCache");
|
||||
if (!info[0]->IsNumber()) {
|
||||
ACE_SCOPED_TRACE("RepeatVirtualScroll:UpdateRenderState");
|
||||
TAG_LOGD(AceLogTag::ACE_REPEAT, "JSRepeatVirtualScroll::UpdateRenderState");
|
||||
if (!info[0]->IsNumber() || !info[1]->IsBoolean()) {
|
||||
return;
|
||||
}
|
||||
auto totalCount = info[0]->ToNumber<uint32_t>();
|
||||
RepeatVirtualScrollModel::GetInstance()->InvalidateKeyCache(totalCount);
|
||||
auto visibleItemsChanged = info[1]->ToBoolean();
|
||||
RepeatVirtualScrollModel::GetInstance()->UpdateRenderState(totalCount, visibleItemsChanged);
|
||||
}
|
||||
|
||||
void JSRepeatVirtualScroll::OnMove(const JSCallbackInfo& info)
|
||||
@ -199,7 +209,7 @@ void JSRepeatVirtualScroll::JSBind(BindingTarget globalObj)
|
||||
{
|
||||
JSClass<JSRepeatVirtualScroll>::Declare("RepeatVirtualScrollNative");
|
||||
JSClass<JSRepeatVirtualScroll>::StaticMethod("create", &JSRepeatVirtualScroll::Create);
|
||||
JSClass<JSRepeatVirtualScroll>::StaticMethod("invalidateKeyCache", &JSRepeatVirtualScroll::InvalidateKeyCache);
|
||||
JSClass<JSRepeatVirtualScroll>::StaticMethod("updateRenderState", &JSRepeatVirtualScroll::UpdateRenderState);
|
||||
JSClass<JSRepeatVirtualScroll>::StaticMethod("onMove", &JSRepeatVirtualScroll::OnMove);
|
||||
JSClass<JSRepeatVirtualScroll>::Bind<>(globalObj);
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ public:
|
||||
static void JSBind(BindingTarget globalObj);
|
||||
|
||||
static void Create(const JSCallbackInfo& info);
|
||||
static void InvalidateKeyCache(const JSCallbackInfo& info);
|
||||
static void UpdateRenderState(const JSCallbackInfo& info);
|
||||
static void OnMove(const JSCallbackInfo& info);
|
||||
};
|
||||
|
||||
|
@ -53,10 +53,11 @@ declare class RepeatVirtualScrollNative {
|
||||
onUpdateNode: (fromKey: string, forIndex: number) => void;
|
||||
onGetKeys4Range: (from: number, toNumber: number) => Array<string>;
|
||||
onGetTypes4Range: (from: number, toNumber: number) => Array<string>;
|
||||
onSetActiveRange: (from: number, to: number) => void;
|
||||
}
|
||||
): void;
|
||||
// invalidate C++ side map index -> key
|
||||
static invalidateKeyCache(totalCount : number): void;
|
||||
// invalidate caches in C++ side, trigger render if needed
|
||||
static updateRenderState(totalCount: number, visibleItemsChanged: boolean): void;
|
||||
// drag and drop
|
||||
static onMove(handler: (from: number, to: number) => void);
|
||||
}
|
||||
|
@ -200,8 +200,19 @@ class __Repeat<T> implements RepeatAPI<T> {
|
||||
}
|
||||
|
||||
// function to decide which template to use, each template has an id
|
||||
public templateId(typeFunc: RepeatTypeGenFunc<T>): RepeatAPI<T> {
|
||||
this.config.typeGenFunc = typeFunc;
|
||||
public templateId(typeGenFunc: RepeatTypeGenFunc<T>): RepeatAPI<T> {
|
||||
// typeGenFunc wrapper with ttype validation
|
||||
const typeGenFuncSafe = (item: T, index: number): string => {
|
||||
const itemType = typeGenFunc(item, index);
|
||||
const itemFunc = this.config.itemGenFuncs[itemType];
|
||||
if (typeof itemFunc != 'function') {
|
||||
stateMgmtConsole.applicationError(`Repeat with virtual scroll. Missing Repeat.template for id '${itemType}'`);
|
||||
return '';
|
||||
}
|
||||
return itemType;
|
||||
};
|
||||
|
||||
this.config.typeGenFunc = typeGenFuncSafe;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,9 @@ class __RepeatVirtualScrollImpl<T> {
|
||||
// RepeatVirtualScrollNode elmtId
|
||||
private repeatElmtId_ : number = -1;
|
||||
|
||||
// Last known active range (as sparse array)
|
||||
private lastActiveRangeData_: Array<{ item: T, ttype: string }> = [];
|
||||
|
||||
public render(config: __RepeatConfig<T>, isInitialRender: boolean): void {
|
||||
this.arr_ = config.arr;
|
||||
this.itemGenFuncs_ = config.itemGenFuncs;
|
||||
@ -130,7 +133,6 @@ class __RepeatVirtualScrollImpl<T> {
|
||||
}; // onUpdateNode
|
||||
|
||||
const onGetKeys4Range = (from: number, to: number): Array<string> => {
|
||||
|
||||
if (to > this.totalCount_ || to > this.arr_.length) {
|
||||
stateMgmtConsole.applicationError(`Repeat with virtualScroll elmtId ${this.repeatElmtId_}: onGetKeys4Range from ${from} to ${to} \
|
||||
with data array length ${this.arr_.length}, totalCount=${this.totalCount_} \
|
||||
@ -197,13 +199,7 @@ class __RepeatVirtualScrollImpl<T> {
|
||||
ObserveV2.getObserve().startRecordDependencies(owningView, this.repeatElmtId_, false);
|
||||
|
||||
for (let i = from; i <= to && i < this.arr_.length; i++) {
|
||||
let ttype = this.typeGenFunc_(this.arr_[i], i) ?? '';
|
||||
if (!this.itemGenFuncs_[ttype]) {
|
||||
stateMgmtConsole.applicationError(`Repeat with virtual scroll elmtId: ${this.repeatElmtId_}. Factory function .templateId returns template id '${ttype}'.` +
|
||||
(ttype === '') ? 'Missing Repeat.each ' : `missing Repeat.template for id '${ttype}'` + '! Unrecoverable application error!');
|
||||
// fallback to use .each function and try to continue the app with it.
|
||||
ttype = '';
|
||||
}
|
||||
let ttype = this.typeGenFunc_(this.arr_[i], i);
|
||||
result.push(ttype);
|
||||
} // for
|
||||
ObserveV2.getObserve().stopRecordDependencies();
|
||||
@ -213,13 +209,25 @@ class __RepeatVirtualScrollImpl<T> {
|
||||
return result;
|
||||
}; // const onGetTypes4Range
|
||||
|
||||
const onSetActiveRange = (from: number, to: number): void => {
|
||||
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl: onSetActiveRange(${from}, ${to})`);
|
||||
// make sparse copy of this.arr_
|
||||
this.lastActiveRangeData_ = new Array<{item: T, ttype: string}>(this.arr_.length);
|
||||
for (let i = from; i <= to && i < this.arr_.length; i++) {
|
||||
const item = this.arr_[i];
|
||||
const ttype = this.typeGenFunc_(this.arr_[i], i);
|
||||
this.lastActiveRangeData_[i] = { item, ttype };
|
||||
}
|
||||
}
|
||||
|
||||
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl(${this.repeatElmtId_}): initialRenderVirtualScroll`);
|
||||
|
||||
RepeatVirtualScrollNative.create(this.totalCount_, Object.entries(this.templateOptions_), {
|
||||
onCreateNode,
|
||||
onUpdateNode,
|
||||
onGetKeys4Range,
|
||||
onGetTypes4Range
|
||||
onGetTypes4Range,
|
||||
onSetActiveRange
|
||||
});
|
||||
RepeatVirtualScrollNative.onMove(this.onMoveHandler_);
|
||||
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl(${this.repeatElmtId_}): initialRenderVirtualScroll`);
|
||||
@ -227,22 +235,47 @@ class __RepeatVirtualScrollImpl<T> {
|
||||
|
||||
private reRender(): void {
|
||||
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl(${this.repeatElmtId_}): reRender ...`);
|
||||
this.purgeKeyCache();
|
||||
RepeatVirtualScrollNative.invalidateKeyCache(this.totalCount_);
|
||||
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl(${this.repeatElmtId_}): reRender - done`);
|
||||
if (this.hasVisibleItemsChanged()) {
|
||||
this.purgeKeyCache();
|
||||
RepeatVirtualScrollNative.updateRenderState(this.totalCount_, true);
|
||||
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl: reRender - done.`);
|
||||
} else {
|
||||
// avoid re-render when data pushed outside visible area
|
||||
RepeatVirtualScrollNative.updateRenderState(this.totalCount_, false);
|
||||
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl: reRender (no changes in visible items) - 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_[''];
|
||||
if (typeof itemFunc === 'function') {
|
||||
itemFunc(repeatItem);
|
||||
} else {
|
||||
stateMgmtConsole.applicationError(`Repeat with virtualScroll elmtId ${this.repeatElmtId_}: ` +
|
||||
(itemType === '') ? 'Missing Repeat.each ' : `missing Repeat.template for id '${itemType}'` +
|
||||
'! Unrecoverable application error!');
|
||||
const itemType = this.typeGenFunc_(repeatItem.item, repeatItem.index);
|
||||
const itemFunc = this.itemGenFuncs_[itemType];
|
||||
itemFunc(repeatItem);
|
||||
}
|
||||
|
||||
private hasVisibleItemsChanged(): boolean {
|
||||
let lastActiveRangeIndex = 0;
|
||||
|
||||
// has any item or ttype in the active range changed?
|
||||
for (let i in this.lastActiveRangeData_) {
|
||||
const oldItem = this.lastActiveRangeData_[+i]?.item;
|
||||
const oldType = this.lastActiveRangeData_[+i]?.ttype;
|
||||
const newItem = this.arr_[+i];
|
||||
const newType = this.typeGenFunc_(this.arr_[+i], +i);
|
||||
|
||||
if (oldItem !== newItem) {
|
||||
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl.hasVisibleItemsChanged() i:#${i} item changed => true`);
|
||||
return true;
|
||||
}
|
||||
if (oldType !== newType) {
|
||||
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl.hasVisibleItemsChanged() i:#${i} ttype changed => true`);
|
||||
return true;
|
||||
}
|
||||
lastActiveRangeIndex = +i;
|
||||
}
|
||||
|
||||
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl.hasVisibleItemsChanged() => false`);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -167,23 +167,27 @@ class ObserveV2 {
|
||||
// clear any previously created dependency view model object to elmtId
|
||||
// find these view model objects with the reverse map id2targets_
|
||||
public clearBinding(id: number): void {
|
||||
const targetSet: Set<WeakRef<Object>> = this.id2targets_[id];
|
||||
let target: Object | undefined;
|
||||
if (targetSet && targetSet instanceof Set) {
|
||||
targetSet.forEach((weakTarget) => {
|
||||
if ((target = weakTarget.deref()) && target instanceof Object) {
|
||||
const idRefs: Object | undefined = target[ObserveV2.ID_REFS];
|
||||
const symRefs: Object = target[ObserveV2.SYMBOL_REFS];
|
||||
// multiple weakRefs might point to the same target - here we get Set of unique targets
|
||||
const targetSet = new Set<Object>();
|
||||
this.id2targets_[id]?.forEach((weak : WeakRef<Object>) => {
|
||||
if (weak.deref() instanceof Object) {
|
||||
targetSet.add(weak.deref())
|
||||
}
|
||||
});
|
||||
|
||||
if (idRefs) {
|
||||
idRefs[id]?.forEach(key => symRefs?.[key]?.delete(id));
|
||||
delete idRefs[id];
|
||||
} else {
|
||||
for (let key in symRefs) { symRefs[key]?.delete(id) };
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
targetSet.forEach((target) => {
|
||||
const idRefs: Object | undefined = target[ObserveV2.ID_REFS];
|
||||
const symRefs: Object = target[ObserveV2.SYMBOL_REFS];
|
||||
|
||||
if (idRefs) {
|
||||
idRefs[id]?.forEach(key => symRefs?.[key]?.delete(id));
|
||||
delete idRefs[id];
|
||||
} else {
|
||||
for (let key in symRefs) {
|
||||
symRefs[key]?.delete(id);
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
delete this.id2targets_[id];
|
||||
delete this.id2cmp_[id];
|
||||
|
@ -156,6 +156,7 @@ public:
|
||||
* memorize last active range(s)
|
||||
*/
|
||||
void SetLastActiveRange(uint32_t from, uint32_t to);
|
||||
std::pair<uint32_t, uint32_t> GetLastActiveRange() { return lastActiveRanges_[0]; };
|
||||
|
||||
// formatting internal structures to string for debug output
|
||||
// and possibly in modified form for DFX in the future
|
||||
|
@ -39,8 +39,9 @@ public:
|
||||
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;
|
||||
const std::function<std::list<std::string>(uint32_t from, uint32_t to)>& onGetTypes4Range,
|
||||
const std::function<void(uint32_t from, uint32_t to)>& onSetActiveRange) = 0;
|
||||
virtual void UpdateRenderState(uint32_t totalCount, bool visibleItemsChanged) = 0;
|
||||
virtual void OnMove(std::function<void(int32_t, int32_t)>&& onMove) = 0;
|
||||
|
||||
private:
|
||||
|
@ -29,7 +29,8 @@ void RepeatVirtualScrollModelNG::Create(
|
||||
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)
|
||||
const std::function<std::list<std::string>(uint32_t from, uint32_t to)>& onGetTypes4Range,
|
||||
const std::function<void(uint32_t from, uint32_t to)>& onSetActiveRange)
|
||||
{
|
||||
ACE_SCOPED_TRACE("RepeatVirtualScrollModelNG::Create");
|
||||
auto* stack = ViewStackProcessor::GetInstance();
|
||||
@ -41,20 +42,20 @@ void RepeatVirtualScrollModelNG::Create(
|
||||
onCreateNode,
|
||||
onUpdateNode,
|
||||
onGetKeys4Range,
|
||||
onGetTypes4Range
|
||||
);
|
||||
onGetTypes4Range,
|
||||
onSetActiveRange);
|
||||
stack->Push(repeatNode);
|
||||
stack->PopContainer();
|
||||
}
|
||||
|
||||
void RepeatVirtualScrollModelNG::InvalidateKeyCache(uint32_t totalCount)
|
||||
void RepeatVirtualScrollModelNG::UpdateRenderState(uint32_t totalCount, bool visibleItemsChanged)
|
||||
{
|
||||
auto* stack = ViewStackProcessor::GetInstance();
|
||||
auto nodeId = stack->ClaimNodeId();
|
||||
auto repeatNode = ElementRegister::GetInstance()->GetSpecificItemById<RepeatVirtualScrollNode>(nodeId);
|
||||
CHECK_NULL_VOID(repeatNode);
|
||||
repeatNode->UpdateTotalCount(totalCount);
|
||||
repeatNode->InvalidateKeyCache();
|
||||
repeatNode->UpdateRenderState(visibleItemsChanged);
|
||||
}
|
||||
|
||||
void RepeatVirtualScrollModelNG::OnMove(std::function<void(int32_t, int32_t)>&& onMove)
|
||||
|
@ -35,9 +35,10 @@ public:
|
||||
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;
|
||||
const std::function<std::list<std::string>(uint32_t from, uint32_t to)>& onGetTypes4Range,
|
||||
const std::function<void(uint32_t from, uint32_t to)>& onSetActiveRange) override;
|
||||
|
||||
void InvalidateKeyCache(uint32_t totalCount) override;
|
||||
void UpdateRenderState(uint32_t totalCount, bool visibleItemsChanged) override;
|
||||
|
||||
void OnMove(std::function<void(int32_t, int32_t)>&& onMove) override;
|
||||
};
|
||||
|
@ -33,7 +33,8 @@ RefPtr<RepeatVirtualScrollNode> RepeatVirtualScrollNode::GetOrCreateRepeatNode(i
|
||||
const std::function<void(uint32_t)>& onCreateNode,
|
||||
const std::function<void(const std::string&, uint32_t)>& onUpdateNode,
|
||||
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetKeys4Range,
|
||||
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetTypes4Range)
|
||||
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetTypes4Range,
|
||||
const std::function<void(uint32_t, uint32_t)>& onSetActiveRange)
|
||||
{
|
||||
auto node = ElementRegister::GetInstance()->GetSpecificItemById<RepeatVirtualScrollNode>(nodeId);
|
||||
if (node) {
|
||||
@ -42,7 +43,8 @@ RefPtr<RepeatVirtualScrollNode> RepeatVirtualScrollNode::GetOrCreateRepeatNode(i
|
||||
return node;
|
||||
}
|
||||
node = MakeRefPtr<RepeatVirtualScrollNode>(
|
||||
nodeId, totalCount, templateCachedCountMap, onCreateNode, onUpdateNode, onGetKeys4Range, onGetTypes4Range);
|
||||
nodeId, totalCount, templateCachedCountMap, onCreateNode, onUpdateNode, onGetKeys4Range, onGetTypes4Range,
|
||||
onSetActiveRange);
|
||||
|
||||
ElementRegister::GetInstance()->AddUINode(node);
|
||||
return node;
|
||||
@ -53,13 +55,22 @@ RepeatVirtualScrollNode::RepeatVirtualScrollNode(int32_t nodeId, int32_t totalCo
|
||||
const std::function<void(uint32_t)>& onCreateNode,
|
||||
const std::function<void(const std::string&, uint32_t)>& onUpdateNode,
|
||||
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetKeys4Range,
|
||||
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetTypes4Range)
|
||||
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetTypes4Range,
|
||||
const std::function<void(uint32_t, uint32_t)>& onSetActiveRange)
|
||||
: ForEachBaseNode(V2::JS_REPEAT_ETS_TAG, nodeId), totalCount_(totalCount),
|
||||
caches_(templateCachedCountMap, onCreateNode, onUpdateNode, onGetKeys4Range, onGetTypes4Range),
|
||||
onSetActiveRange_(onSetActiveRange),
|
||||
postUpdateTaskHasBeenScheduled_(false)
|
||||
{
|
||||
}
|
||||
|
||||
void RepeatVirtualScrollNode::UpdateTotalCount(uint32_t totalCount)
|
||||
{
|
||||
TAG_LOGD(AceLogTag::ACE_REPEAT, "UpdateTotalCount: %{public}d", totalCount);
|
||||
totalCount_ = totalCount;
|
||||
}
|
||||
|
||||
|
||||
void RepeatVirtualScrollNode::DoSetActiveChildRange(
|
||||
int32_t start, int32_t end,
|
||||
int32_t cacheStart, int32_t cacheEnd)
|
||||
@ -74,6 +85,8 @@ void RepeatVirtualScrollNode::DoSetActiveChildRange(
|
||||
|
||||
// memorize active range
|
||||
caches_.SetLastActiveRange(start - cacheStart, end + cacheEnd);
|
||||
// notify TS side
|
||||
onSetActiveRange_(start, end);
|
||||
|
||||
bool needSync = caches_.RebuildL1([start, end, cacheStart, cacheEnd, this](
|
||||
int32_t index, const RefPtr<UINode>& node) -> bool {
|
||||
@ -105,8 +118,7 @@ void RepeatVirtualScrollNode::DoSetActiveChildRange(
|
||||
((end < start) && (index <= end + cacheEnd || start - cacheStart <= index))) {
|
||||
// keep in Repeat L1
|
||||
TAG_LOGD(AceLogTag::ACE_REPEAT,
|
||||
" ... in visible + pre-render range: index %{public}d -> nodeId %{public}d: "
|
||||
"keep in Repeat L1",
|
||||
" ... in visible + pre-render range: index %{public}d -> nodeId %{public}d: keep in Repeat L1",
|
||||
static_cast<int32_t>(index), frameNode->GetId());
|
||||
return true;
|
||||
}
|
||||
@ -116,8 +128,7 @@ void RepeatVirtualScrollNode::DoSetActiveChildRange(
|
||||
"detach, move to spare items L2",
|
||||
index, frameNode->GetId());
|
||||
|
||||
// move active node into L2 cached.
|
||||
// check transition flag.
|
||||
// move active node into L2 cached. check transition flag.
|
||||
if (node->OnRemoveFromParent(true)) {
|
||||
// OnRemoveFromParent returns true means the child can be removed from tree immediately.
|
||||
RemoveDisappearingChild(node);
|
||||
@ -165,6 +176,9 @@ void RepeatVirtualScrollNode::DropFromL1(const std::string& key)
|
||||
void RepeatVirtualScrollNode::DoSetActiveChildRange(
|
||||
const std::set<int32_t>& activeItems, const std::set<int32_t>& cachedItems, int32_t baseIndex)
|
||||
{
|
||||
// Notify TS side. Verify line below when DoSetActiveChildRange() will start to be used.
|
||||
// Call onSetActiveRange_ here;
|
||||
|
||||
bool needSync =
|
||||
caches_.RebuildL1([&activeItems, &cachedItems, baseIndex, this](int32_t index, RefPtr<UINode> node) -> bool {
|
||||
if (node == nullptr) {
|
||||
@ -197,19 +211,28 @@ void RepeatVirtualScrollNode::DoSetActiveChildRange(
|
||||
}
|
||||
}
|
||||
|
||||
void RepeatVirtualScrollNode::InvalidateKeyCache()
|
||||
void RepeatVirtualScrollNode::UpdateRenderState(bool visibleItemsChanged)
|
||||
{
|
||||
TAG_LOGD(AceLogTag::ACE_REPEAT,
|
||||
"InvalidateKeyCache triggered by Repeat rerender: nodeId: %{public}d .",
|
||||
static_cast<int32_t>(GetId()));
|
||||
// empty the cache index -> key
|
||||
// C++ will need to ask all new keys from JS side
|
||||
caches_.InvalidateKeyAndTTypeCaches();
|
||||
children_.clear();
|
||||
"UpdateRenderState triggered by Repeat rerender: nodeId: %{public}d, visibleItemsChanged: %{public}d",
|
||||
static_cast<int32_t>(GetId()), visibleItemsChanged);
|
||||
|
||||
auto frameNode = GetParentFrameNode();
|
||||
if (frameNode) {
|
||||
frameNode->ChildrenUpdatedFrom(0);
|
||||
if (visibleItemsChanged) {
|
||||
// empty the cache index -> key
|
||||
// C++ will need to ask all new keys from JS side
|
||||
caches_.InvalidateKeyAndTTypeCaches();
|
||||
children_.clear();
|
||||
|
||||
if (auto frameNode = GetParentFrameNode()) {
|
||||
frameNode->ChildrenUpdatedFrom(0);
|
||||
}
|
||||
} else {
|
||||
auto lastIndexInActiveRange = caches_.GetLastActiveRange().second;
|
||||
TAG_LOGD(AceLogTag::ACE_REPEAT, "lastIndexInActiveRange:%{public}d", lastIndexInActiveRange);
|
||||
|
||||
if (auto frameNode = GetParentFrameNode()) {
|
||||
frameNode->ChildrenUpdatedFrom(lastIndexInActiveRange + 1);
|
||||
}
|
||||
}
|
||||
|
||||
MarkNeedSyncRenderTree(true);
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <cstdint>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
#include "base/memory/referenced.h"
|
||||
#include "base/utils/macros.h"
|
||||
@ -41,28 +42,27 @@ public:
|
||||
const std::function<void(uint32_t)>& onCreateNode,
|
||||
const std::function<void(const std::string&, uint32_t)>& onUpdateNode,
|
||||
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetKeys4Range,
|
||||
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetTypes4Range);
|
||||
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetTypes4Range,
|
||||
const std::function<void(uint32_t, uint32_t)>& onSetActiveRange);
|
||||
|
||||
RepeatVirtualScrollNode(int32_t nodeId, int32_t totalCount,
|
||||
const std::map<std::string, std::pair<bool, 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);
|
||||
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetTypes4Range,
|
||||
const std::function<void(uint32_t, uint32_t)>& onSetActiveRange);
|
||||
|
||||
~RepeatVirtualScrollNode() override = default;
|
||||
|
||||
void UpdateTotalCount(uint32_t totalCount)
|
||||
{
|
||||
totalCount_ = totalCount;
|
||||
}
|
||||
void UpdateTotalCount(uint32_t totalCount);
|
||||
|
||||
// Number of children that Repeat can product
|
||||
// returns TotalCount
|
||||
int32_t FrameCount() const override;
|
||||
|
||||
// called from TS upon Repeat rerender
|
||||
void InvalidateKeyCache();
|
||||
void UpdateRenderState(bool visibleItemsChanged);
|
||||
|
||||
/**
|
||||
* GetChildren re-assembles children_ and cleanup the L1 cache
|
||||
@ -185,6 +185,9 @@ private:
|
||||
// caches:
|
||||
mutable RepeatVirtualScrollCaches caches_;
|
||||
|
||||
// get active child range
|
||||
std::function<void(uint32_t, uint32_t)> onSetActiveRange_;
|
||||
|
||||
// used by one of the unknown functions
|
||||
std::list<std::string> ids_;
|
||||
|
||||
|
@ -235,7 +235,9 @@ GridModelNG GridTestNg::CreateRepeatGrid(int32_t itemNumber, std::function<float
|
||||
}
|
||||
return keys;
|
||||
};
|
||||
repeatModel.Create(itemNumber, {}, createFunc, updateFunc, getKeys, getTypes);
|
||||
std::function<void(uint32_t, uint32_t)> setActiveRange = [](uint32_t start, uint32_t end) {
|
||||
};
|
||||
repeatModel.Create(itemNumber, {}, createFunc, updateFunc, getKeys, getTypes, setActiveRange);
|
||||
return model;
|
||||
}
|
||||
|
||||
|
@ -532,7 +532,9 @@ void ListTestNg::CreateRepeatVirtualScrollNode(int32_t itemNumber, const std::fu
|
||||
}
|
||||
return keys;
|
||||
};
|
||||
repeatModel.Create(itemNumber, {}, createFunc, updateFunc, getKeys, getTypes);
|
||||
std::function<void(uint32_t, uint32_t)> setActiveRange = [](uint32_t start, uint32_t end) {
|
||||
};
|
||||
repeatModel.Create(itemNumber, {}, createFunc, updateFunc, getKeys, getTypes, setActiveRange);
|
||||
}
|
||||
|
||||
void ListTestNg::FlushIdleTask(const RefPtr<ListPattern>& listPattern)
|
||||
|
@ -95,6 +95,12 @@ auto g_onGetTypes4Range = [](uint32_t from, uint32_t to) -> std::list<std::strin
|
||||
return types;
|
||||
};
|
||||
|
||||
/**
|
||||
* Function needed by RepeatVirtualScrollNode constructor
|
||||
*/
|
||||
auto g_onSetActiveRange = [](uint32_t from, uint32_t to) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Map needed by RepeatVirtualScrollCaches constructor
|
||||
*/
|
||||
@ -227,7 +233,8 @@ HWTEST_F(RepeatNodeCacheSyntaxTest, RepeatNodeCacheTest005, TestSize.Level1)
|
||||
g_onCreateNode,
|
||||
g_onUpdateNode,
|
||||
g_onGetKeys4Range,
|
||||
g_onGetTypes4Range
|
||||
g_onGetTypes4Range,
|
||||
g_onSetActiveRange
|
||||
);
|
||||
stack->Push(repeatNode);
|
||||
stack->PopContainer();
|
||||
@ -252,7 +259,8 @@ HWTEST_F(RepeatNodeCacheSyntaxTest, RepeatNodeCacheTest006, TestSize.Level1)
|
||||
g_onCreateNode,
|
||||
g_onUpdateNode,
|
||||
g_onGetKeys4Range,
|
||||
g_onGetTypes4Range
|
||||
g_onGetTypes4Range,
|
||||
g_onSetActiveRange
|
||||
);
|
||||
|
||||
/**
|
||||
@ -287,7 +295,8 @@ HWTEST_F(RepeatNodeCacheSyntaxTest, RepeatNodeCacheTest007, TestSize.Level1)
|
||||
g_onCreateNode,
|
||||
g_onUpdateNode,
|
||||
g_onGetKeys4Range,
|
||||
g_onGetTypes4Range
|
||||
g_onGetTypes4Range,
|
||||
g_onSetActiveRange
|
||||
);
|
||||
|
||||
/**
|
||||
@ -316,7 +325,8 @@ HWTEST_F(RepeatNodeCacheSyntaxTest, RepeatNodeCacheTest008, TestSize.Level1)
|
||||
g_onCreateNode,
|
||||
g_onUpdateNode,
|
||||
g_onGetKeys4Range,
|
||||
g_onGetTypes4Range
|
||||
g_onGetTypes4Range,
|
||||
g_onSetActiveRange
|
||||
);
|
||||
|
||||
|
||||
@ -336,7 +346,7 @@ HWTEST_F(RepeatNodeCacheSyntaxTest, RepeatNodeCacheTest008, TestSize.Level1)
|
||||
repeatNode->DoSetActiveChildRange(1, 2, 1, 1);
|
||||
repeatNode->DoSetActiveChildRange(activeItems, cachedItems, 1);
|
||||
repeatNode->DropFromL1("Key1");
|
||||
repeatNode->InvalidateKeyCache();
|
||||
repeatNode->UpdateRenderState(true);
|
||||
repeatNode->RecycleItems(0, 100);
|
||||
repeatNode->MoveData(0, 100);
|
||||
|
||||
@ -367,7 +377,8 @@ HWTEST_F(RepeatNodeCacheSyntaxTest, RepeatNodeCacheTest009, TestSize.Level1)
|
||||
g_onCreateNode,
|
||||
g_onUpdateNode,
|
||||
g_onGetKeys4Range,
|
||||
g_onGetTypes4Range
|
||||
g_onGetTypes4Range,
|
||||
g_onSetActiveRange
|
||||
);
|
||||
|
||||
/**
|
||||
@ -648,7 +659,8 @@ HWTEST_F(RepeatNodeCacheSyntaxTest, RepeatNodeCacheTest021, TestSize.Level1)
|
||||
g_onCreateNode,
|
||||
g_onUpdateNode,
|
||||
g_onGetKeys4Range,
|
||||
g_onGetTypes4Range
|
||||
g_onGetTypes4Range,
|
||||
g_onSetActiveRange
|
||||
);
|
||||
|
||||
ConfigurationChange cfgChange;
|
||||
|
Loading…
Reference in New Issue
Block a user