diff --git a/Android/doric/src/main/java/pub/doric/plugin/ShaderPlugin.java b/Android/doric/src/main/java/pub/doric/plugin/ShaderPlugin.java index b237f68c..10adc7e7 100644 --- a/Android/doric/src/main/java/pub/doric/plugin/ShaderPlugin.java +++ b/Android/doric/src/main/java/pub/doric/plugin/ShaderPlugin.java @@ -19,13 +19,22 @@ import pub.doric.DoricContext; import pub.doric.async.AsyncResult; import pub.doric.extension.bridge.DoricMethod; import pub.doric.extension.bridge.DoricPlugin; +import pub.doric.extension.bridge.DoricPromise; +import pub.doric.shader.SuperNode; +import pub.doric.shader.ViewNode; import pub.doric.utils.DoricLog; +import pub.doric.utils.DoricMetaInfo; +import pub.doric.utils.DoricUtils; import pub.doric.utils.ThreadMode; import pub.doric.shader.RootNode; +import com.github.pengfeizhou.jscore.ArchiveException; import com.github.pengfeizhou.jscore.JSDecoder; import com.github.pengfeizhou.jscore.JSObject; +import com.github.pengfeizhou.jscore.JSValue; +import com.github.pengfeizhou.jscore.JavaValue; +import java.lang.reflect.Method; import java.util.concurrent.Callable; /** @@ -71,6 +80,90 @@ public class ShaderPlugin extends DoricJavaPlugin { e.printStackTrace(); DoricLog.e("Shader.render:error%s", e.getLocalizedMessage()); } + } + @DoricMethod + public JavaValue command(JSDecoder jsDecoder, final DoricPromise doricPromise) { + try { + final JSObject jsObject = jsDecoder.decode().asObject(); + final JSValue[] viewIds = jsObject.getProperty("viewIds").asArray().toArray(); + final String name = jsObject.getProperty("name").asString().value(); + final JSValue args = jsObject.getProperty("args"); + ViewNode viewNode = null; + for (JSValue value : viewIds) { + if (viewNode == null) { + viewNode = getDoricContext().getRootNode(); + } else { + if (value.isString() && viewNode instanceof SuperNode) { + String viewId = value.asString().value(); + viewNode = ((SuperNode) viewNode).getSubNodeById(viewId); + } + } + } + if (viewNode == null) { + doricPromise.reject(new JavaValue("Cannot find opposite view")); + } else { + final ViewNode targetViewNode = viewNode; + DoricMetaInfo pluginInfo = getDoricContext().getDriver().getRegistry() + .acquireViewNodeInfo(viewNode.getType()); + final Method method = pluginInfo.getMethod(name); + if (method == null) { + String errMsg = String.format( + "Cannot find plugin method in class:%s,method:%s", + viewNode.getClass(), + name); + doricPromise.reject(new JavaValue(errMsg)); + } else { + Callable callable = new Callable() { + @Override + public JavaValue call() throws Exception { + Class[] classes = method.getParameterTypes(); + Object ret; + if (classes.length == 0) { + ret = method.invoke(targetViewNode); + } else if (classes.length == 1) { + ret = method.invoke(targetViewNode, + createParam(classes[0], doricPromise, args)); + } else { + ret = method.invoke(targetViewNode, + createParam(classes[0], doricPromise, args), + createParam(classes[1], doricPromise, args)); + } + return DoricUtils.toJavaValue(ret); + } + }; + AsyncResult asyncResult = getDoricContext().getDriver() + .asyncCall(callable, ThreadMode.UI); + asyncResult.setCallback(new AsyncResult.Callback() { + @Override + public void onResult(JavaValue result) { + doricPromise.resolve(result); + } + + @Override + public void onError(Throwable t) { + doricPromise.resolve(new JavaValue(t.getLocalizedMessage())); + } + + @Override + public void onFinish() { + + } + }); + } + } + } catch (ArchiveException e) { + e.printStackTrace(); + } + return new JavaValue(true); + } + + + private Object createParam(Class clz, DoricPromise doricPromise, JSValue jsValue) { + if (clz == DoricPromise.class) { + return doricPromise; + } else { + return jsValue; + } } } diff --git a/Android/doric/src/main/java/pub/doric/shader/GroupNode.java b/Android/doric/src/main/java/pub/doric/shader/GroupNode.java index 5d6c9b3a..0220a686 100644 --- a/Android/doric/src/main/java/pub/doric/shader/GroupNode.java +++ b/Android/doric/src/main/java/pub/doric/shader/GroupNode.java @@ -152,4 +152,14 @@ public abstract class GroupNode extends SuperNode { } } } + + @Override + public ViewNode getSubNodeById(String id) { + for (ViewNode node : mChildNodes) { + if (id.equals(node.getId())) { + return node; + } + } + return null; + } } diff --git a/Android/doric/src/main/java/pub/doric/shader/SuperNode.java b/Android/doric/src/main/java/pub/doric/shader/SuperNode.java index 4aece2a7..954b0689 100644 --- a/Android/doric/src/main/java/pub/doric/shader/SuperNode.java +++ b/Android/doric/src/main/java/pub/doric/shader/SuperNode.java @@ -40,6 +40,8 @@ public abstract class SuperNode extends ViewNode { super(doricContext); } + public abstract ViewNode getSubNodeById(String id); + protected ViewGroup.LayoutParams generateDefaultLayoutParams() { return new ViewGroup.LayoutParams(0, 0); } diff --git a/Android/doric/src/main/java/pub/doric/shader/ViewNode.java b/Android/doric/src/main/java/pub/doric/shader/ViewNode.java index 3c26cecf..76e8833e 100644 --- a/Android/doric/src/main/java/pub/doric/shader/ViewNode.java +++ b/Android/doric/src/main/java/pub/doric/shader/ViewNode.java @@ -24,6 +24,7 @@ import android.widget.LinearLayout; import pub.doric.DoricContext; import pub.doric.DoricRegistry; import pub.doric.async.AsyncResult; +import pub.doric.extension.bridge.DoricMethod; import pub.doric.utils.DoricContextHolder; import pub.doric.utils.DoricConstant; import pub.doric.utils.DoricMetaInfo; @@ -243,4 +244,14 @@ public abstract class ViewNode extends DoricContextHolder { mSuperNode.blendSubLayoutConfig(this, layoutConfig); } } + + @DoricMethod + public int getWidth() { + return mView.getWidth(); + } + + @DoricMethod + public int getHeight() { + return mView.getHeight(); + } } diff --git a/Android/doric/src/main/java/pub/doric/shader/list/ListNode.java b/Android/doric/src/main/java/pub/doric/shader/list/ListNode.java index bea96e47..4a8feb43 100644 --- a/Android/doric/src/main/java/pub/doric/shader/list/ListNode.java +++ b/Android/doric/src/main/java/pub/doric/shader/list/ListNode.java @@ -15,6 +15,8 @@ */ package pub.doric.shader.list; +import android.view.View; + import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -91,4 +93,23 @@ public class ListNode extends SuperNode { protected void blendSubLayoutConfig(ViewNode viewNode, JSObject jsObject) { super.blendSubLayoutConfig(viewNode, jsObject); } + + @Override + public ViewNode getSubNodeById(String id) { + RecyclerView.LayoutManager manager = mView.getLayoutManager(); + if (manager == null) { + return null; + } + for (int i = 0; i < manager.getChildCount(); i++) { + View view = manager.getChildAt(i); + if (view == null) { + continue; + } + ListAdapter.DoricViewHolder viewHolder = (ListAdapter.DoricViewHolder) mView.getChildViewHolder(view); + if (id.equals(viewHolder.listItemNode.getId())) { + return viewHolder.listItemNode; + } + } + return null; + } } diff --git a/demo/src/ListDemo.ts b/demo/src/ListDemo.ts index 77a692e6..c8451f99 100644 --- a/demo/src/ListDemo.ts +++ b/demo/src/ListDemo.ts @@ -73,6 +73,13 @@ class ListPanel extends Panel { it.onClick = () => { log(`Click item at ${idx}`) it.height += 10 + it.nativeChannel(context, "getWidth")().then( + resolve => { + log(`resolve,${resolve}`) + }, + reject => { + log(`reject,${reject}`) + }) } }) }, diff --git a/iOS/Pod/Classes/Extension/DoricBridgeExtension.m b/iOS/Pod/Classes/Extension/DoricBridgeExtension.m index 3434aa07..a23b586b 100644 --- a/iOS/Pod/Classes/Extension/DoricBridgeExtension.m +++ b/iOS/Pod/Classes/Extension/DoricBridgeExtension.m @@ -24,7 +24,6 @@ #import "DoricRegistry.h" #import "DoricContextManager.h" #import "DoricNativePlugin.h" -#import "DoricPromise.h" #import "DoricUtil.h" #import @@ -42,30 +41,44 @@ - (id)callNativeWithContextId:(NSString *)contextId module:(NSString *)module me nativePlugin = [(DoricNativePlugin *) [pluginClass alloc] initWithContext:context]; context.pluginInstanceMap[module] = nativePlugin; } + + return [self findClass:pluginClass target:nativePlugin context:context method:method callbackId:callbackId argument:argument]; +} + +- (id)createParamWithMethodName:(NSString *)method context:(DoricContext *)context callbackId:(NSString *)callbackId argument:(id)argument { + if ([method isEqualToString:@"withPromise"]) { + return [[DoricPromise alloc] initWithContext:context callbackId:callbackId]; + } + return argument; +} + +- (id)findClass:(Class)clz target:(id)target context:(DoricContext *)context method:(NSString *)name callbackId:(NSString *)callbackId argument:(id)argument { unsigned int count; id ret = nil; - Method *methods = class_copyMethodList(pluginClass, &count); + Method *methods = class_copyMethodList(clz, &count); + BOOL isFound = NO; for (int i = 0; i < count; i++) { NSString *methodName = [NSString stringWithCString:sel_getName(method_getName(methods[i])) encoding:NSUTF8StringEncoding]; NSArray *array = [methodName componentsSeparatedByString:@":"]; if (array && [array count] > 0) { - if ([array[0] isEqualToString:method]) { + if ([array[0] isEqualToString:name]) { + isFound = YES; SEL selector = NSSelectorFromString(methodName); - NSMethodSignature *methodSignature = [nativePlugin methodSignatureForSelector:selector]; + NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector]; if (methodSignature) { NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; invocation.selector = selector; - invocation.target = nativePlugin; + invocation.target = target; __weak __typeof__(self) _self = self; - void (^block)(void) = ^() { + dispatch_block_t block = ^() { __strong __typeof__(_self) self = _self; @try { - for (int i = 2; i < methodSignature.numberOfArguments; i++) { - if (i - 2 > [array count]) { + for (NSUInteger idx = 2; idx < methodSignature.numberOfArguments; idx++) { + if (idx - 2 > [array count]) { break; } - id args = [self createParamWithMethodName:array[i - 2] context:context callbackId:callbackId argument:argument]; - [invocation setArgument:&args atIndex:i]; + id args = [self createParamWithMethodName:array[idx - 2] context:context callbackId:callbackId argument:argument]; + [invocation setArgument:&args atIndex:idx]; } [invocation invoke]; } @catch (NSException *exception) { @@ -95,14 +108,13 @@ - (id)callNativeWithContextId:(NSString *)contextId module:(NSString *)module me if (methods) { free(methods); } - return ret; -} - -- (id)createParamWithMethodName:(NSString *)method context:(DoricContext *)context callbackId:(NSString *)callbackId argument:(id)argument { - if ([method isEqualToString:@"withPromise"]) { - return [[DoricPromise alloc] initWithContext:context callbackId:callbackId]; + if (!isFound) { + Class superclass = class_getSuperclass(clz); + if (superclass && superclass != [NSObject class]) { + return [self findClass:superclass target:target context:context method:name callbackId:callbackId argument:argument]; + } } - return argument; -} + return ret; +} @end diff --git a/iOS/Pod/Classes/Plugin/DoricShaderPlugin.m b/iOS/Pod/Classes/Plugin/DoricShaderPlugin.m index bb095f8b..73c796e8 100644 --- a/iOS/Pod/Classes/Plugin/DoricShaderPlugin.m +++ b/iOS/Pod/Classes/Plugin/DoricShaderPlugin.m @@ -22,10 +22,15 @@ #import "DoricShaderPlugin.h" #import "DoricRootNode.h" +#import "DoricUtil.h" + +#import + +#import @implementation DoricShaderPlugin -- (void)render:(NSDictionary *)argument withPromise:(DoricPromise *)promise { +- (void)render:(NSDictionary *)argument { __weak typeof(self) _self = self; dispatch_async(dispatch_get_main_queue(), ^{ __strong typeof(_self) self = _self; @@ -33,4 +38,92 @@ - (void)render:(NSDictionary *)argument withPromise:(DoricPromise *)promise { }); } +- (id)command:(NSDictionary *)argument withPromise:(DoricPromise *)promise { + NSArray *viewIds = argument[@"viewIds"]; + id args = argument[@"args"]; + NSString *name = argument[@"name"]; + DoricViewNode *viewNode = nil; + for (NSString *viewId in viewIds) { + if (!viewNode) { + viewNode = self.doricContext.rootNode; + } else { + if ([viewNode isKindOfClass:[DoricSuperNode class]]) { + viewNode = [((DoricSuperNode *) viewNode) subNodeWithViewId:viewId]; + } + } + } + if (!viewNode) { + [promise reject:@"Cannot find opposite view"]; + return nil; + } else { + return [self findClass:[viewNode class] target:viewNode method:name promise:promise argument:args]; + } +} + +- (id)createParamWithMethodName:(NSString *)method promise:(DoricPromise *)promise argument:(id)argument { + if ([method isEqualToString:@"withPromise"]) { + return promise; + } + return argument; +} + +- (id)findClass:(Class)clz target:(id)target method:(NSString *)name promise:(DoricPromise *)promise argument:(id)argument { + unsigned int count; + id ret = nil; + Method *methods = class_copyMethodList(clz, &count); + BOOL isFound = NO; + for (int i = 0; i < count; i++) { + NSString *methodName = [NSString stringWithCString:sel_getName(method_getName(methods[i])) encoding:NSUTF8StringEncoding]; + NSArray *array = [methodName componentsSeparatedByString:@":"]; + if (array && [array count] > 0) { + if ([array[0] isEqualToString:name]) { + isFound = YES; + SEL selector = NSSelectorFromString(methodName); + NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector]; + if (methodSignature) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; + invocation.selector = selector; + invocation.target = target; + __weak __typeof__(self) _self = self; + dispatch_block_t block = ^() { + __strong __typeof__(_self) self = _self; + @try { + for (NSUInteger idx = 2; idx < methodSignature.numberOfArguments; idx++) { + if (idx - 2 > [array count]) { + break; + } + id param = [self createParamWithMethodName:array[idx - 2] promise:promise argument:argument]; + [invocation setArgument:¶m atIndex:idx]; + } + [invocation invoke]; + } @catch (NSException *exception) { + DoricLog(@"CallNative Error:%@", exception.reason); + } + }; + dispatch_async(dispatch_get_main_queue(), ^{ + void *retValue; + block(); + [invocation getReturnValue:&retValue]; + id returnValue = (__bridge id) retValue; + [promise resolve:returnValue]; + }); + return ret; + } + break; + } + } + } + + if (methods) { + free(methods); + } + if (!isFound) { + Class superclass = class_getSuperclass(clz); + if (superclass && superclass != [NSObject class]) { + return [self findClass:superclass target:target method:name promise:promise argument:argument]; + } + } + return ret; +} + @end diff --git a/iOS/Pod/Classes/Shader/DoricGroupNode.m b/iOS/Pod/Classes/Shader/DoricGroupNode.m index 5a4aae7c..a8bd3141 100644 --- a/iOS/Pod/Classes/Shader/DoricGroupNode.m +++ b/iOS/Pod/Classes/Shader/DoricGroupNode.m @@ -159,4 +159,14 @@ - (void)blendSubNode:(NSDictionary *)subModel { } }]; } + +- (DoricViewNode *)subNodeWithViewId:(NSString *)viewId { + for (DoricViewNode *viewNode in self.childNodes) { + if ([viewId isEqualToString:viewNode.viewId]) { + return viewNode; + } + } + return nil; +} + @end diff --git a/iOS/Pod/Classes/Shader/DoricListNode.m b/iOS/Pod/Classes/Shader/DoricListNode.m index b4865253..d2f99259 100644 --- a/iOS/Pod/Classes/Shader/DoricListNode.m +++ b/iOS/Pod/Classes/Shader/DoricListNode.m @@ -150,4 +150,21 @@ - (void)callItem:(NSUInteger)position height:(CGFloat)height { [self.view reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; }]; } + +- (DoricViewNode *)subNodeWithViewId:(NSString *)viewId { + __block DoricViewNode *ret = nil; + dispatch_sync(dispatch_get_main_queue(), ^{ + for (UITableViewCell *tableViewCell in self.view.visibleCells) { + if ([tableViewCell isKindOfClass:[DoricTableViewCell class]]) { + DoricListItemNode *node = ((DoricTableViewCell *) tableViewCell).doricListItemNode; + if ([viewId isEqualToString:node.viewId]) { + ret = node; + break; + } + } + } + }); + return ret; +} + @end diff --git a/iOS/Pod/Classes/Shader/DoricSuperNode.h b/iOS/Pod/Classes/Shader/DoricSuperNode.h index b9915f07..1fe10660 100644 --- a/iOS/Pod/Classes/Shader/DoricSuperNode.h +++ b/iOS/Pod/Classes/Shader/DoricSuperNode.h @@ -32,4 +32,6 @@ - (void)setSubModel:(NSDictionary *)model in:(NSString *)viewId; - (void)clearSubModel; + +- (DoricViewNode *)subNodeWithViewId:(NSString *)viewId; @end \ No newline at end of file diff --git a/iOS/Pod/Classes/Shader/DoricSuperNode.m b/iOS/Pod/Classes/Shader/DoricSuperNode.m index ac1827d4..f3120924 100644 --- a/iOS/Pod/Classes/Shader/DoricSuperNode.m +++ b/iOS/Pod/Classes/Shader/DoricSuperNode.m @@ -122,4 +122,7 @@ - (void)clearSubModel { [self.subNodes removeAllObjects]; } +- (DoricViewNode *)subNodeWithViewId:(NSString *)viewId { + return nil; +} @end \ No newline at end of file diff --git a/iOS/Pod/Classes/Shader/DoricViewNode.m b/iOS/Pod/Classes/Shader/DoricViewNode.m index 1ef9c6e2..7049f702 100644 --- a/iOS/Pod/Classes/Shader/DoricViewNode.m +++ b/iOS/Pod/Classes/Shader/DoricViewNode.m @@ -220,4 +220,12 @@ - (void)requestLayout { [self.superNode requestLayout]; } +- (NSNumber *)getWidth { + return @(self.view.width); +} + +- (NSNumber *)getHeight { + return @(self.view.height); +} + @end diff --git a/js-framework/src/ui/layout.ts b/js-framework/src/ui/layout.ts index 0dd19eac..7b5dbf99 100644 --- a/js-framework/src/ui/layout.ts +++ b/js-framework/src/ui/layout.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { LayoutConfig, Group, Property, IView } from "./view"; +import { Group, Property, IView } from "./view"; import { Gravity } from "../util/gravity"; export interface IStack extends IView { @@ -22,8 +22,6 @@ export interface IStack extends IView { export class Stack extends Group implements IStack { } - - export class Root extends Stack { } diff --git a/js-framework/src/ui/view.ts b/js-framework/src/ui/view.ts index 5e4be06f..f473c9fd 100644 --- a/js-framework/src/ui/view.ts +++ b/js-framework/src/ui/view.ts @@ -255,6 +255,24 @@ export abstract class View implements Modeling, IView { in(group: Group) { group.addChild(this) } + + nativeChannel(context: any, name: string) { + let thisView: View | undefined = this + return function (...args: any) { + const func = context.shader.command + const viewIds = [] + while (thisView != undefined) { + viewIds.push(thisView.viewId) + thisView = thisView.superview + } + const params = { + viewIds: viewIds.reverse(), + name, + args, + } + return Reflect.apply(func, undefined, [params]) as Promise + } + } } export abstract class Superview extends View { @@ -315,6 +333,7 @@ export abstract class Group extends Superview { } addChild(view: View) { + view.superview = this this.children.push(view) } }