1/* 2 * Copyright (c) 2023 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 16interface Frame { 17 x: number; 18 y: number; 19 width: number; 20 height: number; 21} 22 23interface Vector2 { 24 x: number 25 y: number 26} 27 28interface Vector3 { 29 x: number; 30 y: number; 31 z: number; 32} 33 34type Transform = [ 35 number, 36 number, 37 number, 38 number, 39 number, 40 number, 41 number, 42 number, 43 number, 44 number, 45 number, 46 number, 47 number, 48 number, 49 number, 50 number 51]; 52 53enum BorderStyle { 54 SOLID = 0, 55 DASHED, 56 DOTTED, 57 NONE 58} 59 60type EdgeStyles = { 61 top?: BorderStyle; 62 right?: BorderStyle; 63 bottom?: BorderStyle; 64 left?: BorderStyle; 65}; 66 67interface Edges { 68 left: number, 69 right: number, 70 top: number, 71 bottom: number, 72} 73 74type EdgeWidths = Edges; 75 76type EdgeColors = Edges; 77 78interface Corners { 79 topLeft: number, 80 topRight: number, 81 bottomLeft: number, 82 bottomRight: number 83} 84 85type BorderRadiuses = Corners; 86 87interface Rect { 88 left: number, 89 right: number, 90 top: number, 91 bottom: number 92} 93 94interface CornerRadius { 95 topLeft: Vector2, 96 topRight: Vector2, 97 bottomLeft: Vector2, 98 bottomRight: Vector2 99} 100 101interface RoundRect { 102 rect: Rect, 103 corners: CornerRadius 104} 105 106interface Circle { 107 centerX: number, 108 centerY: number, 109 radius: number 110} 111 112interface CommandPath { 113 commands: string 114} 115 116class ShapeMask { 117 public rect: Rect | null = null; 118 public roundRect: RoundRect | null = null; 119 public circle: Circle | null = null; 120 public oval: Rect | null = null; 121 public path: CommandPath | null = null; 122 public fillColor: number = 0XFF000000; 123 public strokeColor: number = 0XFF000000; 124 public strokeWidth: number = 0; 125 setRectShape(rect: Rect) { 126 this.rect = rect; 127 this.roundRect = null; 128 this.circle = null; 129 this.oval = null; 130 this.path = null; 131 } 132 setRoundRectShape(roundRect: RoundRect) { 133 this.roundRect = roundRect; 134 this.rect = null; 135 this.circle = null; 136 this.oval = null; 137 this.path = null; 138 } 139 setCircleShape(circle: Circle) { 140 this.circle = circle; 141 this.rect = null; 142 this.roundRect = null; 143 this.oval = null; 144 this.path = null; 145 } 146 setOvalShape(oval: Rect) { 147 this.oval = oval; 148 this.rect = null; 149 this.circle = null; 150 this.roundRect = null; 151 this.path = null; 152 } 153 setCommandPath(path: CommandPath) { 154 this.path = path; 155 this.oval = null; 156 this.rect = null; 157 this.circle = null; 158 this.roundRect = null; 159 } 160} 161 162class RenderNode { 163 private childrenList: Array<RenderNode>; 164 private nodePtr: number | null; 165 private parentRenderNode: RenderNode | null; 166 private backgroundColorValue: number; 167 private clipToFrameValue: boolean; 168 private frameValue: Frame; 169 private opacityValue: number; 170 private pivotValue: Vector2; 171 private rotationValue: Vector3; 172 private scaleValue: Vector2; 173 private shadowColorValue: number; 174 private shadowOffsetValue: Vector2; 175 private shadowAlphaValue: number; 176 private shadowElevationValue: number; 177 private shadowRadiusValue: number; 178 private transformValue: Transform; 179 private translationValue: Vector2; 180 private baseNode_ : __JSBaseNode__; 181 private borderStyleValue: EdgeStyles; 182 private borderWidthValue: EdgeWidths; 183 private borderColorValue: EdgeColors; 184 private borderRadiusValue: BorderRadiuses; 185 private shapeMaskValue: ShapeMask; 186 187 constructor(type: string) { 188 this.nodePtr = null; 189 this.childrenList = []; 190 this.parentRenderNode = null; 191 this.backgroundColorValue = 0; 192 this.clipToFrameValue = false; 193 this.frameValue = { x: 0, y: 0, width: 0, height: 0 }; 194 this.opacityValue = 1.0; 195 this.pivotValue = { x: 0.5, y: 0.5 }; 196 this.rotationValue = { x: 0, y: 0, z: 0 }; 197 this.scaleValue = { x: 1.0, y: 1.0 }; 198 this.shadowColorValue = 0; 199 this.shadowOffsetValue = { x: 0, y: 0 }; 200 this.shadowAlphaValue = 0; 201 this.shadowElevationValue = 0; 202 this.shadowRadiusValue = 0; 203 this.transformValue = [1, 0, 0, 0, 204 0, 1, 0, 0, 205 0, 0, 1, 0, 206 0, 0, 0, 1]; 207 this.translationValue = { x: 0, y: 0 }; 208 if (type === 'FrameNode') { 209 return; 210 } 211 this.baseNode_ = new __JSBaseNode__(); 212 this.baseNode_.draw = this.draw; 213 this.nodePtr = this.baseNode_.createRenderNode(this); 214 } 215 216 set backgroundColor(color: number) { 217 this.backgroundColorValue = this.checkUndefinedOrNullWithDefaultValue<number>(color, 0); 218 GetUINativeModule().renderNode.setBackgroundColor(this.nodePtr, this.backgroundColorValue); 219 } 220 set clipToFrame(useClip: boolean) { 221 this.clipToFrameValue = this.checkUndefinedOrNullWithDefaultValue<boolean>(useClip, false); 222 GetUINativeModule().renderNode.setClipToFrame(this.nodePtr, this.clipToFrameValue); 223 } 224 set frame(frame: Frame) { 225 if (frame === undefined || frame === null) { 226 this.frameValue = { x: 0, y: 0, width: 0, height: 0 }; 227 } else { 228 this.size = { width: frame.width, height: frame.height }; 229 this.position = { x: frame.x, y: frame.y }; 230 } 231 } 232 set opacity(value: number) { 233 this.opacityValue = this.checkUndefinedOrNullWithDefaultValue<number>(value, 1.0); 234 GetUINativeModule().common.setOpacity(this.nodePtr, this.opacityValue); 235 } 236 set pivot(pivot: Vector2) { 237 if (pivot === undefined || pivot === null) { 238 this.pivotValue = { x: 0.5, y: 0.5 }; 239 } else { 240 this.pivotValue.x = this.checkUndefinedOrNullWithDefaultValue<number>(pivot.x, 0.5); 241 this.pivotValue.y = this.checkUndefinedOrNullWithDefaultValue<number>(pivot.y, 0.5); 242 } 243 GetUINativeModule().renderNode.setPivot(this.nodePtr, this.pivotValue.x, this.pivotValue.y); 244 } 245 set position(position: Vector2) { 246 if (position === undefined || position === null) { 247 this.frameValue.x = 0; 248 this.frameValue.y = 0; 249 } else { 250 this.frameValue.x = this.checkUndefinedOrNullWithDefaultValue<number>(position.x, 0); 251 this.frameValue.y = this.checkUndefinedOrNullWithDefaultValue<number>(position.y, 0); 252 } 253 GetUINativeModule().common.setPosition(this.nodePtr, this.frameValue.x, this.frameValue.y); 254 } 255 set rotation(rotation: Vector3) { 256 if (rotation === undefined || rotation === null) { 257 this.rotationValue = { x: 0, y: 0, z: 0 }; 258 } else { 259 this.rotationValue.x = this.checkUndefinedOrNullWithDefaultValue<number>(rotation.x, 0); 260 this.rotationValue.y = this.checkUndefinedOrNullWithDefaultValue<number>(rotation.y, 0); 261 this.rotationValue.z = this.checkUndefinedOrNullWithDefaultValue<number>(rotation.z, 0); 262 } 263 GetUINativeModule().renderNode.setRotation(this.nodePtr, this.rotationValue.x, this.rotationValue.y, this.rotationValue.z); 264 } 265 set scale(scale: Vector2) { 266 if (scale === undefined || scale === null) { 267 this.scaleValue = { x: 1.0, y: 1.0 }; 268 } else { 269 this.scaleValue.x = this.checkUndefinedOrNullWithDefaultValue<number>(scale.x, 1.0); 270 this.scaleValue.y = this.checkUndefinedOrNullWithDefaultValue<number>(scale.y, 1.0); 271 } 272 GetUINativeModule().renderNode.setScale(this.nodePtr, this.scaleValue.x, this.scaleValue.y); 273 } 274 set shadowColor(color: number) { 275 this.shadowColorValue = this.checkUndefinedOrNullWithDefaultValue<number>(color, 0); 276 GetUINativeModule().renderNode.setShadowColor(this.nodePtr, this.shadowColorValue); 277 } 278 set shadowOffset(offset: Vector2) { 279 if (offset === undefined || offset === null) { 280 this.shadowOffsetValue = { x: 0, y: 0 }; 281 } else { 282 this.shadowOffsetValue.x = this.checkUndefinedOrNullWithDefaultValue<number>(offset.x, 0); 283 this.shadowOffsetValue.y = this.checkUndefinedOrNullWithDefaultValue<number>(offset.y, 0); 284 } 285 GetUINativeModule().renderNode.setShadowOffset(this.nodePtr, this.shadowOffsetValue.x, this.shadowOffsetValue.y); 286 } 287 set shadowAlpha(alpha: number) { 288 this.shadowAlphaValue = this.checkUndefinedOrNullWithDefaultValue<number>(alpha, 0); 289 GetUINativeModule().renderNode.setShadowAlpha(this.nodePtr, this.shadowAlphaValue); 290 } 291 set shadowElevation(elevation: number) { 292 this.shadowElevationValue = this.checkUndefinedOrNullWithDefaultValue<number>(elevation, 0); 293 GetUINativeModule().renderNode.setShadowElevation(this.nodePtr, this.shadowElevationValue); 294 } 295 set shadowRadius(radius: number) { 296 this.shadowRadiusValue = this.checkUndefinedOrNullWithDefaultValue<number>(radius, 0); 297 GetUINativeModule().renderNode.setShadowRadius(this.nodePtr, this.shadowRadiusValue); 298 } 299 set size(size: Size) { 300 if (size === undefined || size === null) { 301 this.frameValue.width = 0; 302 this.frameValue.height = 0; 303 } else { 304 this.frameValue.width = this.checkUndefinedOrNullWithDefaultValue<number>(size.width, 0); 305 this.frameValue.height = this.checkUndefinedOrNullWithDefaultValue<number>(size.height, 0); 306 } 307 GetUINativeModule().renderNode.setSize(this.nodePtr, this.frameValue.width, this.frameValue.height); 308 } 309 set transform(transform: Transform) { 310 if (transform === undefined || transform === null) { 311 this.transformValue = [1, 0, 0, 0, 312 0, 1, 0, 0, 313 0, 0, 1, 0, 314 0, 0, 0, 1]; 315 } else { 316 let i: number = 0; 317 while (i < transform.length && i < 16) { 318 if (i % 5 === 0) { 319 this.transformValue[i] = this.checkUndefinedOrNullWithDefaultValue<number>(transform[i], 1); 320 } else { 321 this.transformValue[i] = this.checkUndefinedOrNullWithDefaultValue<number>(transform[i], 0); 322 } 323 i = i + 1; 324 } 325 } 326 GetUINativeModule().common.setTransform(this.nodePtr, this.transformValue); 327 } 328 set translation(translation: Vector2) { 329 if (translation === undefined || translation === null) { 330 this.translationValue = { x: 0, y: 0 }; 331 } else { 332 this.translationValue.x = this.checkUndefinedOrNullWithDefaultValue<number>(translation.x, 0); 333 this.translationValue.y = this.checkUndefinedOrNullWithDefaultValue<number>(translation.y, 0); 334 } 335 GetUINativeModule().renderNode.setTranslate(this.nodePtr, this.translationValue.x, this.translationValue.y, 0); 336 } 337 get backgroundColor(): number { 338 return this.backgroundColorValue; 339 } 340 get clipToFrame(): boolean { 341 return this.clipToFrameValue; 342 } 343 get opacity(): number { 344 return this.opacityValue; 345 } 346 get frame(): Frame { 347 return this.frameValue; 348 } 349 get pivot(): Vector2 { 350 return this.pivotValue; 351 } 352 get position(): Vector2 { 353 return { x: this.frameValue.x, y: this.frameValue.y }; 354 } 355 get rotation(): Vector3 { 356 return this.rotationValue; 357 } 358 get scale(): Vector2 { 359 return this.scaleValue; 360 } 361 get shadowColor(): number { 362 return this.shadowColorValue; 363 } 364 get shadowOffset(): Vector2 { 365 return this.shadowOffsetValue; 366 } 367 get shadowAlpha(): number { 368 return this.shadowAlphaValue; 369 } 370 get shadowElevation(): number { 371 return this.shadowElevationValue; 372 } 373 get shadowRadius(): number { 374 return this.shadowRadiusValue; 375 } 376 get size(): Size { 377 return { width: this.frameValue.width, height: this.frameValue.height }; 378 } 379 get transform(): Transform { 380 return this.transformValue; 381 } 382 get translation(): Vector2 { 383 return this.translationValue; 384 } 385 checkUndefinedOrNullWithDefaultValue<T>(arg: T, defaultValue: T): T { 386 if (arg === undefined || arg === null) { 387 return defaultValue; 388 } else { 389 return arg; 390 } 391 } 392 appendChild(node: RenderNode) { 393 if (node === undefined || node === null) { 394 return; 395 } 396 if (this.childrenList.findIndex(element => element === node) !== -1) { 397 return; 398 } 399 this.childrenList.push(node); 400 node.parentRenderNode = this; 401 GetUINativeModule().renderNode.appendChild(this.nodePtr, node.nodePtr); 402 } 403 insertChildAfter(child: RenderNode, sibling: RenderNode | null) { 404 if (child === undefined || child === null) { 405 return; 406 } 407 let indexOfNode: number = this.childrenList.findIndex(element => element === child); 408 if (indexOfNode !== -1) { 409 return; 410 } 411 child.parentRenderNode = this; 412 let indexOfSibling = this.childrenList.findIndex(element => element === sibling); 413 if (indexOfSibling === -1) { 414 sibling === null; 415 } 416 if (sibling === undefined || sibling === null) { 417 this.childrenList.splice(0, 0, child); 418 GetUINativeModule().renderNode.insertChildAfter(this.nodePtr, child.nodePtr, null); 419 } else { 420 this.childrenList.splice(indexOfSibling + 1, 0, child); 421 GetUINativeModule().renderNode.insertChildAfter(this.nodePtr, child.nodePtr, sibling.nodePtr); 422 } 423 } 424 removeChild(node: RenderNode) { 425 if (node === undefined || node === null) { 426 return; 427 } 428 const index = this.childrenList.findIndex(element => element === node); 429 if (index === -1) { 430 return; 431 } 432 const child = this.childrenList[index]; 433 child.parentRenderNode = null; 434 this.childrenList.splice(index, 1); 435 GetUINativeModule().renderNode.removeChild(this.nodePtr, node.nodePtr); 436 } 437 clearChildren() { 438 this.childrenList = new Array<RenderNode>(); 439 GetUINativeModule().renderNode.clearChildren(this.nodePtr); 440 } 441 getChild(index: number): RenderNode | null { 442 if (this.childrenList.length > index && index >= 0) { 443 return this.childrenList[index]; 444 } 445 return null; 446 } 447 getFirstChild(): RenderNode | null { 448 if (this.childrenList.length > 0) { 449 return this.childrenList[0]; 450 } 451 return null; 452 } 453 getNextSibling(): RenderNode | null { 454 if (this.parentRenderNode === undefined || this.parentRenderNode === null) { 455 return null; 456 } 457 let siblingList = this.parentRenderNode.childrenList; 458 const index = siblingList.findIndex(element => element === this); 459 if (index === -1) { 460 return null; 461 } 462 return this.parentRenderNode.getChild(index + 1); 463 } 464 getPreviousSibling(): RenderNode | null { 465 if (this.parentRenderNode === undefined || this.parentRenderNode === null) { 466 return null; 467 } 468 let siblingList = this.parentRenderNode.childrenList; 469 const index = siblingList.findIndex(element => element === this); 470 if (index === -1) { 471 return null; 472 } 473 return this.parentRenderNode.getChild(index - 1); 474 } 475 setNodePtr(nodePtr: number | null) { 476 this.nodePtr = nodePtr; 477 } 478 setBaseNode(baseNode: BaseNode | null) { 479 this.baseNode_ = baseNode; 480 } 481 dispose() { 482 this.baseNode_.dispose() 483 } 484 getNodePtr(): number | null { 485 return this.nodePtr; 486 } 487 draw(context) { 488 } 489 invalidate() { 490 GetUINativeModule().renderNode.invalidate(this.nodePtr); 491 } 492 set borderStyle(style: EdgeStyles) { 493 if (style === undefined || style === null) { 494 this.borderStyleValue = { left: BorderStyle.NONE, top: BorderStyle.NONE, right: BorderStyle.NONE, bottom: BorderStyle.NONE }; 495 } else { 496 this.borderStyleValue = style; 497 } 498 GetUINativeModule().renderNode.setBorderStyle(this.nodePtr, this.borderStyleValue.left, this.borderStyleValue.top, this.borderStyleValue.right, this.borderStyleValue.bottom); 499 } 500 get borderStyle(): EdgeStyles { 501 return this.borderStyleValue; 502 } 503 set borderWidth(width: EdgeWidths) { 504 if (width === undefined || width === null) { 505 this.borderWidthValue = { left: 0, top: 0, right: 0, bottom: 0 }; 506 } else { 507 this.borderWidthValue = width; 508 } 509 GetUINativeModule().renderNode.setBorderWidth(this.nodePtr, this.borderWidthValue.left, this.borderWidthValue.top, this.borderWidthValue.right, this.borderWidthValue.bottom); 510 } 511 get borderWidth(): EdgeWidths { 512 return this.borderWidthValue; 513 } 514 set borderColor(color: EdgeColors) { 515 if (color === undefined || color === null) { 516 this.borderColorValue = { left: 0XFF000000, top: 0XFF000000, right: 0XFF000000, bottom: 0XFF000000 }; 517 } else { 518 this.borderColorValue = color; 519 } 520 GetUINativeModule().renderNode.setBorderColor(this.nodePtr, this.borderColorValue.left, this.borderColorValue.top, this.borderColorValue.right, this.borderColorValue.bottom); 521 } 522 get borderColor(): EdgeColors { 523 return this.borderColorValue; 524 } 525 set borderRadius(radius: BorderRadiuses) { 526 if (radius === undefined || radius === null) { 527 this.borderRadiusValue = { topLeft: 0, topRight: 0, bottomLeft: 0, bottomRight: 0 }; 528 } else { 529 this.borderRadiusValue = radius; 530 } 531 GetUINativeModule().renderNode.setBorderRadius(this.nodePtr, this.borderRadiusValue.topLeft, this.borderRadiusValue.topRight, this.borderRadiusValue.bottomLeft, this.borderRadiusValue.bottomRight); 532 } 533 get borderRadius(): BorderRadiuses { 534 return this.borderRadiusValue; 535 } 536 set shapeMask(shapeMask: ShapeMask) { 537 if (shapeMask === undefined || shapeMask === null) { 538 this.shapeMaskValue = new ShapeMask(); 539 } else { 540 this.shapeMaskValue = shapeMask; 541 } 542 if (this.shapeMaskValue.rect !== null) { 543 const rectMask = this.shapeMaskValue.rect; 544 GetUINativeModule().renderNode.setRectMask(this.nodePtr, rectMask.left, rectMask.top, rectMask.right, rectMask.bottom, this.shapeMaskValue.fillColor, this.shapeMaskValue.strokeColor, this.shapeMaskValue.strokeWidth); 545 } else if (this.shapeMaskValue.circle !== null) { 546 const circle = this.shapeMaskValue.circle; 547 GetUINativeModule().renderNode.setCircleMask(this.nodePtr, circle.centerX, circle.centerY, circle.radius, this.shapeMaskValue.fillColor, this.shapeMaskValue.strokeColor, this.shapeMaskValue.strokeWidth); 548 } else if (this.shapeMaskValue.roundRect !== null) { 549 const reoundRect = this.shapeMask.roundRect; 550 const corners = reoundRect.corners; 551 const rect = reoundRect.rect; 552 GetUINativeModule().renderNode.setRoundRectMask( 553 this.nodePtr, 554 corners.topLeft.x, 555 corners.topLeft.y, 556 corners.topRight.x, 557 corners.topRight.y, 558 corners.bottomLeft.x, 559 corners.bottomLeft.y, 560 corners.bottomRight.x, 561 corners.bottomRight.y, 562 rect.left, 563 rect.top, 564 rect.right, 565 rect.bottom, 566 this.shapeMaskValue.fillColor, 567 this.shapeMaskValue.strokeColor, 568 this.shapeMaskValue.strokeWidth); 569 } else if (this.shapeMaskValue.oval !== null) { 570 const oval = this.shapeMaskValue.oval; 571 GetUINativeModule().renderNode.setOvalMask(this.nodePtr, oval.left, oval.top, oval.right, oval.bottom, this.shapeMaskValue.fillColor, this.shapeMaskValue.strokeColor, this.shapeMaskValue.strokeWidth); 572 } else if (this.shapeMaskValue.path !== null) { 573 const path = this.shapeMaskValue.path; 574 GetUINativeModule().renderNode.setPath(this.nodePtr, path.commands, this.shapeMaskValue.fillColor, this.shapeMaskValue.strokeColor, this.shapeMaskValue.strokeWidth); 575 } 576 } 577 get shapeMask(): ShapeMask { 578 return this.shapeMaskValue; 579 } 580} 581 582function edgeColors(all: number): EdgeColors { 583 return { left: all, top: all, right: all, bottom: all }; 584} 585 586function edgeWidths(all: number): EdgeWidths { 587 return { left: all, top: all, right: all, bottom: all }; 588} 589 590function borderStyles(all: BorderStyle): EdgeStyles { 591 return { left: all, top: all, right: all, bottom: all }; 592} 593 594function borderRadiuses(all: number): BorderRadiuses { 595 return { topLeft: all, topRight: all, bottomLeft: all, bottomRight: all }; 596} 597