Skip to content
Stone Kang edited this page Jan 1, 2014 · 1 revision

控件

目前控件一共是15个,包括窗口控件,不包括widget抽象父类。 需要注意的是,在这篇文档(以及本项目的其他文档里)提及的“类”的概念可能存在理论上的错误,因为C语言里是没有“类”的,更不用说父类。但是由于之前的开发者想要用C语言的struct模拟“类”(也模拟得不好),所以就借用了这个概念。大家在做结构优化的时候如果觉得有必要,可以动手修改这部分的实现,改完后发pull request。

总论

首先需要说明一下(几乎)所有控件公有的特性。说“几乎”是因为,由于设计上很松散,所以不管什么公有特性,都总会有一些特殊的控件实现得不一样。

每个控件的起点都是<控件名>_init()函数,终点都是<控件名>_exit()函数。但后者几乎从来不使用,很多控件都在注释里写了原因:“用户应用程序退出之后,application 会释放这个控件所占的空间。”

用户在客户端程序里调用<控件名>_init()函数来获得一个指向新控件的指针。这个函数的参数在各个控件里实现都不一样,有的带有一个没什么用的id,有的需要提供必要的初始化数据。至于它们做的事情倒基本上差不多,也就是用malloc或者calloc申请一个新的结构体,然后给所有的成员赋上初值。这是一个可以进行结构优化的地方,我觉得比较好的一种方法是将这个函数分成两步,分别申请空间和初始化数据,然后把初始化数据的部分变成控件结构体的成员(函数指针),然后把申请空间和调用这个初始化函数的任务交给用户自己。

除了init()exit()函数以外,一般的控件都有一个处理消息的函数,<控件名>_default_callback(addr_t self, addr_t msg)。self就是指向控件自己的指针,表示要针对哪一个控件处理消息。addt_t是项目里定义的一种数据类型,其实就是typedef void *addr_t。在大多数控件的实现里都能看到,这个函数的主要部分就是一个switch,检查消息的类型,并执行对应的操作。具体的操作都是作为static函数写在每个控件的实现里的,其他控件或客户端程序都看不到。

从这个default_callback()函数的两个参数就能看出來,这是前人想要模仿“类”的一种尝试。在C++和其他许多面向对象的语言里,类的成员函数的参数里都带有一个指向对象自己的指针(C++里是隐式传递的this指针)。这个default_callback()里的第一个参数void *self目的就很明显,而且类型还是通用的指针void *,我想就是为了让“子类”的对象也能传进来,因为C语言不能像C++那样自动将子类向上映射到父类。在控件结构体里有一个callback函数指针,它一般在init()函数里被赋值为<控件名>_default_callback

大多数控件里还会有<控件名>_repaint()<控件名>_show()这两个函数,它们分别重绘和显示一个控件对象。不过很多控件都在这两个函数里直接转到default_callback()函数里去处理,因为那边已经有对应的处理方法了,不用重写一遍,而且这样还可以允许用户替换掉default_callback()

其他经常出现的函数还有<控件名>_set_bounds()。这个函数功能很一致,就是设置控件的边界,所以一般都直接调用了父类里的函数。如果把这个函数作为成员放到控件结构体里,那么连这一步都可以省掉,直接指向父类函数即可。

<控件名>_set_color()函数跟set_bounds()函数很像,功能一致,设置控件的颜色,所以一般也是直接调用父类函数。优化方法同上。

<控件名>_set_font()设置字体。不过有的控件没有字体(因为不用显示文字),这种情况在优化的时候,只要在没有字体的控件里指向NULL,或者写一个空函数即可。

<控件名>_init_with_default_style()函数

这个函数比较特别,所以另外开一个小节来讲。

功能上倒是没什么特别的,就是用一组默认的样式初始化一个对象。在几个月之前,这样一个小函数的代码就达到100行左右,而且绝大部分代码在所有控件里都是一样的。现在原来重复的代码基本上都移到widget父类里了,而具体控件里只进行它们特有的工作。

在具体的控件里,这个函数首先定义一下基本的参数,比如它特有的是哪些属性、配置文件的名字等,然后就调用父类里的widget_init_with_default_style()。父类会尝试读取配置文件,如果不存在,就使用控件传入的默认参数初始化,然后将控件的特殊参数返回,让控件自己去完成最后的特别初始化。

前面说的那个配置文件的优先级高于默认参数,如果有配置文件存在,默认参数就不会使用,这样用户就可以在配置文件里规定自己喜欢的样式。

widget父类里的widget_init_with_default_style()函数使用了config_parser模块,所以配置文件的格式是统一的。这部分倒是不那么重要,可以先略过。

Button

按钮,是最简单、又有实际功能的控件之一。

Checkbox

Dialog

Flowbox

Icon

Image View

Label

Panel

Radio Button

Scrollbar

Spinbox

Tab

Text Line

Timer

Window

其他优化说明

这一节说一下其他可以优化的地方。

C语言里的struct不能像C++的class那样规定某个成员为私有,用户无法使用;struct里的成员相当于全部是public的。C语言里要隐藏实现细节的话,比较好的方法是把struct的定义写到C文件里,头文件只写一行,比如struct button;。这样,在用户使用的时候,这个结构体就是不完整的,只能定义它的指针,不能出现实际结构体或使用它的成员。此时如果用户想在这个结构体上进行什么操作的话,就只能将这个指针传给库里的函数去做,只能进行规定的动作。