1/* 2 * Copyright (c) 2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15/// <reference path='./import.ts' /> 16 17const overrideMap = new Map<string, Map<string, 18 new (value: string | number | boolean | object) => ModifierWithKey<string | number | boolean | object>>>(); 19 20 21overrideMap.set('ArkCheckboxComponent', new Map([ 22 ['Symbol(width)', CheckboxWidthModifier], 23 ['Symbol(height)', CheckboxHeightModifier] 24])); 25 26overrideMap.set('ArkTextComponent', new Map([ 27 ['Symbol(foregroundColor)', TextForegroundColorModifier] 28])); 29 30function applyAndMergeModifier<T, M extends ArkComponent, C extends ArkComponent>(instance: T, modifier: M): void { 31 let myMap = modifier._modifiersWithKeys as ModifierMap; 32 myMap.setOnChange((key: Symbol, value: AttributeModifierWithKey) => { 33 modifier._changed = !modifier._changed; 34 }); 35 36 // @ts-ignore 37 let component: C = instance as C; 38 mergeMaps(component._modifiersWithKeys, modifier._modifiersWithKeys); 39} 40 41function copyModifierWithKey(obj: ModifierWithKey<string | number | boolean | object>): ModifierWithKey<string | number | boolean | object> { 42 let newObj: ModifierWithKey<string | number | boolean | object> = { 43 ...obj, 44 applyStage: function (node: number): boolean { 45 throw new Error('Function not implemented.'); 46 }, 47 applyPeer: function (node: number, reset: boolean): void { 48 throw new Error('Function not implemented.'); 49 }, 50 checkObjectDiff: function (): boolean { 51 throw new Error('Function not implemented.'); 52 } 53 }; 54 newObj.applyStage = obj?.applyStage; 55 newObj.applyPeer = obj?.applyPeer; 56 newObj.checkObjectDiff = obj?.checkObjectDiff; 57 return newObj; 58} 59 60function mergeMaps(stageMap: Map<Symbol, AttributeModifierWithKey>, 61 newMap: Map<Symbol, AttributeModifierWithKey>): Map<Symbol, AttributeModifierWithKey> { 62 newMap.forEach((value, key) => { 63 stageMap.set(key, copyModifierWithKey(value)); 64 }); 65 66 return stageMap; 67} 68 69class ModifierUtils { 70 static dirtyComponentSet: Set<ArkComponent | ArkSpanComponent> = new Set(); 71 static dirtyFlag = false; 72 static timeoutId = -1; 73 74 static copyModifierWithKey(obj: ModifierWithKey<string | number | boolean | object>): ModifierWithKey<string | number | boolean | object> { 75 let newObj: ModifierWithKey<string | number | boolean | object> = { 76 ...obj, 77 applyStage: function (node: number): boolean { 78 throw new Error('Function not implemented.'); 79 }, 80 applyPeer: function (node: number, reset: boolean): void { 81 throw new Error('Function not implemented.'); 82 }, 83 checkObjectDiff: function (): boolean { 84 throw new Error('Function not implemented.'); 85 } 86 }; 87 newObj.applyStage = obj?.applyStage; 88 newObj.applyPeer = obj?.applyPeer; 89 newObj.checkObjectDiff = obj?.checkObjectDiff; 90 return newObj; 91 } 92 93 static mergeMaps(stageMap: Map<Symbol, AttributeModifierWithKey>, 94 newMap: Map<Symbol, AttributeModifierWithKey>): void { 95 newMap.forEach((value, key) => { 96 stageMap.set(key, this.copyModifierWithKey(value)); 97 }); 98 } 99 100 static mergeMapsEmplace<T0 extends string | number | boolean | object, M0 extends ModifierWithKey<T0>>( 101 stageMap: Map<Symbol, AttributeModifierWithKey>, 102 newMap: Map<Symbol, AttributeModifierWithKey>, 103 componentOverrideMap: Map<string, new (value: T0) => M0>): void { 104 newMap.forEach((value, key) => { 105 if (!key) { 106 ArkLogConsole.info('key of modifier map is undefined, ModifierWithKey is ' + 107 (value ? value.constructor.name.toString() : 'undefined')); 108 } else { 109 if (componentOverrideMap.has(key.toString())) { 110 //@ts-ignore 111 const newValue = new (componentOverrideMap.get(key.toString()))(value.stageValue); 112 stageMap.set(key, newValue); 113 } else { 114 stageMap.set(key, this.copyModifierWithKey(value)); 115 } 116 } 117 }); 118 } 119 120 static applyAndMergeModifier<T, M extends ArkComponent | ArkSpanComponent, C extends ArkComponent | ArkSpanComponent>(instance: T, modifier: M): void { 121 // @ts-ignore 122 let component: C = instance as C; 123 if (component.constructor.name && overrideMap.has(component.constructor.name)) { 124 const componentOverrideMap = overrideMap.get(component.constructor.name); 125 this.mergeMapsEmplace(component._modifiersWithKeys, modifier._modifiersWithKeys, 126 componentOverrideMap); 127 } else { 128 this.mergeMaps(component._modifiersWithKeys, modifier._modifiersWithKeys); 129 } 130 } 131 132 static applySetOnChange<T, M extends ArkComponent | ArkSpanComponent, C extends ArkComponent | ArkSpanComponent>(modifier: M): void { 133 // It is to make the stateMgmt can addRef of _changed, 134 // so that the modifier change can be observed by makeObserved when modifier._changed changed. 135 modifier._changed; 136 let myMap = modifier._modifiersWithKeys as ModifierMap; 137 if (modifier._classType === ModifierType.STATE) { 138 myMap.setOnChange((key: Symbol, value: AttributeModifierWithKey) => { 139 this.putDirtyModifier(modifier, value); 140 }); 141 } else { 142 myMap.setOnChange((key: Symbol, value: AttributeModifierWithKey) => { 143 modifier._changed = !modifier._changed; 144 }); 145 } 146 } 147 148 static putDirtyModifier<M extends ArkComponent | ArkSpanComponent>( 149 arkModifier: M, attributeModifierWithKey: ModifierWithKey<string | number | boolean | object>): void { 150 attributeModifierWithKey.value = attributeModifierWithKey.stageValue; 151 if (!arkModifier._weakPtr.invalid()) { 152 attributeModifierWithKey.applyPeer(arkModifier.nativePtr, 153 (attributeModifierWithKey.value === undefined || attributeModifierWithKey.value === null)); 154 } else { 155 ArkLogConsole.info('pointer is invalid when putDirtyModifier in ' + (arkModifier ? 156 arkModifier.constructor.name.toString() : 'undefined') + ' of ' + (attributeModifierWithKey ? 157 attributeModifierWithKey.constructor.name.toString() : 'undefined')); 158 } 159 this.dirtyComponentSet.add(arkModifier); 160 if (!this.dirtyFlag) { 161 this.dirtyFlag = true; 162 this.requestFrame(); 163 } 164 } 165 166 static requestFrame(): void { 167 const frameCallback = () => { 168 if (this.timeoutId !== -1) { 169 clearTimeout(this.timeoutId); 170 } 171 this.dirtyComponentSet.forEach(item => { 172 const nativePtrValid = !item._weakPtr.invalid(); 173 if (item._nativePtrChanged && nativePtrValid) { 174 item._modifiersWithKeys.forEach((value, key) => { 175 value.applyPeer(item.nativePtr, 176 (value.value === undefined || value.value === null)); 177 }); 178 item._nativePtrChanged = false; 179 } 180 if (nativePtrValid) { 181 getUINativeModule().frameNode.markDirty(item.nativePtr, 0b100); 182 } 183 }); 184 this.dirtyComponentSet.clear(); 185 this.dirtyFlag = false; 186 this.timeoutId = -1; 187 }; 188 if (this.timeoutId !== -1) { 189 clearTimeout(this.timeoutId); 190 } 191 this.timeoutId = setTimeout(frameCallback, 100); 192 getUINativeModule().frameNode.registerFrameCallback(frameCallback); 193 } 194} 195 196class ModifierMap { 197 private map_: Map<Symbol, AttributeModifierWithKey>; 198 private changeCallback: ((key: Symbol, value: AttributeModifierWithKey) => void) | undefined; 199 200 constructor() { 201 this.map_ = new Map(); 202 } 203 204 public clear(): void { 205 this.map_.clear(); 206 } 207 208 public delete(key: Symbol): boolean { 209 return this.map_.delete(key); 210 } 211 212 public forEach(callbackfn: (value: AttributeModifierWithKey, key: Symbol, 213 map: Map<Symbol, AttributeModifierWithKey>) => void, thisArg?: any): void { 214 this.map_.forEach(callbackfn, thisArg); 215 } 216 public get(key: Symbol): AttributeModifierWithKey | undefined { 217 return this.map_.get(key); 218 } 219 public has(key: Symbol): boolean { 220 return this.map_.has(key); 221 } 222 public set(key: Symbol, value: AttributeModifierWithKey): this { 223 const _a = this.changeCallback; 224 this.map_.set(key, value); 225 _a?.call(this, value); 226 return this; 227 } 228 public get size(): number { 229 return this.map_.size; 230 } 231 public entries(): IterableIterator<[Symbol, AttributeModifierWithKey]> { 232 return this.map_.entries(); 233 } 234 public keys(): IterableIterator<Symbol> { 235 return this.map_.keys(); 236 } 237 public values(): IterableIterator<AttributeModifierWithKey> { 238 return this.map_.values(); 239 } 240 public [Symbol.iterator](): IterableIterator<[Symbol, AttributeModifierWithKey]> { 241 return this.map_.entries(); 242 } 243 public get [Symbol.toStringTag](): string { 244 return 'ModifierMapTag'; 245 } 246 public setOnChange(callback: (key: Symbol, value: AttributeModifierWithKey) => void): void { 247 this.changeCallback = callback; 248 } 249} 250 251class AttributeUpdater { 252 private _state: number; 253 private _attribute: ArkComponent; 254 private _isAttributeUpdater: boolean; 255 256 static StateEnum = { 257 INIT: 0, 258 UPDATE: 1 259 }; 260 261 constructor() { 262 this._state = AttributeUpdater.StateEnum.INIT; 263 this._attribute = null; 264 this._isAttributeUpdater = true; 265 } 266 267 public get isAttributeUpdater(): boolean { 268 return this._isAttributeUpdater; 269 } 270 271 public get attribute(): ArkComponent { 272 return this._attribute; 273 } 274 275 public set attribute(value: ArkComponent) { 276 if (!this._attribute && value) { 277 this._attribute = value; 278 } 279 } 280 281 public get modifierState(): number { 282 return this._state; 283 } 284 285 public set modifierState(value: number) { 286 this._state = value; 287 } 288 289 initializeModifier(instance: ArkComponent): void {} 290 291 updateConstructorParams(...args: Object[]): void { 292 if (!this.attribute) { 293 ArkLogConsole.info('AttributeUpdater has not been initialized before updateConstructorParams.'); 294 return; 295 } 296 this.attribute.initialize(args); 297 } 298}