diff --git a/GaiaXHarmony/GXStretchBinding/GXStretch/src/main/ets/stretch/Node.ts b/GaiaXHarmony/GXStretchBinding/GXStretch/src/main/ets/stretch/Node.ts index 15f628067..5e8960d7c 100644 --- a/GaiaXHarmony/GXStretchBinding/GXStretch/src/main/ets/stretch/Node.ts +++ b/GaiaXHarmony/GXStretchBinding/GXStretch/src/main/ets/stretch/Node.ts @@ -6,7 +6,10 @@ import hilog from '@ohos.hilog'; export class Node { ptr?: number | null = null; + // 经过处理后的样式,可能在复用过程中被改写。 style?: Style | null = null; + // 保存css中的样式 + cssStyle?: Style | null = null; // 父节点 parent: Node | null = null; children: Node[]; @@ -16,6 +19,7 @@ export class Node { } constructor(style: Style, children: Node[] = []) { + this.cssStyle = style.clone(); if (!style.isInit) { style.init() } diff --git a/GaiaXHarmony/GXStretchBinding/GXStretch/src/main/ets/stretch/Style.ts b/GaiaXHarmony/GXStretchBinding/GXStretch/src/main/ets/stretch/Style.ts index 2cff2c651..6bf142401 100644 --- a/GaiaXHarmony/GXStretchBinding/GXStretch/src/main/ets/stretch/Style.ts +++ b/GaiaXHarmony/GXStretchBinding/GXStretch/src/main/ets/stretch/Style.ts @@ -81,6 +81,16 @@ export class Size { this.width = width this.height = height } + + clone(): Size { + let size; + if (this.width instanceof Dim && this.height instanceof Dim) { + size = new Size(this.width.clone(), this.height.clone()) + } else { + size = new Size(this.width, this.height) + } + return size + } } export class Rect { @@ -95,6 +105,17 @@ export class Rect { this.top = top this.bottom = bottom } + + + clone(): Rect { + let size; + if (this.start instanceof Dim && this.end instanceof Dim && this.top instanceof Dim && this.bottom instanceof Dim) { + size = new Rect(this.start.clone(), this.end.clone(), this.top.clone(), this.bottom.clone()) + } else { + size = new Rect(this.start, this.end, this.top, this.bottom) + } + return size + } } export class Dim { @@ -105,6 +126,11 @@ export class Dim { this.type = type this.value = value } + + clone(): Dim { + let dim = new Dim(this.type, this.value) + return dim + } } export class DimPoints extends Dim { @@ -151,8 +177,8 @@ export class Style { margin: Rect = new Rect(Undefined, Undefined, Undefined, Undefined); padding: Rect = new Rect(Undefined, Undefined, Undefined, Undefined); border: Rect = new Rect(Undefined, Undefined, Undefined, Undefined); - flexGrow: number = 0.0; - flexShrink: number = 1.0; + flexGrow: number = 0; + flexShrink: number = 0; flexBasis: Dim = Auto; size: Size = new Size(Auto, Auto); minSize: Size = new Size(Auto, Auto); @@ -174,8 +200,8 @@ export class Style { margin: Rect = new Rect(Undefined, Undefined, Undefined, Undefined), padding: Rect = new Rect(Undefined, Undefined, Undefined, Undefined), border: Rect = new Rect(Undefined, Undefined, Undefined, Undefined), - flexGrow: number = 0.0, - flexShrink: number = 1.0, + flexGrow: number = 0, + flexShrink: number = 0, flexBasis: Dim = Auto, size: Size = new Size(Auto, Auto), minSize: Size = new Size(Auto, Auto), @@ -293,4 +319,30 @@ export class Style { this.isInit = false; } } + + clone(): Style { + let style = new Style(); + style.display = this.display + style.positionType = this.positionType; + style.direction = this.direction; + style.flexDirection = this.flexDirection; + style.flexWrap = this.flexWrap; + style.overflow = this.overflow; + style.alignItems = this.alignItems; + style.alignSelf = this.alignSelf; + style.alignContent = this.alignContent; + style.justifyContent = this.justifyContent; + style.position = this.position.clone(); + style.margin = this.margin.clone(); + style.padding = this.padding.clone(); + style.border = this.border.clone(); + style.flexGrow = this.flexGrow; + style.flexShrink = this.flexShrink; + style.flexBasis = this.flexBasis; + style.size = this.size.clone(); + style.minSize = this.minSize.clone(); + style.maxSize = this.maxSize.clone(); + style.aspectRatio = this.aspectRatio; + return style; + } } \ No newline at end of file diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/BuildProfile.ets b/GaiaXHarmony/GaiaXCore/GaiaX/BuildProfile.ets index f05d220c1..3a501e5dd 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/BuildProfile.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/BuildProfile.ets @@ -1,6 +1,17 @@ +/** + * Use these variables when you tailor your ArkTS code. They must be of the const type. + */ +export const HAR_VERSION = '1.0.0'; +export const BUILD_MODE_NAME = 'debug'; +export const DEBUG = true; +export const TARGET_NAME = 'default'; + +/** + * BuildProfile Class is used only for compatibility purposes. + */ export default class BuildProfile { - static readonly HAR_VERSION = '1.0.0'; - static readonly BUILD_MODE_NAME = 'debug'; - static readonly DEBUG = true; - static readonly TARGET_NAME = 'default'; + static readonly HAR_VERSION = HAR_VERSION; + static readonly BUILD_MODE_NAME = BUILD_MODE_NAME; + static readonly DEBUG = DEBUG; + static readonly TARGET_NAME = TARGET_NAME; } \ No newline at end of file diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/GXTemplateEngine.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/GXTemplateEngine.ets index fc8ea5789..74a93c730 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/GXTemplateEngine.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/GXTemplateEngine.ets @@ -19,7 +19,7 @@ import GXRawFileTemplateSource from './source/GXRawFileTemplateSource'; import GXRegisterCenter from './GXRegisterCenter'; import { configAnalyzeIfNeeded } from 'GaiaXAnalyze'; import { Stretch } from 'gxstretch'; -import { GXInjector,GXImageBuilderParams } from './components/injector/GXInjector'; +import { GXInjector, GXInjectorBuilderParams } from './components/injector/GXInjector'; export default class GXTemplateEngine { static instance = new GXTemplateEngine(); @@ -38,9 +38,13 @@ export default class GXTemplateEngine { } } - registerImage(imageBuilder:WrappedBuilder<[GXImageBuilderParams]>) { + registerImage(imageBuilder: WrappedBuilder<[GXInjectorBuilderParams]>) { GXInjector.instance.registerImage(imageBuilder) } + + registerCustomView(customViewBuilder: WrappedBuilder<[GXInjectorBuilderParams]>) { + GXInjector.instance.registerCustomView(customViewBuilder) + } } diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/binding/GXRenderManager.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/binding/GXRenderManager.ets index 1cfad3d94..f7dd908a1 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/binding/GXRenderManager.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/binding/GXRenderManager.ets @@ -105,10 +105,10 @@ export default class GXRenderManager { * 计算和绑定布局 * @param node */ - static computeAndApplyLayout(node: GXNode) { + static computeAndApplyLayout(node: GXNode, isSpecialUpdate: boolean = false) { const layout = GXRenderManager.computeLayout(node); if (layout != null) { - node.applyLayout(layout); + node.applyLayout(layout, isSpecialUpdate); } } diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXCustomView.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXCustomView.ets new file mode 100644 index 000000000..135004d4d --- /dev/null +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXCustomView.ets @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2021, Alibaba Group Holding Limited; + * + * 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. + */ + +import GXTemplateContext from '../context/GXTemplateContext' +import { GXCustomViewBuilderParams, GXInjector } from './injector/GXInjector' +import GXCustomViewNode from '../node/GXCustomViewNode' +import { Node } from 'gxstretch'; + +@Component +export struct GXCustomView { + // 节点属性 + @Prop node: GXCustomViewNode + @Prop gxContext: GXTemplateContext; + + // 创建自定义组件的新实例后,执行其build()函数之前调用 + aboutToAppear(): void { + + } + + // 析构之前调用,用于free节点和rust指针 + aboutToDisappear(): void { + + } + + build() { + // buildCustomView(this.node); + if (GXInjector.instance.injectedCustomView() != null) { + Row() { + GXInjector.instance.injectedCustomView()?.builder(new GXCustomViewBuilderParams(this.node.data, this.node.className)) + } + .id(this.node.nodeId) + .position({ x: this.node.x, y: this.node.y }) + .size({ width: this.node.width, height: this.node.height }) + .opacity(this.node.opacity) + .shadow(this.node.boxShadow) + .borderWidth(this.node.borderWidth) + .borderColor(this.node.borderColor) + .borderRadius(this.node.borderRadius) + .backgroundColor(this.node.backgroundColor) + .backgroundImage(this.node.backgroundImage) + .linearGradient({ + direction: this.node.linearGradientDirection, + colors: this.node.linearGradientColors + }) + .onClick(() => { // 执行事件 + this.node.handleEvent(this.node.clickEvent); + }) + .onTouchIntercept(() => { // 调用onTouchIntercept修改该组件的HitTestMode属性 + return this.node.getHitTestMode(); + }) + } else { + Text(this.node.className) + .fontSize("12fp") + .fontColor("red") + .id(this.node.nodeId) + .position({ x: this.node.x, y: this.node.y }) + .size({ width: this.node.width, height: this.node.height }) + .opacity(this.node.opacity) + .shadow(this.node.boxShadow) + .borderWidth(this.node.borderWidth) + .borderColor(this.node.borderColor) + .borderRadius(this.node.borderRadius) + .backgroundColor(this.node.backgroundColor) + .backgroundImage(this.node.backgroundImage) + .linearGradient({ + direction: this.node.linearGradientDirection, + colors: this.node.linearGradientColors + }) + .clip(this.node.clip)// 子视图超出父视图进行裁剪 + // .backdropBlur(3) // 毛玻璃效果 + // .clip(new Circle({ width: '280px', height: '280px' })) + // .mask(new Circle({ width: '280px', height: '280px' }).fill(Color.Gray)) + .onClick(() => { // 执行事件 + this.node.handleEvent(this.node.clickEvent); + }) + .onTouchIntercept(() => { // 调用onTouchIntercept修改该组件的HitTestMode属性 + return this.node.getHitTestMode(); + }) + .onAppear(() => { + }) + } + } +} + + +@Builder +export function buildCustomView(node: GXCustomViewNode) { + if (GXInjector.instance.injectedCustomView() != null) { + Row() { + GXInjector.instance.injectedCustomView()?.builder(new GXCustomViewBuilderParams(node.data, node.className)) + } + .id(node.nodeId) + .position({ x: node.x, y: node.y }) + .size({ width: node.width, height: node.height }) + .opacity(node.opacity) + .shadow(node.boxShadow) + .borderWidth(node.borderWidth) + .borderColor(node.borderColor) + .borderRadius(node.borderRadius) + .backgroundColor(node.backgroundColor) + .backgroundImage(node.backgroundImage) + .linearGradient({ + direction: node.linearGradientDirection, + colors: node.linearGradientColors + }) + .onClick(() => { // 执行事件 + node.handleEvent(node.clickEvent); + }) + .onTouchIntercept(() => { // 调用onTouchIntercept修改该组件的HitTestMode属性 + return node.getHitTestMode(); + }) + } else { + Text(node.className) + .fontSize("12fp") + .fontColor("red") + .id(node.nodeId) + .position({ x: node.x, y: node.y }) + .size({ width: node.width, height: node.height }) + .opacity(node.opacity) + .shadow(node.boxShadow) + .borderWidth(node.borderWidth) + .borderColor(node.borderColor) + .borderRadius(node.borderRadius) + .backgroundColor(node.backgroundColor) + .backgroundImage(node.backgroundImage) + .linearGradient({ + direction: node.linearGradientDirection, + colors: node.linearGradientColors + }) + .clip(node.clip)// 子视图超出父视图进行裁剪 + // .backdropBlur(3) // 毛玻璃效果 + // .clip(new Circle({ width: '280px', height: '280px' })) + // .mask(new Circle({ width: '280px', height: '280px' }).fill(Color.Gray)) + .onClick(() => { // 执行事件 + node.handleEvent(node.clickEvent); + }) + .onTouchIntercept(() => { // 调用onTouchIntercept修改该组件的HitTestMode属性 + return node.getHitTestMode(); + }) + .onAppear(() => { + }) + } +} \ No newline at end of file diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXGridView.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXGridView.ets index fbe1e0006..5aec93eca 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXGridView.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXGridView.ets @@ -96,7 +96,6 @@ export default struct GXGridView { .padding(this.node.contentInset) // .enableScrollInteraction(false) // scrollEnable .onScrollIndex((first: number) => { - console.info(first.toString()) }) } } diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXImage.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXImage.ets index dca5a20b3..9b7f0c6be 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXImage.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXImage.ets @@ -114,6 +114,11 @@ export function buildImage(node: GXImageNode) { .borderColor(node.borderColor) .borderRadius(node.borderRadius) .backgroundColor(node.backgroundColor) + .backgroundImage(node.backgroundImage) + .linearGradient({ + direction: node.linearGradientDirection, + colors: node.linearGradientColors + }) .onClick(() => { // 执行事件 console.log("Column click"); node.handleEvent(node.clickEvent); @@ -134,14 +139,16 @@ export function buildImage(node: GXImageNode) { .borderColor(node.borderColor) .borderRadius(node.borderRadius) .backgroundColor(node.backgroundColor) + .backgroundImage(node.backgroundImage) + .linearGradient({ + direction: node.linearGradientDirection, + colors: node.linearGradientColors + }) .onComplete(() => { - console.log('load image complete'); }) .onError(() => { - console.log('load image fail'); }) .onClick(() => { // 执行事件 - console.log("Column click"); node.handleEvent(node.clickEvent); }) .onTouchIntercept(() => { // 调用onTouchIntercept修改该组件的HitTestMode属性 diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXRichText.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXRichText.ets index bf6f2145e..4ec411675 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXRichText.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXRichText.ets @@ -74,7 +74,6 @@ export function buildRichText(node: GXRichTextNode){ .width(node.width) .height(node.height) .onClick(() => { // 执行事件 - console.log("Column click"); node.handleEvent(node.clickEvent); }) .onTouchIntercept(() => { // 调用onTouchIntercept修改该组件的HitTestMode属性 diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXRootView.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXRootView.ets index bd2b90445..2e29fd8ea 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXRootView.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXRootView.ets @@ -77,8 +77,11 @@ export function buildRootView(node: GXRootNode, context: GXTemplateContext) { .borderColor(node.borderColor) .backgroundColor(node.backgroundColor) .backgroundImage(node.backgroundImage) + .linearGradient({ + direction: node.linearGradientDirection, + colors: node.linearGradientColors + }) .onClick(() => { // 执行事件 - console.log("Column click"); node.handleEvent(node.clickEvent); }) .onTouchIntercept(() => { // 调用onTouchIntercept修改该组件的HitTestMode属性 diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXScrollView.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXScrollView.ets index 47df49226..d99200475 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXScrollView.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXScrollView.ets @@ -116,12 +116,10 @@ export default struct GXScrollView { .edgeEffect(EdgeEffect.Spring) // 滑动到边缘无效果 .cachedCount(3) //cachedCount表示屏幕外List/Grid预加载item的个数 .onDidScroll((xOffset: number, yOffset: number) => { - console.info(xOffset + ' ' + yOffset) }) .alignListItem(this.node.gravity) .scrollBar(BarState.Off) .onScrollStop(() => { - console.info('Scroll Stop') }) } } diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXText.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXText.ets index e3f416b6e..21405e25a 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXText.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXText.ets @@ -84,6 +84,10 @@ export function buildText(node: GXTextNode) { .borderRadius(node.borderRadius) .backgroundColor(node.backgroundColor) .backgroundImage(node.backgroundImage) + .linearGradient({ + direction: node.linearGradientDirection, + colors: node.linearGradientColors + }) .textOverflow({ overflow: node.textOverflow }) .decoration({ type: node.textDecoration, diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXView.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXView.ets index 7d84d2d52..c92e4076f 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXView.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXView.ets @@ -90,12 +90,15 @@ export function buildView(node: GXViewNode, context: GXTemplateContext) { .borderRadius(node.borderRadius) .backgroundColor(node.backgroundColor) .backgroundImage(node.backgroundImage) + .linearGradient({ + direction: node.linearGradientDirection, + colors: node.linearGradientColors + }) .clip(node.clip) // 子视图超出父视图进行裁剪 // .backdropBlur(3) // 毛玻璃效果 // .clip(new Circle({ width: '280px', height: '280px' })) // .mask(new Circle({ width: '280px', height: '280px' }).fill(Color.Gray)) .onClick(() => { // 执行事件 - console.log("Column click"); node.handleEvent(node.clickEvent); }) .onTouchIntercept(() => { // 调用onTouchIntercept修改该组件的HitTestMode属性 diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXViewBuilder.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXViewBuilder.ets index d40b0ebb1..27f61523a 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXViewBuilder.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/GXViewBuilder.ets @@ -33,6 +33,9 @@ import GXSliderView from './GXSilderView' import GXGridView from './GXGridView' import GXTemplateContext from '../context/GXTemplateContext' import { GXNodeType } from '../common/GXCommon' +import { buildCustomView } from './GXCustomView' +import GXCustomViewNode from '../node/GXCustomViewNode' +import { Display } from 'gxstretch' @Builder export function buildGaiaXView(gxContext: GXTemplateContext, node: GXNode) { @@ -63,7 +66,10 @@ export function createTemplateViewByNode(gxContext: GXTemplateContext, node: GXN @Builder export function createSubViewByNode(gxContext: GXTemplateContext, node: GXNode) { - if (node.isTemplateType) { + if (node.style?.display != Display.Flex) { + // 不可见节点,直接终止递归 + Column() + } else if (node.isTemplateType) { // template createTemplateViewByNode(gxContext, node) @@ -87,7 +93,10 @@ export function createSubViewByNode(gxContext: GXTemplateContext, node: GXNode) // GXRichText({ node: (node as GXRichTextNode) }) buildRichText((node as GXRichTextNode)) - } else { + } else if (node.nodeType() == GXNodeType.custom) { + // 用户自定义view + buildCustomView((node as GXCustomViewNode)) + } else { // default - view // GXView({ gxContext: gxContext, node: (node as GXViewNode) }) buildView((node as GXViewNode), gxContext) diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/injector/GXInjector.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/injector/GXInjector.ets index 097cdcd5a..f0e7d0782 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/injector/GXInjector.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/components/injector/GXInjector.ets @@ -15,9 +15,12 @@ */ enum InjectType { - image = 0 + image = 0, + customView = 1 } +export declare type GXInjectorBuilderParams = GXImageBuilderParams | GXCustomViewBuilderParams; + export class GXImageBuilderParams { url: ResourceStr | null = null viewWidth: number | null = null @@ -42,28 +45,58 @@ export class GXImageBuilderParams { } } +export class GXCustomViewBuilderParams { + data: ESObject | null = null; + className: string = ""; + + constructor(data: ESObject | null, className: string) { + this.data = data; + this.className = className; + } +} + export class GXInjector { static instance = new GXInjector(); // 注册图片组件Map - injectorMap: Record> = {}; + injectorMap: Record> = {}; /** * 注册Image * @param builder */ - registerImage( builder: WrappedBuilder<[GXImageBuilderParams]>) { + registerImage( builder: WrappedBuilder<[GXInjectorBuilderParams]>) { this.injectorMap[InjectType.image] = builder } + /** + * 注册自定义view + * @param builder + */ + registerCustomView(builder: WrappedBuilder<[GXInjectorBuilderParams]>) { + this.injectorMap[InjectType.customView] = builder + } + + /** * 获取注册的Image * @returns Image */ - injectedImage():WrappedBuilder<[GXImageBuilderParams]> | null { + injectedImage(): WrappedBuilder<[GXImageBuilderParams]> | null { if (this.injectorMap[InjectType.image]) { return this.injectorMap[InjectType.image] } else { return null } } + + /** + * 获取注册的 自定义View + */ + injectedCustomView(): WrappedBuilder<[GXCustomViewBuilderParams]> | null { + if (this.injectorMap[InjectType.customView]) { + return this.injectorMap[InjectType.customView] + } else { + return null + } + } } diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/creator/GXNode.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/creator/GXNode.ets index 4231a7f0d..ccf40cab5 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/creator/GXNode.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/creator/GXNode.ets @@ -37,6 +37,7 @@ export default class GXNode extends Node { y: number = 0; width: number = 0; height: number = 0; + limitWidth: number = -1; // 埋点响应 trackEvent?: GXTrack; @@ -71,19 +72,22 @@ export default class GXNode extends Node { } // 递归绑定布局 - applyLayout(layout: Layout) { + applyLayout(layout: Layout, isSpecialUpdate: boolean) { // 当前节点布局,暂时不做节点拍平逻辑 this.x = layout.x; this.y = layout.y; + if (isSpecialUpdate) { + this.limitWidth = layout.width; + } this.width = layout.width; this.height = layout.height; // 递归子节点 for (let index = 0; index < this.children.length; index++) { const childNode: GXNode = this.children[index] as GXNode; // let childLayout: Layout = layout.children[index]; - let childLayout: Layout = (index < layout.children.length) ? layout.children[index] : null; + let childLayout: Layout | null = (index < layout.children.length) ? layout.children[index] : null; if (childNode && childLayout != null) { - childNode.applyLayout(childLayout); + childNode.applyLayout(childLayout, isSpecialUpdate); } } } diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/creator/GXNodeHelper.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/creator/GXNodeHelper.ets index f30f24847..5f3c27766 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/creator/GXNodeHelper.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/creator/GXNodeHelper.ets @@ -26,6 +26,7 @@ import GXTextNode from '../node/GXTextNode'; import GXViewNode from '../node/GXViewNode'; import { Style } from 'gxstretch'; import GXNode from './GXNode'; +import GXCustomViewNode from '../node/GXCustomViewNode'; export default class GXNodeHelper { /** @@ -162,6 +163,9 @@ export default class GXNodeHelper { case GXNodeType.richtext: node = new GXRichTextNode(style); break; + case GXNodeType.custom: + node = new GXCustomViewNode(style); + break; default: node = new GXViewNode(style); break; diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/creator/GXNodeTreeCreator.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/creator/GXNodeTreeCreator.ets index fb4c40311..76aeab184 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/creator/GXNodeTreeCreator.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/creator/GXNodeTreeCreator.ets @@ -18,11 +18,12 @@ import GXTemplateManager from '../template/GXTemplateManager'; import GXTemplateContext from '../context/GXTemplateContext'; import GXTemplateInfo from '../template/GXTemplateInfo'; import GXTemplateItem from '../context/GXTemplateItem'; -import { GXRecord } from '../common/GXCommon'; +import { GXNodeType, GXRecord } from '../common/GXCommon'; import GXStyleHelper from './GXStyleHelper'; import GXNodeHelper from './GXNodeHelper'; import { JSON } from '@kit.ArkTS'; import GXNode from './GXNode'; +import GXCustomViewNode from '../node/GXCustomViewNode'; export default class GXNodeTreeCreator { /** @@ -187,6 +188,11 @@ export default class GXNodeTreeCreator { if (animation) { node.extension.animation = animation[nodeId] as GXRecord; } + // 自定义 View 注入className + // 使用iOS一样的key,防止被gxStudio导出时抹除 + if (nodeType == GXNodeType.custom) { + (node as GXCustomViewNode).className = layer['view-class-ios'] as string; + } // 其他属性 node.extension.templateContext = templateContext; diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/creator/GXStyleHelper.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/creator/GXStyleHelper.ets index 16f5a3cbe..7e4fb59e2 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/creator/GXStyleHelper.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/creator/GXStyleHelper.ets @@ -59,23 +59,35 @@ export default class GXStyleHelper { let flag = false; // margin - const marginTop = styleInfo['margin-top'] as string; - const marginLeft = styleInfo['margin-left'] as string; - const marginBottom = styleInfo['margin-bottom'] as string; - const marginRight = styleInfo['margin-right'] as string; - if (marginTop != null || marginLeft != null || marginBottom != null || marginRight != null) { + const margin = styleInfo['margin'] as string; + if (margin) { flag = true; - GXStyleHelper.convertMargin(style, marginTop, marginLeft, marginBottom, marginRight); + GXStyleHelper.convertPadding(style, margin, margin, margin, margin); + } else { + const marginTop = styleInfo['margin-top'] as string; + const marginLeft = styleInfo['margin-left'] as string; + const marginBottom = styleInfo['margin-bottom'] as string; + const marginRight = styleInfo['margin-right'] as string; + if (marginTop != null || marginLeft != null || marginBottom != null || marginRight != null) { + flag = true; + GXStyleHelper.convertMargin(style, marginTop, marginLeft, marginBottom, marginRight); + } } // padding - const paddingTop = styleInfo['padding-top'] as string; - const paddingLeft = styleInfo['padding-left'] as string; - const paddingBottom = styleInfo['padding-bottom'] as string; - const paddingRight = styleInfo['padding-right'] as string; - if (paddingTop != null || paddingLeft != null || paddingBottom != null || paddingRight != null) { + const padding = styleInfo['padding'] as string; + if (padding) { flag = true; - GXStyleHelper.convertPadding(style, paddingTop, paddingLeft, paddingBottom, paddingRight); + GXStyleHelper.convertPadding(style, padding, padding, padding, padding); + } else { + const paddingTop = styleInfo['padding-top'] as string; + const paddingLeft = styleInfo['padding-left'] as string; + const paddingBottom = styleInfo['padding-bottom'] as string; + const paddingRight = styleInfo['padding-right'] as string; + if (paddingTop != null || paddingLeft != null || paddingBottom != null || paddingRight != null) { + flag = true; + GXStyleHelper.convertPadding(style, paddingTop, paddingLeft, paddingBottom, paddingRight); + } } // width | height @@ -509,7 +521,7 @@ export default class GXStyleHelper { const tmpValue = value.substring(0, value.length - 2); const number = GXUtils.convertToNumber(tmpValue) ?? 0; return number; - } else if (value.endsWith('px')) { + } else if (value.endsWith('pt')) { // 计算 pt = px * ratio const ratio = 1.0; const tmpValue = value.substring(0, value.length - 2); @@ -579,4 +591,48 @@ export default class GXStyleHelper { return isAuto ? new DimAuto() : new DimPoints(0); } } + + /** + * 将系统 graphics 使用的weight枚举,替换为ts层weight + * 0:100 + * 1:200 + * 2:300 + * ... + * @param value + * @returns + */ + static convertSystemWeight(value: number | string): number { + if (value === 0 || value === "0") { + return 100; + } + if (value === 1 || value === "1") { + return 200; + } + if (value === 2 || value === "2") { + return 300; + } + if (value === 3 || value === "3") { + return 400; + } + if (value === 4 || value === "4") { + return 500; + } + if (value === 5 || value === "5") { + return 600; + } + if (value === 6 || value === "6") { + return 700; + } + if (value === 7 || value === "7") { + return 800; + } + if (value === 8 || value === "8") { + return 900; + } + if (typeof value === "number") { + return value; + } else { + return Number(value).valueOf(); + } + } } \ No newline at end of file diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/node/GXBaseNode.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/node/GXBaseNode.ets index dcd919876..8ee24113f 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/node/GXBaseNode.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/node/GXBaseNode.ets @@ -17,6 +17,7 @@ import GXStyleHelper from '../creator/GXStyleHelper'; import { GXRecord } from '../common/GXCommon'; import GXNode from '../creator/GXNode'; +import GXUtils from '../utils/GXUtils'; export default class GXBaseNode extends GXNode { // 透明度 @@ -29,14 +30,17 @@ export default class GXBaseNode extends GXNode { // 背景色,默认透明 backgroundColor = 'Transparent'; // 圆角,默认无圆角 - borderRadius: string | null = null; + borderRadius: Length | BorderRadiuses | null = null; // 毛玻璃效果 backdropFilter: string | null = null; // 渐变 backgroundImage: string | null = null; // 阴影 boxShadow: ShadowOptions | null = null; - + // linearGradient 背景参数 + linearGradientDirection: GradientDirection | null = null; + // linearGradient 背景参数 + linearGradientColors: Array<[ResourceColor, number]> | null = null; // 更新布局 & 样式信息 handleExtend(dataInfo: GXRecord) { @@ -109,19 +113,27 @@ export default class GXBaseNode extends GXNode { // border-color const borderColor = styleInfo['border-color'] as string; if (borderColor) { - this.borderColor = borderColor; + this.borderColor = GXUtils.parseColor(borderColor) ?? "Transparent"; } // background-color const backgroundColor = styleInfo['background-color'] as string; if (backgroundColor != null) { - this.backgroundColor = backgroundColor; + this.backgroundColor = GXUtils.parseColor(backgroundColor) ?? "Transparent"; } // background-image const backgroundImage = styleInfo['background-image'] as string; - if (backgroundImage != null) { - this.backgroundImage = backgroundImage; + if (!!backgroundImage && backgroundImage != null) { + if (backgroundImage.startsWith("linear")) { + let colors = this.getLinearGradient(backgroundImage); + this.linearGradientDirection = this.getDirection(colors); + this.linearGradientColors = this.getColors(colors); + } else { + this.backgroundImage = GXUtils.parseColor(backgroundImage) ?? "Transparent"; + // backgroundImage 不支持颜色设置。也覆盖到 backgroundColor 上 + this.backgroundColor = this.backgroundImage; + } } // background-filter @@ -151,14 +163,19 @@ export default class GXBaseNode extends GXNode { const tr = GXStyleHelper.convertSimpleValue(topRight, 0); const br = GXStyleHelper.convertSimpleValue(bottomRight, 0); const bl = GXStyleHelper.convertSimpleValue(bottomLeft, 0); - this.borderRadius = `${tf} ${tr} ${br} ${bl}` + this.borderRadius = { + topLeft: tf, + topRight: tr, + bottomLeft: bl, + bottomRight: br + } } // border-radius if (isFullBorderRadius) { const borderRadius = styleInfo['border-radius'] as string; if (borderRadius != null) { let value = GXStyleHelper.convertSimpleValue(borderRadius, 0); - this.borderRadius = `${value}}`; + this.borderRadius = `${value}`; } } } @@ -178,4 +195,95 @@ export default class GXBaseNode extends GXNode { } } } + + private getDirection(colors: Array): GradientDirection { + if (colors.length > 0) { + const linear = colors[0]; + switch (linear) { + case "to right": + case "toright": + return GradientDirection.Right; + case "to left": + case "toleft": + return GradientDirection.Left; + case "to top": + case "totop": + return GradientDirection.Top; + case "to bottom": + case "tobottom": + return GradientDirection.Bottom; + case "to top left": + case "totopleft": + return GradientDirection.LeftTop; + case "to top right": + case "totopright": + return GradientDirection.RightTop; + case "to bottom left": + case "tobottomleft": + return GradientDirection.LeftBottom; + case "to bottom right": + case "tobottomright": + return GradientDirection.RightBottom; + } + } + return GradientDirection.Bottom + } + + private getColors(colors: Array): Array<[ResourceColor, number]> | null { + let result: Array<[ResourceColor, number]> = []; + colors.forEach((color, index) => { + if (!color.startsWith("to") && !color.includes("deg")) { + result.push([GXUtils.parseColor(color) ?? "Transparent", index / (colors.length - 1)]); + } + }) + + return result; + } + + /** + * 解析 + * linear-gradient(to top, #FE2C5500, #FE2C5557, #FE2C5557, #FE2C5500) + * linear-gradient(to top, rgba(xx,xx,xx,xx), rgba(xx,xx,xx,xx), rgba(xx,xx,xx,xx), rgba(xx,xx,xx,xx)) + * 将括号内数据变为数组返回,数组首个元素魏颜色渐变方向,后续内容为颜色色值。 + */ + private getLinearGradient(linear: string): Array { + let result = Array() + try { + let substring = linear.substring(linear.indexOf("(") + 1, linear.lastIndexOf(")")) + if (substring.includes("rgba")) { + // linear-gradient(to top, rgba(xx,xx,xx,xx), rgba(xx,xx,xx,xx), rgba(xx,xx,xx,xx), rgba(xx,xx,xx,xx)) + substring.split("rgba").forEach((item, index) => { + let content = item.trim() + if (content.endsWith(",")) { + content = content.substring(0, content.length - 1) + } + if (index > 0) { + result.push("rgba" + content) + } else { + result.push(content) + } + }) + } else if (substring.includes("rgb")) { + // linear-gradient(to top, rgb(xx,xx,xx), rgb(xx,xx,xx), rgb(xx,xx,xx), rgb(xx,xx,xx)) + substring.split("rgb").forEach((item, index) => { + let content = item.trim() + if (content.endsWith(",")) { + content = content.substring(0, content.length - 1) + } + if (index > 0) { + result.push("rgb" + content) + } else { + result.push(content) + } + }) + } else { + // linear-gradient(to top, #FE2C5500, #FE2C5557, #FE2C5557, #FE2C5500) + substring.split(",").forEach((item) => { + result.push(item.trim()) + }) + } + } catch (error) { + } + return result + } } diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/node/GXCustomViewNode.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/node/GXCustomViewNode.ets new file mode 100644 index 000000000..1e35e55f5 --- /dev/null +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/node/GXCustomViewNode.ets @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021, Alibaba Group Holding Limited; + * + * 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. + */ + +import { GXNodeType, GXRecord } from '../common/GXCommon'; +import GXBaseNode from './GXBaseNode'; + +export default class GXCustomViewNode extends GXBaseNode { + data?: ESObject = null + className: string = "" + // 节点类型 + nodeType(): GXNodeType { + return GXNodeType.custom; + } + + // 处理数据 + setData(dataInfo: GXRecord): void { + // 处理extend + this.handleExtend(dataInfo); + this.data = dataInfo['value']; + } +} \ No newline at end of file diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/node/GXImageNode.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/node/GXImageNode.ets index 0ebf01571..75c93f48e 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/node/GXImageNode.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/node/GXImageNode.ets @@ -62,12 +62,51 @@ export default class GXImageNode extends GXViewNode { // 获取mode getImageMode(mode: string): void { // 图片的模式 - if (mode == 'cover') { - this.fitMode = ImageFit.Cover; - } else if (mode == 'contain') { - this.fitMode = ImageFit.Contain; - } else { - this.fitMode = ImageFit.Fill; + switch (mode) { + case 'cover': + this.fitMode = ImageFit.Cover; + break; + case 'contain': + this.fitMode = ImageFit.Contain; + break; + case 'scaleToFill': + this.fitMode = ImageFit.Fill; + break; + case 'aspectFit': + this.fitMode = ImageFit.Contain; + break; + case 'aspectFill': + this.fitMode = ImageFit.Cover; + break; + case 'left': + this.fitMode = ImageFit.START; + break; + case 'right': + this.fitMode = ImageFit.END; + break; + case 'center': + this.fitMode = ImageFit.CENTER; + break; + case 'top': + this.fitMode = ImageFit.TOP; + break; + case 'bottom': + this.fitMode = ImageFit.BOTTOM; + break; + case 'top left': + this.fitMode = ImageFit.TOP_START; + break; + case 'top right': + this.fitMode = ImageFit.TOP_END; + break; + case 'bottom left': + this.fitMode = ImageFit.BOTTOM_START; + break; + case 'bottom right': + this.fitMode = ImageFit.BOTTOM_END; + break; + default: + this.fitMode = ImageFit.Fill; } } } \ No newline at end of file diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/node/GXTextNode.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/node/GXTextNode.ets index b03a54c2f..247634964 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/node/GXTextNode.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/node/GXTextNode.ets @@ -20,7 +20,7 @@ import GXStyleHelper from '../creator/GXStyleHelper'; import GXContext from '../utils/GXContext'; import GXUtils from '../utils/GXUtils'; import GXViewNode from './GXViewNode'; -import { Display } from 'gxstretch'; +import { DimPoints, Display } from 'gxstretch'; export default class GXTextNode extends GXViewNode { text: string | null = null; @@ -117,7 +117,7 @@ export default class GXTextNode extends GXViewNode { if (style != null) { // fit-content && 处于展示中 if (style.fitContent && style.display == Display.Flex) { - const limitWidth = this.width; + const limitWidth = this.limitWidth; const result = this.measureTextSize(limitWidth); let size = style.size; // 宽 @@ -181,7 +181,7 @@ export default class GXTextNode extends GXViewNode { // color const color = styleInfo['color'] as string; if (color != null) { - this.textColor = color; + this.textColor = GXUtils.parseColor(color) ?? 'black'; } // 对齐方式 @@ -301,8 +301,8 @@ export default class GXTextNode extends GXViewNode { // 处理限制条件 const scale = GXContext.shared().screenScale ?? 1.0; - const height = tmpHeight / scale; - let width = tmpWidth / scale; + const height = Math.ceil(tmpHeight / scale); + let width = Math.ceil(tmpWidth / scale); // 最大宽度限制 if (this.maxLines == 1 && limitWidth != 0) { width = Math.min(width, limitWidth); diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/template/GXTemplateLRUCache.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/template/GXTemplateLRUCache.ets index 16e9266af..736ce727f 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/template/GXTemplateLRUCache.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/template/GXTemplateLRUCache.ets @@ -43,7 +43,7 @@ export default class GXTemplateLRUCache { if (value != undefined) { this.cache.delete(key); // 删除键,因为Map会保留键的插入顺序 this.cache.set(key, value); // 重新设置键,将其移动到最近使用的位置 - return null + return value } return null } diff --git a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/utils/GXUtils.ets b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/utils/GXUtils.ets index 736db3d9c..032a23eda 100644 --- a/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/utils/GXUtils.ets +++ b/GaiaXHarmony/GaiaXCore/GaiaX/src/main/ets/utils/GXUtils.ets @@ -142,4 +142,54 @@ export default class GXUtils { const numLittleEndian = view.getUint16(0, true); // true 表示小端序 return numLittleEndian } + + static parseColor(color: string): string | null { + let res: string | null = color; + if (!!color && color.includes("%")) { + let list = color.split(" ") + if (list.length == 2) { + color = list[0] + } + } + res = GXUtils.parseHexColor(color); + if (res != null) { + return res; + } + res = GXUtils.parseRGBAColor(color); + if (res != null) { + return res; + } + return res; + } + + /** + * rgb(xx, xx, xx, xx) + * rgba(22, 24, 35, 0.5) 格式颜色处理 + * @param color + * @returns + */ + private static parseRGBAColor(color: string): string | null { + if (!!color && color.startsWith("rgb")) { + return color; + } + return null; + } + + /** + * #xxxxxxxx 颜色处理 + * argb 转 rgba + * @param color + * @returns + */ + private static parseHexColor(color: string): string | null { + if (!!color && color.startsWith("#")) { + if (color.length == 9) { + let argb = `#${color.substring(7, color.length)}${color.substring(1, color.length - 2)}`; + return argb; + } else { + return color; + } + } + return null; + } } \ No newline at end of file diff --git a/GaiaXHarmony/GaiaXCore/entry/src/main/resources/rawfile/template/template_scroll_multi_type_item_0/index.css b/GaiaXHarmony/GaiaXCore/entry/src/main/resources/rawfile/template/template_scroll_multi_type_item_0/index.css index b5e5a53df..ab9f5855d 100644 --- a/GaiaXHarmony/GaiaXCore/entry/src/main/resources/rawfile/template/template_scroll_multi_type_item_0/index.css +++ b/GaiaXHarmony/GaiaXCore/entry/src/main/resources/rawfile/template/template_scroll_multi_type_item_0/index.css @@ -1,5 +1,5 @@ #template_scroll_multi_type_item_0 { width: 100px; height: 100px; - background-color: #0000ff; + background-image: linear-gradient(to left, #0000ff, #00ffff); }