资源描述
Form&Widget架构分析
Form&Widget
构架分析
AEE Group
Yangguomin and Wu jiaguo
目录
158
158/158
第一部分 Form 4
1.1. Form总体描述 4
1.1.1. 概述 4
1.1.2. 接口定义及继承关系 4
1. IBASE接口 4
2. IQueryInforace接口 5
3. IHandler:接口; 5
4. IForm:接口 5
5. IRootForm:接口 5
1.2. Form 6
1.2.1. 在Form&Widget中HandleEvent处理事件机制 8
1.2.2. 在Form中事件的处理 13
1.2.3. Form对Widget的支持管理 15
1.2.4. Form对RootForm中Title,Softkey的支持 15
1.2.5. Form窗体绘制 16
1.2.6. IForm对Theme的支持 17
1.3. Form窗体的使用 18
1. New 18
2. HandleEvnet 19
3. Dtor 21
1.4. Form扩展 21
1.4.1. 扩展Form 21
1.4.2. Form家族 22
1.4.3. ListForm 22
1. ListForm的创建: 22
2. ListForm的事件处理 25
3. ListForm的Dtor 28
1.4.4. Popup 29
1.4.5. Dialog 33
1.4.6. PopMenu 33
1.4.7. ErrorDialog 34
1.4.8. InfoDialog 34
1.4.9. WarningDialog 34
1.5. 1.4 RootForm 34
1.5.1. 屏幕区 35
1.5.2. RootForm对Forms的管理 35
RootFom_InsertForm() 36
RootForm_RemoveForm() 41
RootForm_GetForm() 44
RootForm_StatckChange() 46
RootForm_Update 50
RootForm_ResolveForm() 56
1.5.3. 其它 56
1.5.4. 问题 57
2. 第二部分 Widgets 58
2.1. Widgets总体描述 58
2.1.1. 概述 58
2.1.2. 接口定义及继承关系 58
IContainer接口 59
IModel接口 60
IWidget接口 60
2.2. Container 61
2.2.1. Container是怎样管理Widget的? 63
1. WidgetNode,Container利用WidgetNode来建立堆栈 63
2. ContainerBase管理WidgetNode堆栈 64
3. Container绘制Widget支持 66
4. Container对Widget的事件支持 72
5. Container对Widget焦点支持,Focus 74
2.2.2. Container对Forms的支持 79
2.2.3. Container与Model的关系 79
2.2.4. Container家族 79
ContainerBase 80
PropContainer 80
ImageStaticWidget 84
SoftKeyWidget 84
IConstaintContainer 84
IXYContainer 84
IRootContainer 85
ICardContainer 85
IDecorator 85
2.3. Model 85
2.3.1. Model家族 85
2.3.2. Model内部机制 85
2.3.3. 对ModelListener的分析 87
2.3.4. Adapter的实现 89
2.3.5. Model的重要接口 92
2.4. Widget 98
2.4.1. Widget家族 100
1. 基于WidgetBase的Widget 101
2. 基于Decorator的Widget 133
3. 第三部分 专项描述 160
3.1. Form&Widget的消息处理机制: 160
3.2 ViewModel 和 Model 161
第一部分 Form
1.1. Form总体描述
1.1.1. 概述
对于这部分描述:
1. VTBL接口定义,以及各个接口继承关系
2. RootForm对Forms的管理
3. 消息传递
在Forms中的难点在于RootForm对Forms的管理。这里也就这重描述RootForm是怎样对Form进行管理的
1.1.2. 接口定义及继承关系
就接口的继承关系来说,Forms具有如下的层次关系:
下面简要说明各个接口的实现。
1. IBASE接口
IBASE是所有对象的基类,用于记录对象的引用数量,确定对象的创建和释放。
l uint32 (*AddRef) (iname*); //加引用数量
l uint32 (*Release) (iname*); //减引用数量
2. IQueryInforace接口
该接口继承于IBASE接口,支持接口查询,查询成功,返回对象的引用,并增加接口引用数量。
l int (*QueryInterface)(iname *, AEECLSID, void **);//查询接口
3. IHandler:接口;
该接口继承于IQueryInerface接口,支持消息处理,所有从HandleEvent接口继承的对象,具备消息处理机制,用于支持消息传递。
l boolean (*HandleEvent) (iname *, AEEEvent evt, uint16 wParam, uint32 dwParam);
l void (*SetHandler) (iname *, HandlerDesc *pDesc); //设置消息处理器
4. IForm:接口
IForm完全继承于IHandler接口,没有自己的定义成员函数。IForm所有支持的成员函数如下:
//IBASE
uint32 (*AddRef) (iname*);
uint32 (*Release) (iname*);
//IQueryInforace.
int (*QueryInterface)(iname *, AEECLSID, void **);
//hander event;
boolean (*HandleEvent) (iname *, AEEEvent evt, uint16 wParam, uint32 dwParam);
void (*SetHandler) (iname *, HandlerDesc *pDesc);
5. IRootForm:接口
IRootForm接口继承于IForm接口除了所支持的函数如下所示:
/IBASE
uint32 (*AddRef) (iname*);
uint32 (*Release) (iname*);
//IQueryInforace.
int (*QueryInterface)(iname *, AEECLSID, void **);
//hander event;
boolean (*HandleEvent) (iname *, AEEEvent evt, uint16 wParam, uint32 dwParam);
void (*SetHandler) (iname *, HandlerDesc *pDesc);
//IRootForm
int (*InsertForm) (iname *, IForm *pf, IForm *pfBefore); //插入窗体
int (*RemoveForm) (iname *, IForm *pf); //移出窗体
IForm *(*GetForm) (iname *, IForm *pwRef, boolean bNext, boolean bWrap);
int (*ResolveForm) (iname *, char const *szFormUrl, IForm **ppiForm);
void (*GetClientRect) (iname *, IXYContainer **ppContainer, AEERect *rc)
1.2. Form
Form用于管理一个固定的窗体外观,处理用户交互消息, 可以通过ISHELL_CreateInstance进行创建。Form提供了一个IRootForm接口,专门用于管理所有窗体,另外提供了Pop和Dialog处理弹出的消息窗体。
Form负责了窗体的创建,管理了他自己的Widget的整个生存周期。
基本的Form窗体结构定义如下所示:
typedef struct Form {
AEEVTBL(IForm) * pvt;
int nRefs;
IModule * piModule;
//事件处理机制
HandlerDesc handler;
PFNHANDLER pfnDefHandler; //消息处理器函数
IShell * piShell;
IWidget * piWidget; //指向自身Widget对象的指针XYContainer
IRootForm * piRootForm; //指向RootForm的指针
boolean bActive; //表明该Form是否是激活的
boolean bIsPopup; //是否是弹出窗体
uint16 wCancelKey;
const char * themeBaseName; //Theme文件名,支持Theme
IModel * piModel; //保存Model指针
uint32 dwItemMask;
struct {//记录SoftKey的两个文本和Title标题文本
AECHAR *softkey[2];
AECHAR *title;
} text;
struct { //记录背景图片
IImage * ptr;
} bgImage;
struct {//记录标题图片
IImage *ptr;
} titleImage;
} Form;
1.2.1. 在Form&Widget中HandleEvent处理事件机制
l HandlerDesc数据结构:
Typedef struct {
PFNHANDLER pfn;//你要处理的事件函数(回调函数)
Void * pCxt;//你传入的第一个对象参数
PFNFREEHANDLER pfnFree;//释放函数
} HandlerDesc;
l 使用三个函数(宏定义)来初始化数据结构,和调用数据结构中保存的函数。
#define HANDLERDESC_Init(pdesc,ph,pv,pf) \
(pdesc)->pfn = (PFNHANDLER)(ph),\
(pdesc)->pCxt = (pv), \
(pdesc)->pfnFree = (PFNFREEHANDLER)(pf)
#define HANDLERDESC_Call(pdesc,e,w,dw) \
( ((pdesc)->pfn) ? (pdesc)->pfn((pdesc)->pCxt,(e),(w),(dw)) : FALSE )
#define HANDLERDESC_Free(pdesc) \
if ((pdesc)->pfnFree) (pdesc)->pfnFree((pdesc)->pCxt);
l SetHandler接口
SetHandler();用于替换原来的Hander, 重新指向HandlerEvent函数,SetHander的常用实现如下所示:
void WidgetBase_SetHandler(IWidget *po, HandlerDesc *phd)
{
DECLARE_ME;
// Reset on null descriptor
if (!phd) {
HANDLERDESC_Init(&me->hd, me->pfnDefHandler, me, 0);
} else {
HandlerDesc hdTemp = *phd;
*phd = me->hd;//返回了自身的Handler,以备对方调用。
me->hd = hdTemp;
}
}
看上边的代码,在SetHandle扔调用过程中,替换了当前Widget的HandleEvent,并返回了自己现在的Hander, 这样,开发时在处理好了自己的事件,并可调用替换掉的Hander所指向的HandleEvent。
在提供事件处理的对象中,一般提供了
HandlerDesc handler;
PFNHANDLER pfnDefHandler;
支持IHandler接口的对象,都必须实现HandleEvent接口。
基本类为了支持重载,在它的HandleEvent中都不是事件处理的真实函数。他的内容通常如下:
return HANDLERDESC_Call(&me->handler, evt, wParam, dwParam);
而真正处理事件的函数保存在me->handler中,在Handler中的函数改变,消息处理函数相应的跟着改变。
在Form&Widget中有两种典型方式的消息重载方式。
方式1. 典型的重载消息事件如下所示
1. 覆盖父类的HandleEvent(程序代码来源于SoftKeyWidget)
IWidget *piContWidget = 0;
SoftkeyWidget *me = MALLOCREC(SoftkeyWidget);
if (!me)
return ENOMEMORY;
ISHELL_CreateInstance(piShell, AEECLSID_PROPCONTAINER, (void **)&me->piContainer);
IPROPCONTAINER_QueryInterface(me->piContainer, AEEIID_WIDGET,
(void **)&piContWidget);
HANDLERDESC_Init(&me->handler, SoftkeyWidget_HandleEvent,
me, SoftkeyWidget_Delete);
IWIDGET_SetHandler(piContWidget, &me->handler);
这里重载父类PropContainer的处理函数。
HANDLERDESC_Init中的第一个参数为HandleResc的Hander,第二个参数为HandleEvent的处理函数,第三个为HandleEvent调用时的第一个参数(一般为对象参数,在这里是SoftkeyWidget),第四个为对象在释放时调用的Release函数。
2.在自己的消息处理函数中调用父类的消息处理函数
boolean SoftkeyWidget_HandleEvent(SoftkeyWidget *me, AEEEvent evt, uint16 wParam, uint32 dwParam)
{
int result = 0;
if (HANDLERDESC_Call(&me->handler, evt, wParam, dwParam)) {//调用 Prop的消息处理函数
if (evt == EVT_WDG_SETFOCUS || evt == EVT_WDG_CANTAKEFOCUS) {
return FALSE;
} else {
return TRUE;
}
} else if (evt == EVT_WDG_GETPROPERTY) {
………………………….
}
上边HANDLERDESC_Call(&me->handler, evt, wParam, dwParam)调用了替换掉的消息处理函数。
这种方式广泛用于重载利用ISHELL_CreateInstance对象的消息重载。
方式2. 构造类型的重载
基类构造函数的实现:
void Form_Ctor(Form *me, AEEVTBL(IForm) *pvt, IModule *piModule,
IShell *piShell, PFNHANDLER pfnDefHandler)
{
……………………………..
pvt->HandleEvent = Form_HookedHandleEvent;
pvt->SetHandler = Form_SetHandler;
me->pfnDefHandler = DEFHANDLER(Form_HandleEvent);//初始化事件处理函数
HANDLERDESC_Init(&me->handler, me->pfnDefHandler, me, 0);
……………………………….
}
上边示例代码初始化的事件函数时,如果传入了事件处理函数,则用传入如的事件处理函数初始化,否则使用缺省的处理函数:
宏定义如下:
#define DEFHANDLER(mpfn) (pfnDefHandler ? pfnDefHandler : (PFNHANDLER)(mpfn))
在派生类中:
int Popup_Construct(Popup *me, AEEVTBL(IForm) *pvt, IModule *piModule,
IShell *piShell, boolean bBackdrop, PFNHANDLER pfnDefHandler)
{
int result;
Form_Ctor(&me->base, (AEEVTBL(IForm) *)pvt, piModule,
piShell, DEFHANDLER(Popup_HandleEvent));
……………………………………..
}
可以看出,如果没有重载Pop事件处理函数的话,Form的事件处理将被Popup_HandleEvent重载。如果在Pop事件处理函数中需要处理From的事件处理函数应该怎么处理呢?答案很简单,直接调用Form的事件处理函数就可以了。
1.2.2. 在Form中事件的处理
在Form中的事件处理也同上边的事件处理相同,在Form中事件的具体处理如下所示:
1. 定义了成员变量
PFNHANDLER pfnDefHandler;//指向真正调用的HandleEvent函数
HandlerDesc handler;//用于保存HandleEvent信息,支持记录指向HandleEvent的指针。
2. Form构造函数中
Pvt->HandleEvent = Form_HookedHandleEvent;
Me->pfnDefHandler = DEFHANDLER(Form_HandleEvent);
HANDLERDESC_Init(&me->handler, me->pfnDefHandler, me, 0);
3. 在Form_HookedHandleEvent ()中
HANDLERDESC_Call(&me->handler, evt, wParam, dwParam);
Form中事件的处理流程:
l p->HandleEvent()本身地址指向的是Form_HookedHandleEvent(),
l 在Form_HookedHandleEvent中调用HANDLERDESC_Call()实际上调用的是Form_HandleEvent(),Form_HandleEvent()才是真正处理Form事件的地方,
l 如果我要处理自己定义的事件,我只需要用SetHandler重新定义或者构造时覆盖Handler中事件处理函数指针就可以调用自定义的事件处理函数。
Pvt->HandleEvent = Form_HookedHandleEvent;
Me->pfnDefHandler = DEFHANDLER(Form_HandleEvent);
HANDLERDESC_Init(&me->handler, me->pfnDefHandler, me, 0);
如果构造函数中事件函数参数为有效值,即可覆盖掉Form_HandleEvent函数。
接下来我们再看Form派生类的事件处理
Form派生类的事件处理
我们以ListForm派生类来说明:
1. 在listForm中定义了ListForm_HandleEvent()函数
2. 在构造函数中,调用了
Form_Ctor(&me->base, pvt, piModule,
piShell, DEFHANDLER(ListForm_HandleEvent));
从上边描述可以知道ListForm_HandleEvent已经覆盖了Form_HandleEvent, 处理IFORM_HandleEvent时,调用的函数为ListForm_HandleEvent函数。
注意:现在如果我要调用基类Form的HandleEvent,应该怎么处理呢?
我们来看ListForm_HandleEvent的处理:
在ListForm_HandleEvent所有的消息都没有处理之后,有语句:
return Form_HandleEvent(po, evt, wParam, dwParam);
这样就调用了基类函数的事件处理,不但处理了我们自定义的事件,也调用了基类提供给我们的事件处理。
Form中消息支持函数定义如下,详细实现请看FormBase.c
boolean Form_HandleEvent(IForm *po, AEEEvent evt, uint16 wParam,
uint32 dwParam)
该函数处理了大量的参数设置,和参数获取操作。
static boolean Form_HookedHandleEvent(IForm *po, AEEEvent evt, uint16 wParam,
uint32 dwParam)
void Form_SetHandler(IForm *po, HandlerDesc *phd)
1.2.3. Form对Widget的支持管理
Form管理了Widget的生存周期,在一个Form中存放了一个Widget的指针,这个Widget可以是Widget,也可以是Decorator和Container。当为一个Decorator或者Container时,Form可以管理多个Widget组成的外观。
在Form在构造中创建Widget,并初始化他们的显示外观,比如边框,颜色,每个Widget的位置,背景图等。在Form释放时,也必须释放创建的Widget。
IWidget * piWidget;//保存Widget
Form_OnActive(); //用于重绘Widget
1.2.4. Form对RootForm中Title,Softkey的支持
在Form中提供了成员:
struct {
AECHAR *softkey[2];
AECHAR *title;
} text;
struct {
IImage *ptr;
} titleImage;
Text存放了SoftKey要显示的文本和Title要显示的文本。titleImage则提供了保存Title的Image。RootForm注册了一个Listener到Form中,如果Title或者Softkey显示信息改变,则通过下边的函数重置RootForm,RootForm负责把信息显示在Title和RootForm上。
static void Form_LoadTextNotify(Form *me, FormRes *pFormRes,
AECHAR **ppDest, uint32 dwItemMask)
static void Form_LoadImageNotify(Form *me, FormRes *pFormRes,
IImage **ppDest, uint32 dwItemMask)
1.2.5. Form窗体绘制
对Form的绘制由函数Form_onActivate支持
1. 从RootForm获取RootContainer
2. 获取设置Widget的Extent信息
3. 然后确定自己的Widget在ClientRect中绘制的位置
4. 绘制Widget
5.
static __inline void Form_onActivate(Form *me)
{
AEERect rc;
IXYContainer *piContainer;
//1.从RootForm获取RootContainer
IROOTFORM_GetClientRect(FORM_ROOT(me), &piContainer, NULL);
//2.获取设置Widget的Extent信息。
if (FORM_WIDGET(me) &&
IFORM_GetProperty(FORM_TO_IFORM(me),
FID_PREFRECT, (uint32)&rc) == SUCCESS) {// 返回rc-Widget的最合适的尺寸,大小
WExtent extent;
WidgetPos pos;
SETWEXTENT(&extent, rc.dx, rc.dy);
IWIDGET_SetExtent(FORM_WIDGET(me), &extent);
// and position
//3. 确定自己的Widget在ClientRect中绘制的位置
IXYCONTAINER_GetPos(piContainer, FORM_WIDGET(me), &pos);
pos.x = rc.x;
pos.y = rc.y;
//4. 绘制Widget
IXYCONTAINER_SetPos(piContainer, FORM_WIDGET(me),
WIDGET_ZNORMAL, &pos);
}
IXYCONTAINER_Release(piContainer);//其实并没有释放空间,只是将对rootcontainer的引用数减1
}
1.2.6. IForm对Theme的支持
Form中保存了一个const char * themeBaseName;这是Theme文件中的路径字符串,在Form中缺省名字为“System”,在HandleEvent中处理了FID_THEME事件,它利用该路径获取背景图片,并对本窗体的Widget利用themeBaseName来对Widget进行初始化。
你可以在运行过程中动态的应用,利用IFORM_SetThemeName();进行设置。
Form对Theme的处理:
case FID_THEME:
{
IResFile *piResFile = 0;
IImage *piImage = 0;
if (SUCCESS == IROOTFORM_GetThemeFile(FORM_ROOT(me), &piResFile)) {
// Load background image
IRESFILE_GetNamedImage(piResFile, (void **)&piImage,
FORM_THEME_BASENAME(me), "Background.Image", 0);
// Apply form widget properties
Theme_ApplyProps(piResFile, FORM_WIDGET(me),
FORM_THEME_BASENAME(me), "Properties", 0);
}
// Replace image if one was found
if (piImage) {
RELEASEIF(FORM_BGIMAGE(me));
FORM_BGIMAGE(me) = piImage;
IIMAGE_AddRef(piImage);
};
RELEASEIF(piImage);
RELEASEIF(piResFile);
return TRUE;
}
1.3. Form窗体的使用
Form已经存在了,那么在你的应用程序中应该如何使用他们呢?
自定义窗体一般需要处理三个部分。
构造窗体
1. New,负责创建窗体,分配内存
2. Ctor,初始化窗体的Widget
释放窗体:
可以用Delete对应New,可以成为固定的模版,不进行更改
Dtor对应Ctor,必须对你的窗体进行处理
3. Dtor,析构你创建的窗体
4. Delete,释放窗体
事件处理
5. HandleEvent,处理人机交互消息
下边介绍一般情况创建Form的基本步骤。
1. New
这里分配内存并调用Ctor构造窗体:这部分你可以采用固定模式:
// malloc memery to story form and call ctor to initial this object.
//************************************************************************
// here is not changed
int FormFrame_New(IForm **ppo, IShell *piShell, IModule *piModule,
IRootForm *piRootForm)
{
FormFrame *pme = NULL;
pme = MALLOC(sizeof(FormFrame));
if (!pme)
{
return ENOMEMORY;
}
if(FormFrame_Ctor(pme, piShell,piModule, piRootForm))
{
// add the message maped.
//************************************
INIT_MASSAGE_MAP(FormFrame, pme);
//************************************
*ppo = pme->piForm;
return SUCCESS;
}
else
{
FormFrame_Delete(pme);
return EFAILED;
}
}
2. Ctor初始化
完成:
1. 创建组成窗体的Widget,并初始化他们的位置。
2. 创建Form,并设置Handler,自己处理事件消息
Sample:
//************************************************************************
// constuctor , you must intial the object at here.
//************************************************************************
static boolean FormFrame_Ctor(FormFrame *po, IShell *piShell,
IModule *piModule, IRootForm *piRootForm)
{
int iRt;
WExtent we;
WidgetPos wp;
char *pch = "Example!!!!";
AECHAR *pwch =NULL;
IXYContainer *pxyGround = N
展开阅读全文