第二十八篇:SOUI中自定义控件开发过程

在SOUI中已经提供了大部分常用的控件,但是内置控件不可能满足用户的所有要求,因此一个真实的应用少不得还要做一些自定义控件。

学习一个新东西,最简单的办法就是依葫芦画瓢。事实上在SOUI系统中内置控件和自定义控件的开发流程是完全一样的,因此只需要打开SOUI的源代码,随便找一个控件看一下就大体差不多了。

下面我以controls.extend目录下的的SRadioBox2控件为例对控件开发过程需要注意的地方做一点说明。

要开发一个控件,首先要确定的是应该从哪个控件来继承。选择一个合适的基类是正确开发自定义控件的前提。

之所以要开发一个SRadioBox2控件,我需要解决的问题很简单:SRadioBox控件总是在左边显示一个圆圈,这个圆圈有时候不是我想要的。

因此我需要做的就是继承SRadioBox控件的行为,重写WM_PAINT的处理。

因此就有了下面的代码:

 class SRadioBox2 : public SRadioBox
    {
    public:
        SRadioBox2(void);
        ~SRadioBox2(void);
    }

需要注意的是,所有SOUI控件都是在namespace SOUI中,因此自定义控件也最好是放在SOUI这个namespace里。

有了上面的骨架,下面来逐步添加内容。

首先我们需要给自定义控件定义一个在XML中可以识别的标签。

只需要在类的最开始增加一行:

 class SRadioBox2 : public SRadioBox
    {
    SOUI_CLASS_NAME(SRadioBox2,L"radio2")
    public:
        SRadioBox2(void);
        ~SRadioBox2(void);
    }

SOUI_CLASS_NAME告诉XML解析器,碰到radio2时自动创建SRadioBox2对象。

实际上这一行更重要的作用是用来做对象类型运行时识别(RTTI),有了这个机制,在编译器关闭C++的RTTI时仍然可以安全的进行类型转换。

然后我们需要处理控件的WM_PAINT消息。

为了处理这个消息,我们需要加入消息映射表及消息处理函数:

class SRadioBox2 : public SRadioBox
    {
    SOUI_CLASS_NAME(SRadioBox2,L"radio2")
    public:
        SRadioBox2(void);
        ~SRadioBox2(void);

    protected:
        void OnPaint(IRenderTarget *pRT);

        SOUI_MSG_MAP_BEGIN()
            MSG_WM_PAINT_EX(OnPaint)
        SOUI_MSG_MAP_END()
    };

SOUI控件的消息处理机制是和WTL中抄过来的,和MFC也很相似。只是对于部分消息,由于对于消息的参数的解释不一样,消息映射的宏会有一点变化。

如这里的WM_PAINT消息,在SOUI里wparam传递的是一个IRenderTarget指针,而传统的Win32传递的是一个HDC。因此我们需要使用MSG_WM_PAINT_EX代替WTL中使用的MSG_WM_PAINT。

大家可能会问有哪些消息映射宏SOUI和WLT不一样?

实际上对于一个有经验的程序员,他应该可以找到MSG_WM_PAINT_EX的宏定义,并且在定义附近就可以找到所有的SOUI和WLT不同的映射宏。

下面是到目前为止SOUI中所有和WTL不同的宏:

// BOOL OnEraseBkgnd(IRenderTarget * pRT)
#define MSG_WM_ERASEBKGND_EX(func)     if (uMsg == WM_ERASEBKGND)     {     SetMsgHandled(TRUE);     lResult = (LRESULT)func((IRenderTarget *)wParam);     if(IsMsgHandled())     return TRUE;     }

// void OnPaint(IRenderTarget * pRT)
#define MSG_WM_PAINT_EX(func)     if (uMsg == WM_PAINT)     {     SetMsgHandled(TRUE);     func((IRenderTarget *)wParam);     lResult = 0;     if(IsMsgHandled())     return TRUE;     }

// void OnNcPaint(IRenderTarget * pRT)
#define MSG_WM_NCPAINT_EX(func)     if (uMsg == WM_NCPAINT) {     SetMsgHandled(TRUE);     func((IRenderTarget *)wParam);     lResult = 0;     if(IsMsgHandled())     return TRUE; }

// void OnSetFont(IFont *pFont, BOOL bRedraw)
#define MSG_WM_SETFONT_EX(func)     if (uMsg == WM_SETFONT)     {     SetMsgHandled(TRUE);     func((IFont*)wParam, (BOOL)LOWORD(lParam));     lResult = 0;     if(IsMsgHandled())     return TRUE;     }

// void OnSetFocus()
#define MSG_WM_SETFOCUS_EX(func)     if (uMsg == WM_SETFOCUS) {     SetMsgHandled(TRUE);     func();     lResult = 0;     if(IsMsgHandled())     return TRUE; }

// void OnKillFocus()
#define MSG_WM_KILLFOCUS_EX(func)     if (uMsg == WM_KILLFOCUS) {     SetMsgHandled(TRUE);     func();     lResult = 0;     if(IsMsgHandled())     return TRUE; }

// void OnNcMouseHover(int nFlag,CPoint pt)
#define MSG_WM_NCMOUSEHOVER(func)     if(uMsg==WM_NCMOUSEHOVER){    SetMsgHandled(TRUE);     func(wParam,CPoint(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam)));     lResult = 0;     if(IsMsgHandled())     return TRUE; }

// void OnNcMouseLeave()
#define MSG_WM_NCMOUSELEAVE(func)     if(uMsg==WM_NCMOUSELEAVE){    SetMsgHandled(TRUE);     func();     lResult = 0;     if(IsMsgHandled())     return TRUE; }

// void OnTimer(char cTimerID)
#define MSG_WM_TIMER_EX(func)     if (uMsg == WM_TIMER) {     SetMsgHandled(TRUE);     func((char)wParam);     lResult = 0;     if(IsMsgHandled())     return TRUE; }

#define WM_TIMER2    (WM_USER+5432)    //定义一个与HWND定时器兼容的SOUI定时器

#define MSG_WM_TIMER2(func)     if (uMsg == WM_TIMER2) {     SetMsgHandled(TRUE);     func(wParam);     lResult = 0;     if(IsMsgHandled())     return TRUE; }

通常情况下,自定义控件还需要处理一些自定义的属性,这时我需要还需要增加一个属性映射表(如SGifPlayer):

        SOUI_ATTRS_BEGIN()
            ATTR_CUSTOM(L"skin", OnAttrSkin) //为控件提供一个skin属性,用来接收SSkinObj对象的name
        SOUI_ATTRS_END()

完成上面几步,一个自定义控件基本上就完成了。

可能还需要实现几个修改基类行为的虚函数。

时间: 2024-10-10 22:27:46

第二十八篇:SOUI中自定义控件开发过程的相关文章

Egret入门学习日记 --- 第二十八篇(书中 9.19 ~ 9.19 节 内容)

第二十八篇(书中 9.19 节 内容) 没想到第九章的组件篇可真是够长的,没事,慢慢来吧. 开始 9.19节. 重点: 1.创建一个Tips提示组件. 操作: 1.创建一个Tips提示组件. 哇!出大问题!这个Tips组件有点牛皮!怎么办? 书中内容这部分,我直接懵逼. 还有这部分也是. 一点一点来分析好吧. 好,开始分析一波. 第一步:查看最终展示效果. 这是最终效果.(我去,我现在才知道,原来博客园的编辑器还可以放Gif图片...) 第二步:这个黑色的弹框组件本质上是由什么构成的? 可见,在

第二十八篇: 学习笔记

最近一直在学一些零零碎碎的东西, 首先,将<鸟哥的 Linux 私房菜 -- 基础学习篇>过了一遍. 因为有了Windows驱动,PCI/PCIe, USB, xHCI, Windows内核编程的基础, 加上自己以前也动手写过Linux设备驱动, 研究过Makefile, KConfig, make menuconfig, GIT的部分内容 鸟哥的这本书确实写得非常通俗易懂,帮我解开了一些以前知道怎么操作, 但不知道为什么要这样操作的疑惑. 另外,基本完成了USB Audio/Video Cl

第二十八篇 静态属性、类方法、静态方法

静态属性 静态属性:实际上说的就是数据属性 需求:每个人都有自己的房子,想知道每个人自己的房子都有多大平米 实现分析:每个人,表示有很多人,不可能一个人写一次计算面积的方法,那么就可以将它提炼出来,放到类里 class Room: tag=1 def __init__(self,name,owner,width,length,heigh): self.name=name self.owner=owner self.width=width self.length=length self.heigh

Python之路【第二十八篇】:django视图层、模块层

1.视图函数 文件在view_demo 一个视图函数简称视图,是一个简单的Python 函数,它接受Web请求并且返回Web响应.响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . . 是任何东西都可以.无论视图本身包含什么逻辑,都要返回响应.代码写在哪里也无所谓,只要它在你的Python目录下面.除此之外没有更多的要求了——可以说“没有什么神奇的地方”.为了将代码放在某处,约定是将视图放置在项目或应用程序目录中的名为views.py的文件中. 视图

开始写游戏 --- 第二十八篇

今天: 1.添加:物品奖励组件 2.添加:物品信息弹框组件 3.碰到BUG,现在被卡住了 上图: 1.添加:物品奖励组件 { 效果: 代码: } 2.添加:物品信息弹框组件 { 效果: 代码: 要使用这个组件的话,还要搭配一个数据转换类: } 3.碰到BUG,现在被卡住了 { 可以看到,物品信息组件显示位置出现问题. 这个FGUI官方群,现在都没有给具体的解决方案. 这是在FGUI编辑器中的效果: 这是游戏运行的效果: 为了证明是同一个组件,我特意换了一下颜色: 可见,现在这种情况...我真的有

小刘同学的第二十八篇博文

今天状态不是很好,不知道怎么的,坐在椅子都感觉很难受,难得晚上吃的麻辣香锅有毒? 特别难受,一阵阵的恶心,强打起精神来,把16次课的最后一道逻辑题写完了,其实这个挺简单的,不过没思考,很简单的用了很多ifififif-- 应该可以少用点if,逻辑应该会更好看一点,实在是头晕恶心,就没多想,写完了就行吧-- <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"

我的第二十八篇博客---seleinum

from selenium import webdriver #初始化驱动driver=webdriver.Chrome()#请求页面driver.get(url='https://www.baidu.com')#找到对应标签,发送文本driver.find_element_by_xpath("//*[@id='kw']").send_keys('2019') #找到按钮,点击clickdriver.find_element_by_xpath("//*[@id='su']&q

Egret入门学习日记 --- 第二十二篇(书中 9.7~9.8 节 内容)

第二十二篇(书中 9.7~9.8 节 内容) 开始 9.7节 内容. 重点: 1.进度条ProgressBar的声明和使用. 操作: 1.进度条ProgressBar的声明和使用. 现在真的轻车熟路了,很简单.无非就是设置一下最大值,当前值的属性. 然后,事件监听的话,也是一样的.只不过事件名字的话,我就选书中这个事件吧. 可惜不能发动图,不然你们就可以看到这个进度条,每帧+1的速度前进. 当然,如果你想换自定义皮肤,还是老规矩,去找默认的 EXML 文件. 然后,怎么换素材,就按照自己喜欢的换

Egret入门学习日记 --- 第二十四篇(书中 9.12~9.15 节 内容)

第二十四篇(书中 9.12~9.15 节 内容) 开始 9.12节 内容. 重点: 1.TextInput的使用,以及如何设置加密属性. 操作: 1.TextInput的使用,以及如何设置加密属性. 创建exml文件,拖入组件,设置好id. 这是显示密码星号处理的属性. 创建绑定类. 实例化,并运行. 但是焦点在密码输入框时,密码是显示的. 暂时不知道怎么设置 “焦点在密码框上时,还是显示为 * 号” 的方法. 至此,9.12节 内容结束. 开始 9.13节 . 这个,和TextInput的使用