终于到RTGUI WIDGET篇了。这个应该就比较重要的一部分了,不过可能一次写不完,只能改成多次分开写,而且会写的很不好。(当然有更重要的,就是相关的一些应用举例,后面可能看情况写一下。)
首先谈下什么是RTGUI WIDGET,RTGUI WIDGET其实是一种控件的基本单位,也就是最底层单位,就好比毫米是厘米的底层单位一样。对于我们来说,要控制一个物体,那么前提是这个物体必须具有实体(拿锤子砸石头,没有锤子你怎么砸,难道拿自己拳头),同样的,在RTGUI里面如果要显示一个东西,最基本条件是有这么个东西让你去显示,而这个东西表现出来的本质就是一个RTGUI WIDGET。在RTGUI里面,你可能看到了很多的控件,Button,label,或者listview,觉得他们功能不一样,显示效果也不一样,但是实际上他们也仅仅是一个RTGUI WIDGET而已。
RTGUI 大部分结构都用了一种类似于C++的面向对象的派生继承结构,因此能不能看懂rtgui里面的widget结体架构实际上可以区分一个人对C的认识深度,一般来说很多人用C工作了两三年,但是仍然可能看不懂RTGUI widget的结构,因为不是所有人都会接触到这样的一个知识点,当然,我也没有接触到,只不过是多习惯多查东西,所以自然也看懂的东西会多些。
呃,具体什么叫面向对象(Object Oriented)呢,具体还是自己去谷歌百度,它们讲比较官方些,我们这样的草根还是不要乱下定义了。具体什么叫做结构继承,相信各位大侠也是胸有成足了同,毕竟C语言的书看了那么多了。那么简单知识就略过,说下RTGUI 的派生。
struct s1{
type a;
type b;
type c;
};
Struct s2{
Struct s1 s;
Type e;
Type d;
};
类似上面的例子已经在很多地方看到很多遍了,相信大家都不陌生了,无非就是当把一个结构体s1完整放在另一个结构体s2的第一个参数位置,那么利用s2的地址指针就可以通过强制类型转换来安全的访问结构体s1的内容。而这个就是派生的本质,通过把各个小模块当成内存块放到一个结构体里面,那么通过这个结构体是可以获得这些属性具体值。而这个恰恰就是RTGUI _WIDGET能构成控件的原因,是通过将一些基本属性,比如字体、字体对齐、前景色、背景色、父窗口、事件处理等等很多基本的属性都放在了这个struct rtgui_widget里面,同时利用这个结构来派生各个具备功能的控件,比如按钮等,于是不管是按钮还是其他控件,对一般的操作,比如显示、隐藏等,都能通过指针来访问struct rtgui_widget的内存从而实现统一的接口操作(这个是很重要的,统一的接口操作能非常有效的提高代码重用率,一个代码只需要复制粘贴就能在很多地方用了)。
比如下面的代码
struct rtgui_tag
{
struct rtgui_widget parent;
/* label */
char* Text;
rtgui_image_t * Image;
rt_uint16_t Style;
rtgui_color_t FontColor;
rt_uint16_t Flag; //是否显示图
};
从基本的widget类派生出一个控件(当然,真实情况不仅仅是这样,后面可能会说下RTGUI_WIDGET 的真正派生情况,是一个很难的地方)
void rtgui_draw_tag(rtgui_tag_t *me) {
/* draw label */
struct rtgui_dc *dc;
struct rtgui_rect rect,rect1;
rtgui_color_t bc, fc;
rtgui_widget_t *widget;
widget = RTGUI_WIDGET(me);
//if (RTGUI_WIDGET_IS_HIDE(widget)) return;
/* begin drawing */
if ((widget->min_height < 2) || (widget->min_width < 2)) return;
dc = rtgui_dc_begin_drawing(widget);
if (dc == RT_NULL) return;
bc = RTGUI_WIDGET_BACKGROUND(widget);
fc = RTGUI_WIDGET_FOREGROUND(widget);
rtgui_widget_get_rect(widget, &rect1);
rect = rect1;
if ((me->Style < 10) && (me->Style > 0)) {
rtgui_widget_rect_to_device(widget, &rect1);
GuiBackGroundUpdate(dc, &rect1, &rect);
}
//rtgui_widget_get_rect(widget, &rect);
if ((me->Flag & GUI_FLAG_SHOW_IMAGE) && (me->Image != RT_NULL)) {
rtgui_rect_t image_rect;
image_rect.x1 = 0; image_rect.y1 = 0;
image_rect.x2 = me->Image->w;
image_rect.y2 = me->Image->h;
rtgui_rect_moveto_align(&rect, &image_rect,
RTGUI_ALIGN_CENTER_HORIZONTAL
| RTGUI_ALIGN_CENTER_VERTICAL);
rtgui_image_blit(me->Image, dc, &image_rect);
} else if (me->Style == 0) {
rtgui_dc_fill_rect(dc, &rect);
} else if (me->Style == 1) {
rtgui_dc_fill_rect(dc, &rect);
rtgui_dc_draw_rect(dc, &rect);
}
/* 拷贝背景覆盖原来的*/
//else if ((label->ubStyle & 0x0f) == 1) {
// rtgui_dc_fill_rect(dc, &rect);
//}
/* 拷贝背景覆盖原来的+画方框*/
// else if ((label->ubStyle & 0x0f) == 2) {
// rtgui_dc_fill_rect(dc, &rect);
// rtgui_dc_draw_rect(dc, &rect);
// }
/*刷背景色+画方框*/
//else {
// rtgui_widget_rect_to_device(RTGUI_WIDGET(label), &rect);
// rt_hw_lcd_background(&rect);
//rtgui_widget_get_rect(RTGUI_WIDGET(label), &rect);
//}
RTGUI_WIDGET_FOREGROUND(widget) = me->FontColor;
rtgui_widget_get_rect(widget, &rect);
/* default left and center draw */
if ((me->Flag & GUI_FLAG_SHOW_TEXT) && (me->Text != RT_NULL)) {
rtgui_dc_draw_text(dc, me->Text, &rect);
}
RTGUI_WIDGET_BACKGROUND(widget) = bc;
RTGUI_WIDGET_FOREGROUND(widget) = fc;
/* 拷贝到主显示区*/
//rtgui_widget_rect_to_device(RTGUI_WIDGET(label), &rect);
//rt_hw_lcd_update(&rect);
/* end drawing */
rtgui_dc_end_drawing(dc);
}
通过强制类型转换取得它的基本属性进行绘制,根据这个原理,那么界面是什么样,是完全由你自己决定的事情了。
很多都抱怨RTGUI的代码都是一大堆指针指来指去,不知道是什么东西,完全看不懂。但是其实它都只是针对不同情况读取不同子类的值来判断、设置状态并处理相应事件而已,如果认识到这一点,那对着指针只要知道指针针对的是什么结构什么参数就很好理解代码的功能了。个人建议不要过于纠结这些代码,除非你真想专注学习那些用法。
这篇不会什么实质内容,注定只是个坑,下次讲下object,关于控件事件,希望能写好,不喜勿喷。