又要开始码字了。 这次是接上次的内容开始讲下object,本来还打算说下事件,不过感觉还是没法怎么说,这种涉及具体用法的东西,只能是放在实用篇尝试在确实应用中找。由于是续篇,多余话就不说了。
首先看下下面这个关于RTGUI WIDGET
的基本数据结构
struct rtgui_widget
{
/* inherit from rtgui_object */
struct rtgui_object object;
/* the widget that contains this widget */
struct rtgui_widget *parent;
/* the window that contains this widget */
struct rtgui_win *toplevel;
/* the widget children and sibling */
rtgui_list_t sibling;
/* widget flag */
rt_int32_t flag;
/* hardware device context */
rt_uint32_t dc_type;
const struct rtgui_dc_engine *dc_engine;
/* the graphic context of widget */
rtgui_gc_t gc;
/* the widget extent */
rtgui_rect_t extent;
/* minimal width and height of widget */
rt_int16_t min_width, min_height;
/* widget align */
rt_int32_t align;
rt_uint16_t border;
rt_uint16_t border_style;
/* the rect clip */
rtgui_region_t clip;
/* call back */
rt_bool_t (*on_focus_in)(struct rtgui_object *widget, struct rtgui_event *event);
rt_bool_t (*on_focus_out)(struct rtgui_object *widget, struct rtgui_event *event);
/* user private data */
rt_uint32_t user_data;
};
这个结构体里面包含了很多知识,深入的我讲不出来,没有那个水平,只能是把几个浅显的说一下。
首先第一个说的就是Object,也就是这一篇的重点。
struct rtgui_type
{
/* type name */
char *name;
/* parent type link */
struct rtgui_type *parent;
/* constructor and destructor */
rtgui_constructor_t constructor;
rtgui_destructor_t destructor;
/* size of type */
int size;
};
struct rtgui_object
{
/* object type */
const rtgui_type_t *type;
/* the event handler */
rtgui_event_handler_ptr event_handler;
enum rtgui_object_flag flag;
};
上面是一个RTGUI object的结构,内容很少,就是一个标志object的状态,一个object的事件处理指针,以及一个object的类型。从前面的垃圾博文,我说过,RTGUI WIDGET是控件的基本单位,但是实际上RTGUI WIDGET却是从RTGUI OBJECT派生出来的,然而RTGUI OBJECT却不能算是一个最小单位,因为它并非一个可操控可显示实体,控件大小,控件附加状态、颜色字体参数都是找不到的。但是RTGUI OBJECT有个很重要的地方,它包含了控件的类型以及一个事件处理指针。
很多人都是冲着RTGUI各种现成的控件来的,但是RTGUI 的控件也是自己搭建出来的,只不过它相对于控件的搭建方式比较特殊,它是利用C语言来实现类型C++对象的构造析构架构(用过c++的对象的人应该对这个模式很熟悉,就是当对象建立的时候,会自动调用构造函数执行一些必要初始化,控件删除的时候自动调用析构函数进行必要的内存清楚工作),当然,用c来实现这个功能,必然是无法隐藏它的调用过程的,具体过程也是可以简化成下面图所示的过程,参照一个c++的派生与继承关系,从当前类一直寻找基类,并同时调用对应构造与析构函数,直到最基类停止,这样就能够保证派生的正常初始化。
而控件能查找到基类的原因在于在控件类型的结构体中包含了一个基类的指针struct rtgui_type *parent,这是一个简单的单向列表,只要parent这个参数有效就能一直查找下去,以此来实现派生关系。(因此如果你新建了一个新的控件,那以就应该把参数初始化都放在构造函数constructor,把必要的变量删除,比如字符串缓冲,图片等占用申请资源的变量指针释放,都放到析构函数destructor里面,防止控件销毁还占用内存,这样当用这个控件类型派生新控件的时候,至少能保证被派生的控件部分能够正常初始化,就算出问题也该出在派生部分新建参数的操作上了,这样容易检查错误,也能够实现比较好的控件管理,不过没了解c++对象派生的还真不好理解这个)。
具体代码则是在
rtgui_object_create() \rtgui_object_destory()\rtgui_type_object_constructor()\rtgui_type_destructor_call()
这四个函数里面,但是它们都出乎意料的简单易懂,我就不说了。
说到控件的创建与销毁,必然会牵扯到一个控件类型定义的问题。很多人都知道RTGUI可以自己通过简单代码创建新的控件类型,但是却不知道RTGUI系统是如何知道有这个类型的。不过如果你打来某个控件的头文件与某个控件的c文件,拿最简单的widget为例,会有下面的代码。
Rtgui_widget.h :
DECLARE_CLASS_TYPE(widget);
/** Gets the type of a widget */
#define RTGUI_WIDGET_TYPE (RTGUI_TYPE(widget))
/** Casts the object to a rtgui_widget */
#define RTGUI_WIDGET(obj) (RTGUI_OBJECT_CAST((obj), RTGUI_WIDGET_TYPE, rtgui_widget_t))
/** Check if the object is a rtgui_widget */
#define RTGUI_IS_WIDGET(obj) (RTGUI_OBJECT_CHECK_TYPE((obj), RTGUI_WIDGET_TYPE))
Rtgui_widget.c
DEFINE_CLASS_TYPE(widget, "widget",
RTGUI_OBJECT_TYPE,
_rtgui_widget_constructor,
_rtgui_widget_destructor,
sizeof(struct rtgui_widget));
其中DEFINE_CLASS_TYPE代码的作用是为rtgui系统注册一个新的RTGUI控件类型,但是怎么定义的呢?这个需要用到编译器的特殊功能字符“##”。在编译器里面,##的作用是把两个宏参数贴一起构成另一个参数(这么说很难理解,具体还是去查下谷哥百度吧,打入##就有结果了),如果你懂了这个功能,那么就能看得懂下面的代码
#define DEFINE_CLASS_TYPE(type, name, parent, constructor, destructor, size) \
const struct rtgui_type _rtgui_##type = { \
name, \
parent, \
RTGUI_CONSTRUCTOR(constructor), \
RTGUI_DESTRUCTOR(destructor), \
size }
它的功能就想当于定义一个新的控件类型,控件类型的名字则是_rtgui_type,利用##的作用,_rtgui_直接跟type连接形成了一个新的控件名并成具有默认值,由于这是利用RTGUI默认宏创建的类型,它已经定义好了构造与析构函数,名称以及派生的基类,那么利用rtgui_object_create()与rtgui_object_destory()函数自然也就可以正确的完成创建与销毁。
#define RTGUI_TYPE(type) (struct rtgui_type*)&(_rtgui_##type)
#define RTGUI_WIDGET(obj) (RTGUI_OBJECT_CAST((obj), RTGUI_WIDGET_TYPE, rtgui_widget_t))
#define DECLARE_CLASS_TYPE(type) extern const struct rtgui_type _rtgui_##type
加上上面的部分,相当于在RTGUI全局定义了一个新的控件类型rtgui type,并包含必要的初始化参数,这样RTGUI系统也就知道你在系统中加了什么控件、包括控件名称、构造与析构函数,当然,它是形成在编译阶段。
至于RTGUI OBJECT里面包含的事件函数指针,则是用来实现控件事件自定义处理用的,本身倒是没有什么好说的,不过是个函数指针赋值与函数调实现灵活的事件处理而已。
惯例的来一个小结,写这篇时候想了很多,到底该怎么写。一开始思路也是事件,想的比较多,希望能尽量讲出有用的一篇文章出来,不过后来发现讲事件竟然无从下手,而讲解现在这个内容原理,其实已经开始背离本意了,个人还是觉得需要讲的应该不是什么原理,而是怎么用,只不过自己那点能耐到最后换出来的依旧是这样一篇没有什么价值的垃圾文章。
不过这部分终究是需要讲下的,但是至于后面怎么讲也是该写一篇算一篇吧,半途而费终究不好,但是耍下赖提前结束倒是可以的,不过估计也就一两篇无关痛痒的个人见解而已,用我的话说也就是瞎扯淡,后面看情况要不要启动实用篇,也就是一些简单用法,但是现在并没有硬件,网上一大棒子stm32fsmc加TFT,我确是没有的,而且个人习惯是不偏向于具体功能代码,而是通过数值反馈关系实现功能,因此到现在都很少有什么示例代码,所以能不能启用实用篇,还是很难说,也许可以用简单代码,不附带例程,那样就能开展了吧,前阵子还想着ui3.0的,结果刚开始了编写了一个控件后,就发现效果虽然不错,但是想实用已经很难了,因为自开架构必然工作量大,但是不考虑实用,除了效果好,一无是处,已经没有兴趣弄下去了,这样想来,RTGUI的开发者的确是让个很钦佩的,核心那么多代码虽然原理参照MFC,但是也是凭空写出来的(具体控件则是不算的,那个只能算是RTGUI的一个个小应用),此处向其致敬吧,现在工作早就远离RTGUI了,也不知道自己对它的印象能能保留到什么模样。