BREW SDK本身没有提供统一的应用程序框架, 开发者可以自已选择适合自已的程序框架。但是采用良好的程序结构可以开发代码结构清晰,可读性更高,易于维护。BREW SDK中带有一个例子MediaPlayer。提供了一个IWindow接口的结构证明是一个比较好的程序框架。
这里推荐使用的IWinApp、IWindow对上述结构进行了进一步封装,采用了一个类似MFC的文档-视图模块。MFC开发时一般提供了CWinApp,CFrameWnd, CDocument和一个CView的派生类。
仿照MFC,这里为BREW开发提出了一个IWinApp和IWindow接口。IWinApp接口的作用相当于MFC中的CWinApp和CDocument。IWindow接口则相当于CFrameWnd和CView的功能。
下面对这个接口的设计作一个简单的介绍。
消息分发顺序
- BREW AEE在接收到系统发送的消息,首先将消息发送到IWinApp接口的Handle_Event函数。
- IWinApp接口再将消息转发至IWindow接口的Handle_Event函数。
- IWindow接口再将消息转发到其中的当前活动的IControl的Handle_Event函数。
- 如果IControl处理该消息,则返回TRUE,否则IWindow接着判断是否处理该消息,如是处理,则返回TRUE。否则IWinApp判断是否处理该消息,如果处理,则返回TRUE。否则BREW调用缺省的消息处理器完成对该消息的处理。
因此要求所有UI相关的窗口都必需实现IWindow接口。
IWindow接口
接口定义如下:
typedef struct _IWindow IWindow;
QINTERFACE(IWindow)
{
// Enables/Disables the window. Window controls will not process
// events if the window is disabled.
void (*Enable)(IWindow * po, boolean bEnable);
// Redraws the window if enabled
void (*Redraw)(IWindow * po);
// Handles the events routed to the window
boolean (*HandleEvent)(IWindow * po, AEEEvent eCode, uint16 wParam, uint32 dwParam);
// Releases the window resources
void (*Delete)(IWindow * po);
};
#define IWINDOW_Enable(p) GET_PVTBL(p, IWindow)->Enable(p, TRUE)
#define IWINDOW_Disable(p) GET_PVTBL(p, IWindow)->Enable(p, FALSE)
#define IWINDOW_Redraw(p) GET_PVTBL(p, IWindow)->Redraw(p)
#define IWINDOW_HandleEvent(p, e, w, dw) GET_PVTBL(p, IWindow)->HandleEvent(p, e, w, dw)
#define IWINDOW_Delete(p) GET_PVTBL(p, IWindow)->Delete(p)
#define IWINDOW_SETVTBL(pVtbl, pfnEn, pfnRd, pfnHE, pfnDel) \
(pVtbl)->Enable = (pfnEn); \
(pVtbl)->Redraw = (pfnRd); \
(pVtbl)->HandleEvent = (pfnHE); \
(pVtbl)->Delete = (pfnDel)
typedef struct _AEE_BASE_WINDOW_T AEE_BASE_WINDOW_T;
#define INHERIT_BASEWINDOW(iname) \
DECLARE_VTBL(iname) \
IWinApp* m_pOwner; \
IShell* m_pIShell; \
IDisplay* m_pIDisplay; \
flg m_bActive:1
IWindow接口定义了四个虚函数:
虚函数 | 说明 |
Enable | 激活该IWindow派生类,要必要重画该窗口 |
Redraw | 窗口重画函数,IWinApp接口根据需要会调用该函数来重画当前活动窗口。 |
HandleEvent | 窗口消息处理函数,IWinApp接口调用该函数将消息转发至IWindow接口类。 |
Delete | 清除该IWindow接口。 |
其它定义的几个宏是为IWindow派生类提供的。
如果需要从IWindow派生UI Window时,可以采用如下的方式。例如:
typedef struct _TestScrollWin TestScrollWin;
struct _TestScrollWin
{
INHERIT_BASEWINDOW(IWindow);
PIRADIOBOX pRadio[12];
PICHECKBOX pCheck[12];
ITextCtl* m_pServerName; //server name input box
ITextCtl* m_pDomainName; //Domain name input box
ITextCtl* m_pUserName; //User name input box
ITextCtl* m_pPassword; //User password input box
IContainer* m_pContainer;
IMenuCtl * m_pSoftkey;
};
PUBLIC IWindow* TestScrollWin_New(IWinApp *pApp,void *pData);
PUBLIC boolean TestScrollWin_HandleEvent(IWindow * po, AEEEvent eCode, uint16 wParam, uint32 dwParam);
PUBLIC void TestScrollWin_Redraw(IWindow * po);
PUBLIC void TestScrollWin_Delete(IWindow * po);
PUBLIC void TestScrollWin_Enable(IWindow * po, boolean bEnable);
这里要注意的xxxx_New函数的实现,例子如下:
IWindow* TestScrollWin_New(IWinApp *pApp,void *pData)
{
TestScrollWin *pThis=NULL;
VTBL(IWindow) vtbl;
IWINDOW_SETVTBL(&vtbl, TestScrollWin_Enable,
TestScrollWin_Redraw, TestScrollWin_HandleEvent,
TestScrollWin_Delete);
pThis = (TestScrollWin *)AEE_BASE_WINDOW_New(sizeof(TestScrollWin),
pApp, &vtbl);
if (!pThis)
return NULL;
pThis->m_pOwner=pApp;
pThis->m_pIDisplay=pApp->a.m_pIDisplay;
pThis->m_pIShell=pApp->a.m_pIShell;
。。。
其中宏IWINDOW_SETVTBL 是为从IWindow派生的类初始化四个虚函数指针。函数AEE_BASE_WINDOW_New则是为该派生类分配内存。
IWinApp接口
IWinApp进一步提供了对AEEApplet结构的封,提供了消息映射及消息分发功能。
其主要定义如下:
#define AEE_MAX_EVENT_HANDLERS 256 //Max events handlers
#define AEE_MAX_WINDOWS 256 //Max window type
#define AEE_WINDOW_CLASS_NONE 0
typedef struct _IWinApp IWinApp;
typedef IWindow *(*WINDOWCONSTRUCTOR)(IWinApp *pApp,void *pData);
struct AEE_MSGMAP_ENTRY
{
AEEEvent nID; // control ID (or 0 for windows messages)
AEEEvent nLastID; // used for entries specifying a range of control id’s
AEEHANDLER pfn; // routine to call (or special value)
};
struct AEE_WINMAP_ENTRY
{
uint16 nWindowClass; //Window class ID
WINDOWCONSTRUCTOR pfn; //Window constructor
};
typedef struct AEE_MSGMAP_ENTRY PAEE_MSGMAP_ENTRY[AEE_MAX_EVENT_HANDLERS];
#define DECLARE_MESSAGE_MAP() \
struct AEE_MSGMAP_ENTRY m_tMessageEntries[AEE_MAX_EVENT_HANDLERS]
#define INHERIT_IWinApp \
AEEApplet a; \
IWindow* m_pActiveWindow; \
uint16 m_nEventNum; \
uint16 m_nWindowsNum; \
AEECallback m_cbRedraw; \
flg m_bRedraw:1; \
uint16 m_nActiveWindowType; \
struct AEE_WINMAP_ENTRY m_tWindowClassEntries[AEE_MAX_WINDOWS]; \
DECLARE_MESSAGE_MAP()
struct _IWinApp
{
INHERIT_IWinApp;
};
PUBLIC void BEGIN_MESSAGE_MAP(IWinApp *pApp);
#define END_MESSAGE_MAP(p)
PUBLIC void ON_EVENT(IWinApp *pApp,AEEEvent eCode,AEEHANDLER pfn);
PUBLIC void ON_EVENT_RANGE(IWinApp *pApp,AEEEvent eCode1,AEEEvent eCode2,AEEHANDLER pfn);
PUBLIC boolean IWINAPP_HandleEvent(IWinApp * pApp, AEEEvent eCode,uint16 wParam, uint32 dwParam);
PUBLIC boolean IWINAPP_RegisteWindowClass(IWinApp * pApp,uint16 nClass,WINDOWCONSTRUCTOR pfn);
PUBLIC void IWINAPP_Redraw(IWinApp *pThis, boolean bDefer);
PUBLIC void IWINAPP_RedrawNotify(IWinApp *pThis);
#define IWINAPP_CancelRedraw(p) { CALLBACK_Cancel(&(p)->m_cbRedraw); (p)->m_bRedraw = FALSE; }
#define IWINAPP_DisableWin(p) { IWINDOW_Disable((p)->m_pActiveWindow); IWINAPP_CancelRedraw(p); }
PUBLIC void IWINAPP_InitMessageMap(IWinApp * pApp);
PUBLIC boolean IWINAPP_InitInstance(IWinApp * pApp);
PUBLIC void IWINAPP_ExitInstance(IWinApp * pApp);
PUBLIC boolean IWINAPP_SetWindow(IWinApp *pThis,uint16 eWin, void *pData);
其中关键的是消息映谢处理:
宏BEGIN_MESSAGE_MAP为定义消息映射开始。
宏ON_EVENT定义一个消息与处理函数对应关系。
宏ON_EVENT_RANGE定义一个消息段与处理函数之间的对应关系。
宏END_MESSAGE_MAP结束消息定义。
所有的消息处理函数类型为AEEHANDLER
其定义为
typedef boolean (*AEEHANDLER)(void * pData, AEEEvent evt, uint16 wParam, uint32 lParam);
用法示例如下:
PUBLIC void IWINAPP_InitMessageMap(IWinApp *pApp)
{
BEGIN_MESSAGE_MAP(pApp);
ON_EVENT(pApp,EVT_APP_START,OnStart);
ON_EVENT(pApp,EVT_KEY,OnKey);
ON_EVENT_RANGE(pApp,EVT_KEY,EVT_CTL_SET_TEXT,OnKey);
END_MESSAGE_MAP(pApp);
}
函数IWINAPP_InitMessageMap 是由IWinApp提供一个函数,用于初始化消息映射。
采用这个结构在程序入口点函数AEEClsCreateInstance 会有如下代码。
if(AEEApplet_New(sizeof(CHelloApp), ClsId, pIShell,pMod,(IApplet**)ppObj,
(AEEHANDLER)IWINAPP_HandleEvent,
(PFNFREEAPPDATA)IWINAPP_ExitInstance) == TRUE)
从IWinApp派生类中无需再定义自已的HandleEvent函数。IWinApp接口直接完成消息处理。
函数IWINAPP_RegisteWindowClass 用于向IWinApp接口注册一个IWindow派生类。IWINAPP_SetWindow则将某个注册过的IWindow派生类设为当前活动窗口。