Sciter教程_02_分析uminimal例程

Sciter教程系列-02 分析sdk/demos/uminimal例程

上一个教程中建立窗口的代码在不同操作系统中并不通用,uminimal例程窗口实现中并不包含特定于操作系统的代码,适用于Windows/MacOS/Linux系统中的应用,并且该例程展示了脚本如何调用本地(C++)代码。

编译demos/uminimal例程

将文件sdk/demos/uminimal/uminimal.cppsdk/include/sciter-win-main.cpp拷贝到解决方案下的src/文件夹,在VS中移除筛选器src下的所有文件,然后添加这两个文件。拷贝sdk/demos/uminimal/res/文件夹,覆盖解决方案下的res文件夹。运行打包脚本,在VS中编译C++代码并运行,结果如下图所示。

在这里插入图片描述

注意,本文所使用的系统是Windows,所以拷贝的是sciter-win-main.cpp,对应的MacOS应拷贝sciter-osx-main.mm,Linux应拷贝sciter-gtk-main.cpp(只在Windows系统下验证过)。

分析C++代码

先看看窗口构建的流程。在uminimal.cpp中,定义了一个frame类,继承自父类sciter::window,父类中关于窗口创建、窗口最小化/最大化/关闭、消息代理/响应函数以及构造函数均在sciter-win-main.cpp中实现,同时,该文件还实现了入口函数wWinMain,在入口函数中,将消息循环以函数对象形式传递给uimain函数,该函数实现在uminimal.cpp中。在uimain函数中,实例化了一个frame对象,调用了资源文件加载以及窗口显示方法,最终将上图所示窗口显示出来。

除了窗口构建流程和minimal例程有差异,本例程还出现了SOM_PASSPORT_BEGIN/SOM_PASSPORT_END这个新的宏,这是用于暴露C++函数给脚本代码调用的。

SOM(Sciter Object Model)

Sciter对象模型,主要用于在以下三个方面通过本地代码(C++代码)扩展Sciter。

  • 脚本访问本地代码的命名空间、方法以及类。
  • 自定义的Sciter窗口。
  • 自定义本地DOM元素。
1、脚本访问本地代码的命名空间、方法以及类

为了达到脚本访问本地代码的对象或命名空间,需要做以下两件事:

  • 在本地代码中定义继承自sciter::om::asset<T>的类。这是为了使得对象可以进行引用计数,保证脚本在引用该对象时对象不会被释放。
  • 在宏定义SOM_PASSPORT_BEGIN/END中通过SOM_FUNCS将方法或者属性暴露给脚本,使得脚本可以调用这些暴露的本地方法。

uminimal例程中的NativeMath类定义如下。

struct NativeMath : public sciter::om::asset<NativeMath> {
  int sum(int a, int b) { return a + b; };
  int sub(int a, int b) { return a - b; };

  SOM_PASSPORT_BEGIN(NativeMath)
    SOM_FUNCS( SOM_FUNC(sum), SOM_FUNC(sub))
  SOM_PASSPORT_END
};

NativeMath将其两个成员函数暴露给脚本,为了能够使脚本能够像访问全局对象一样访问NativeMath,需要执行以下调用:

SciterSetGlobalAsset(new NativeMath());

之后在脚本中就可以执行以下调用:

var res = NativeMath.sum(1, 2);
2、自定义的Sciter窗口

窗口类的方法也可以暴露给脚本访问,只需要做到以下两点:

  • 类继承自sciter::event_handler。该父类继承了sciter::om::asset<event_handler>,满足引用计数要求。
  • 将需要暴露的方法放在SOM_PASSPORT_BEGIN/END之间。

例程中的frame类继承自sciter::window,其父类又继承自sciter::event_handler,满足第一个条件。

该类还将成员函数helloWorldgeneratedImage暴露给脚本了,于是脚本中可以像以下代码调用这两个方法。

view.frame.helloWorld();
view.frame.generatedImage();
3、自定义本地DOM元素

后续涉及tabs behavior再分析此种情况。

4、SOM_PASSPORT语句块的介绍

该语句块应该出现在类定义体内,开始于SOM_PASSPORT_BEGIN[_EX],结束于SOM_PASSPORT_END。开始语句块存在两种形式:

  • SOM_PASSPORT_BEGIN(className),其中className即是C++中的类名,也是脚本中使用的对象名。
  • SOM_PASSPORT_BEGIN_EX(className,scriptName),如果想在脚本中使用别的对象名,应该采用这种形式,由scriptName指定C++类className在脚本中对应的名字。

在开始语句块与结束语句块之间,还支持以下的宏:

  • SOM_FUNCS(functions definition list)。括号内的函数列表是用逗号分隔的宏,这些宏可以是SOM_FUNC(name)或者SOM_FUNC_EX(name, scriptName),后者用于本地/脚本不同类名的情况。
  • SOM_PROPS(properties definition list)。括号内的属性列表是用逗号分隔的宏,这些宏可以是以下列出宏的组合:
    • SOM_PROP(fieldName)。基于成员变量fieldName定义的读写属性。
    • SOM_RO_PROP(fieldName)。基于成员变量fieldName定义的只读(RO-ReadOnly)属性。
    • SOM_VIRTUAL_PROP(scriptName, getterFuncName, setterFuncName)。通过提供getter/setter方法定义的虚拟属性。
    • SOM_RO_VIRTUAL_PROP(scriptName, getterFuncName)。通过提供的getter方法定义只读的虚拟属性。
    • SOM_ITEM_GET(funcName)。当脚本执行形如var item = nativeObj[key];的代码时,运算符[]需要本地执行名为funcName的按索引读值函数提供支持。本地函数形式为bool funcName(keyType key_or_index, valType& outVal)
    • SOM_ITEM_SET(funcName)。当脚本执行形如nativeObj[key] = val;的代码时,运算符[]需要本地执行名为funcName的按索引写值函数提供支持。本地函数形式为bool funcName(keyType key_or_index, valType outVal)
    • SOM_ITEM_NEXT(funcName)。当脚本执行形如for(var item in nativeObj)的遍历过程时,需要本地执行名为funcName的迭代器函数提供支持。本地函数形式为bool funcName(sciter::value& index, valType& val)

以下列出的C++类型可以直接转换到对应的脚本类型。

  • bool – 对应到脚本中的true/false
  • int – 对应到脚本中的int(只支持int32)。
  • float/double – 对应到脚本中的float
  • sciter::string – 对应到脚本中的string
  • std::string – 对应到脚本中的symbol
  • std::vector<T> – 对应到脚本中的arrayT也要转换成脚本中的对应类型。
  • sciter::value – 对应到脚本中的union类型,可以包含上述列出的类型、undefined值、键值对map以及本地类型asset的引用。

分析TIS代码

Sciter中前端的响应处理使用的是tiscript脚本语言。有关该语言可以克隆https://github.com/gxlmyacc/sciter-doc-zh仓库进行查阅,该仓库是sciter的中文帮助手册,运行其中的Help.exe可以阅读帮助手册。

首先看self.ready事件。该事件在文档加载的最后一步生成,在事件响应函数中,调用了C++中注册(暴露)的函数。这里的view代表当前执行脚本所在的窗口,frame则是在执行SOM_PASSPORT_BEGIN(frame)是注册的类名,此类名可在脚本中像全局对象一样使用,通过该对象,可调用注册的两个成员函数helloWorldgeneratedImage。在event change事件中调用NativeMath的方法也是同理。

function self.ready() {
  // accessing native helloWorld() method defined in C++ class frame: 
  $(#message).text = view.frame.helloWorld();
  $(img#native).value = view.frame.generatedImage();
}

再看看event click事件中,通过view.window创建了一个窗口,这是view的一个成员函数,用于创建单独的窗口,该方法接受一个参数paramsparams是一个对象,它可以包含以下字段:

  • type - int类型,窗口类型,View.FRAME_WINDOWView.TOOL_WINDOWView.POPUP_WINDOW中之一。
  • url - string类型,加载到该窗口的文档的路径。
  • state - int类型,窗口的初始状态,View.WINDOW_SHOWNView.WINDOW_HIDDENView.WINDOW_MINIMIZEDView.WINDOW_MAXIMIZEDView.WINDOW_FULL_SCREEN其中之一。
  • parameters - 对象类型,传递的参数,它会放到新创建窗口的view.parameters变量中。
  • caption - string类型,窗口标题。
  • alignment - int类型,窗口相对于屏幕的位置,1 到 9,相对于父窗口。
  • screen - int类型,0 到 View.screens - 1,如果alignment是1到9,则该字段决定该窗口显示在哪个屏幕/监视器。可选参数。

详细信息可查阅sdk/doc/content/sciter/View.htm,该文档介绍了视图(View)对象的成员变量和成员函数等。

总结

通过uminimal这个例程,分析了通用的创建窗口加载资源文件、C++本地代码注册到脚本、脚本调用本地C++代码的整个流程。

  • 18
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值