diff --git a/frameworks/bridge/arkts_frontend/koala_projects/arkoala-arkts/arkui-ohos/src/component/contentSlot.ets b/frameworks/bridge/arkts_frontend/koala_projects/arkoala-arkts/arkui-ohos/src/component/contentSlot.ets new file mode 100644 index 0000000000000000000000000000000000000000..16c76d2fc0696e04b893357a1d1d471018295bd9 --- /dev/null +++ b/frameworks/bridge/arkts_frontend/koala_projects/arkoala-arkts/arkui-ohos/src/component/contentSlot.ets @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ArkCommonMethodPeer, CommonMethod, ArkCommonMethodComponent, ArkCommonMethodStyle, + TouchEvent } from './common'; +import { TypeChecker, ArkUIGeneratedNativeModule } from "#components" +import { Finalizable, runtimeType, RuntimeType, SerializerBase, registerCallback, wrapCallback, toPeerPtr, KPointer, MaterializedBase, NativeBuffer, nullptr, KInt, KBoolean, KStringPtr } from "@koalaui/interop" +import { int32, int64, float32 } from "@koalaui/common" +import { NodeAttach, remember } from "@koalaui/runtime" +import { PeerNode } from "../PeerNode" +import { ComponentBase } from "./../ComponentBase" +import { Content } from "../Content" +import { NodeContent } from "../NodeContent" +import { ArkUIAniModule } from "arkui.ani" + +export class ArkContentSlotPeer extends PeerNode { + protected constructor(peerPtr: KPointer, id: int32, name: string = "", flags: int32 = 0) { + super(peerPtr, id, name, flags) + } + public static create(component: ComponentBase | undefined, flags: int32 = 0): ArkContentSlotPeer { + const peerId = PeerNode.nextId() + const _peerPtr = ArkUIAniModule._ContentSlot_construct(peerId) + const _peer = new ArkContentSlotPeer(_peerPtr, peerId, "ContentSlot", flags) + component?.setPeer(_peer) + return _peer + } + setContentSlotOptionsAttribute(content?: Content): void { + let content_type: int32 = RuntimeType.UNDEFINED + content_type = runtimeType(content) + if ((RuntimeType.UNDEFINED) != (content_type)) { + const content_value = content as NodeContent + ArkUIAniModule._ContentSlotInterface_setContentSlotOptions(this.peer.ptr, toPeerPtr(content_value)) + } else { + ArkUIAniModule._ContentSlotInterface_setContentSlotOptions(this.peer.ptr, nullptr) + } + } +} + +export class ArkContentSlotStyle extends ArkCommonMethodStyle implements ContentSlotAttribute { + public setContentSlotOptions(content?: Content): this { + return this + } +} + +export class ArkContentSlotComponent extends ComponentBase implements ContentSlotAttribute { + getPeer(): ArkContentSlotPeer { + return (this.peer as ArkContentSlotPeer) + } + + public setContentSlotOptions(content?: Content): this { + if (this.checkPriority("setContentSlotOptions")) { + const content_type = runtimeType(content) + if ((RuntimeType.OBJECT == content_type) || (RuntimeType.UNDEFINED == content_type)) { + const content_casted = content as (Content | undefined) + this.getPeer()?.setContentSlotOptionsAttribute(content_casted) + return this + } + throw new Error("Can not select appropriate overload") + } + return this + } +} +/** @memo */ +export function ContentSlotImpl( + /** @memo */ + style: ((attributes: ContentSlotAttribute) => void) | undefined, + /** @memo */ + content_?: (() => void) | undefined, +): void { + const receiver = remember(() => { + return new ArkContentSlotComponent() + }) + NodeAttach((): ArkContentSlotPeer => ArkContentSlotPeer.create(receiver), (_: ArkContentSlotPeer) => { + style?.(receiver) + content_?.() + }) +} diff --git a/frameworks/bridge/arkts_frontend/koala_projects/arkoala-arkts/arkui-ohos/src/component/interop.ets b/frameworks/bridge/arkts_frontend/koala_projects/arkoala-arkts/arkui-ohos/src/component/interop.ets index dc8f745285f1f5deb1950eec04178f2c96690234..5649301af8b3d88a27c3901a3e16009af49c72ac 100644 --- a/frameworks/bridge/arkts_frontend/koala_projects/arkoala-arkts/arkui-ohos/src/component/interop.ets +++ b/frameworks/bridge/arkts_frontend/koala_projects/arkoala-arkts/arkui-ohos/src/component/interop.ets @@ -13,11 +13,11 @@ * limitations under the License. */ -import { ArkUIAniModule } from "arkui.ani" +import { ArkUIAniModule } from 'arkui.ani' import { KPointer } from '@koalaui/interop'; import { PeerNode, findPeerNode } from '../PeerNode'; import { int32 } from '@koalaui/common'; -import { __context, __id, GlobalStateManager, IncrementalNode, memoEntry, NodeAttach, StateContext } from '@koalaui/runtime'; +import { __context, __id, GlobalStateManager, IncrementalNode, memoEntry, memoEntry1, mutableState, MutableState, NodeAttach, StateContext } from '@koalaui/runtime'; import { ExtendableComponent } from './extendableComponent'; import { StateDecoratedVariable, @@ -32,10 +32,12 @@ import { ObserveSingleton } from '../stateManagement'; import { IDecoratedV1Variable, WatchFuncType, WatchIdType } from '../stateManagement/decorator'; -import { UIContextUtil } from "arkui/base/UIContextUtil"; -import { DetachedRootEntryImpl, UIContextImpl } from "arkui/base/UIContextImpl"; -import { CustomComponent } from "./customComponent"; -import { setNeedCreate } from "../ArkComponentRoot"; +import { UIContextUtil } from 'arkui/base/UIContextUtil'; +import { DetachedRootEntryImpl, UIContextImpl } from 'arkui/base/UIContextImpl'; +import { CustomComponent } from './customComponent'; +import { setNeedCreate } from '../ArkComponentRoot'; +import { StateMgmtTool } from '#stateMgmtTool'; +import { ArkContentSlotPeer } from './contentSlot'; export class CompatiblePeerNode extends PeerNode { protected constructor(peerPtr: KPointer, id: int32, view: ESValue, name: string = '', flags: int32 = 0) { @@ -80,7 +82,7 @@ export function compatibleComponent( bindCompatibleProvideCallback(staticComponent!, dynamicComponent!); bindCompatibleLocalStorageCallback(staticComponent!, dynamicComponent!); } - let resetViewPUFindProvideInterop = global.getProperty("resetViewPUFindProvideInterop"); + let resetViewPUFindProvideInterop = global.getProperty('resetViewPUFindProvideInterop'); resetViewPUFindProvideInterop.invoke(); let resetViewPUInterop = global.getProperty('resetViewPUFindLocalStorageInterop'); resetViewPUInterop.invoke(); @@ -98,7 +100,7 @@ export function compatibleStaticComponent void ): number { - const instantiateImpl = /** @memo */ () => { + const instantiateImpl = /** @memo */ (): void => { T._instantiateImpl(undefined, factory, options, undefined, content); }; @@ -135,13 +137,14 @@ export function compatibleStaticComponent( if (manager === undefined) { manager = GlobalStateManager.instance; } - const node = manager.updatableNode(new IncrementalNode(), (context: StateContext) => { + const node = manager.updatableNode(ArkContentSlotPeer.create(undefined) as PeerNode, (context: StateContext) => { const frozen = manager.frozen; manager.frozen = true; ArkUIAniModule._Common_Sync_InstanceId(uiContext.getInstanceId()); @@ -465,16 +471,95 @@ export function transferCompatibleBuilder( manager.frozen = frozen; }); - const inc = node.value; - const peerNode = findPeerNode(inc); - if (peerNode === undefined) { - node.dispose(); - return 0; - } - uiContext.getDetachedRootEntryManager().detachedRoots_.set(peerNode.peer.ptr, new DetachedRootEntryImpl(node)); - return peerNode.getPeerPtr() as number; + const ptr = node.value.peer.ptr; + uiContext.getDetachedRootEntryManager().detachedRoots_.set(ptr, new DetachedRootEntryImpl(node)); + return ptr as number; } const createDynamicBuilder = ESValue.getGlobal().getProperty('createDynamicBuilder'); const dynamicBuilder = createDynamicBuilder.invoke(ESValue.wrap(staticBuilderFunc)); return dynamicBuilder; +} + + +export function transferCompatibleUpdatableBuilder( + /** @memo */ + builder: (args: T) => void +): ESValue { + const staticBuilderFunc = (args: T): [number, ()=>void] => { + let state: MutableState | undefined; + if (isDynamicObject(args)) { + state = updateDynamicObjectForInterop(args); + } else { + const objType = Type.of(args); + let handler = objType instanceof ClassType && (objType as ClassType).getName().endsWith('@Proxy') + ? (proxy.Proxy.tryGetHandler(args) as InteropBuilderLiteralProxyHandler) // a very slow call so need to judge proxy first + : undefined; + if (handler) { + state = handler!.state; + } + } + const uiContext = UIContextUtil.getOrCreateCurrentUIContext() as UIContextImpl; + let manager = uiContext.stateMgr; + if (manager === undefined) { + manager = GlobalStateManager.instance; + } + const node = manager.updatableNode(ArkContentSlotPeer.create(undefined) as PeerNode, (context: StateContext) => { + const frozen = manager.frozen; + manager.frozen = true; + ArkUIAniModule._Common_Sync_InstanceId(uiContext.getInstanceId()); + let r = OBSERVE.renderingComponent; + OBSERVE.renderingComponent = ObserveSingleton.RenderingComponentV1; + memoEntry1(context, 0, builder, args); + OBSERVE.renderingComponent = r; + ArkUIAniModule._Common_Restore_InstanceId(); + manager.frozen = frozen; + }); + const ptr = node.value.peer.ptr; + uiContext.getDetachedRootEntryManager().detachedRoots_.set(ptr, new DetachedRootEntryImpl(node)); + return [ptr as number, ()=>{ if (state) { state.value++; } }]; + } + const createDynamicUpdatableBuilder = ESValue.getGlobal().getProperty('createDynamicUpdatableBuilder'); + const dynamicBuilder = createDynamicUpdatableBuilder.invoke(ESValue.wrap(staticBuilderFunc)); + return dynamicBuilder; +} + +/** + * Creates a proxy for static object literal in the ArkTS 1.1 context + * to intercept property getter operations. + */ +function makeBuilderParameterStaticProxy(name: string, value: T, sourceGetter: Any): T { + const result = proxy.Proxy.create(value, new InteropBuilderLiteralProxyHandler(sourceGetter)); + return result; +} + +/** + * Empowers the proxy object of a dynamic object with ArkTS 1.2's dependency addition capability + * through hook functions, thereby enabling it to refresh the 1.2 UI. + */ +function updateDynamicObjectForInterop(args: Any): MutableState | undefined { + let state: MutableState = StateMgmtTool.getGlobalStateManager().mutableState(0, true); + const startHookFunc = ESValue.getGlobal().getProperty('startStaticHook'); + const result = startHookFunc.invoke(ESValue.wrap(args), ESValue.wrap(()=>{ state.value })).unwrap(); + if (result === undefined) { + return undefined; + } + return state; +} + +class InteropBuilderLiteralProxyHandler extends proxy.DefaultProxyHandler { + source: ESValue; + state: MutableState; + constructor(source: Any) { + this.source = ESValue.wrap(source) + this.state = StateMgmtTool.getGlobalStateManager().mutableState(0, true); + } + + override get(target: T, name: string): Any { + const propertyGetter: ESValue = this.source.getPropertySafe(name); + if (propertyGetter === ESValue.Undefined) { + return super.get(target, name); + } + this.state.value; + return propertyGetter.invoke().unwrap(); + } } \ No newline at end of file diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/interop/interop_builder.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/interop/interop_builder.ts index 00d95406914a18da01e15188ab3ffef3599a3e23..826b7500489ca058ae11ace7baf877feb901739d 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/interop/interop_builder.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/interop/interop_builder.ts @@ -21,6 +21,14 @@ function __makeBuilderParameterStaticProxy_Interop_Internal(name: string, value: return InteropExtractorModule.makeBuilderParameterStaticProxy(name, value, sourceGetter); } +function startStaticHook(source: Object, addRef: () => void): Object | undefined { + if ('__static_interop_hook' in source) { + source['__static_interop_hook'] = addRef; + return source; + } + return undefined; +} + /** * * @param staticBuilder ArkTS1.2builder, return the pointer of PeerNode diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_builder_proxy.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_builder_proxy.ts index 41b70ad55a7438f69d629124b883a2128510ca2a..6c64ca72c1609b397106f6ad323a0e3d370cbf40 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_builder_proxy.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_builder_proxy.ts @@ -44,6 +44,11 @@ function makeBuilderParameterProxy(builderName: string, source: Object): Object let staticHook: StaticInteropHook | undefined = InteropConfigureStateMgmt.instance.needsInterop() ? new StaticInteropHook() : undefined; return new Proxy(source, { set(target, prop, val) { + //for interop + if (InteropConfigureStateMgmt.instance.needsInterop() && prop === '__static_interop_hook') { + staticHook!.addRef = val; + return true; + } throw Error(`@Builder '${builderName}': Invalid attempt to set(write to) parameter '${prop.toString()}' error!`); }, get(target, prop) { @@ -54,18 +59,6 @@ function makeBuilderParameterProxy(builderName: string, source: Object): Object if (!(typeof target === 'object') && (prop1 in target)) { throw Error(`@Builder '${builderName}': '${prop1}' used but not a function parameter error!`); } - // for interop - if (InteropConfigureStateMgmt.instance.needsInterop()) { - if (prop === '__static_interop_hook') { - return (state, addRef) => { - staticHook!.state = state; - staticHook!.addRef = addRef; - }; - } - if (prop === '__static_interop_state') { - return () => staticHook.state; - } - } const value = target[prop1]; if (typeof value !== 'function') { stateMgmtConsole.debug(` - no fun`); @@ -89,6 +82,12 @@ function makeBuilderParameterProxy(builderName: string, source: Object): Object stateMgmtConsole.debug(` - func - no ObservedPropertybstract - ret value ${funcRet}`); return funcRet; - } // get + }, // get + has(target, prop) { + if (InteropConfigureStateMgmt.instance.needsInterop() && prop === '__static_interop_hook') { + return true; + } + return prop in target; + } }); // new Proxy }