GtkWidget的基本结构是这样的:
typedef
struct
{
GtkStyle *GSEAL
( style
) ;
GtkRequisition GSEAL
( requisition
) ;
GtkAllocation GSEAL
( allocation
) ;
GdkWindow *GSEAL
( window
) ;
GtkWidget *GSEAL
( parent
) ;
} GtkWidget;
其中最重要的是它的window属性,每个GtkWidget都必须有一个window。Widget是围绕着window转的,只有有了 window,Widget的存在才有意义。
要注意这里的window是一个GdkWindow,而不是GtkWindow。GdkWindow是对X的window的封装,大致上是屏幕上的 一块矩形区域,可以在上面画画,可以接收事件。
一个Widget从创建、显示到销毁,大致要经过这么几个过程:
1、创建(new)
这是调用gtk_xxx_new时所触发的。它干的活很简单,用gobject的对象系统创建一个相应widget的实例。
当创建实例时,gobject会自动调用指定的初始化(init)函数(在get_type时指定),init函数负责把widget的各字段都初始化 (把标题文字什么的设为NULL之类的)。
注意此时window并没有被创建,其实只是有了个widget的架子而已。
创建之后就可以对widget进行各种属性的设置了。
2、实例化(realize)
实例化的过程,就是将window创建出来的过程。这其中包括几个阶段:
询问大小请求(size_request)
GTK在实例化一个widget之前,会询问这个widget希望的大小是多大。widget可以根据自己的情况(例如属性什么的),计算出自己所 需要的大小,也可以返回一个默认值,反正就是widget自己定啦:)。
分配大小(size_allocate):
GTK获得大小请求后就会给widget分配一个大小。要注意的是分配的大小不一定和请求的大小相同。一般来说,在分配大小时widget需要做几 件事。
将分配的大小记录在自己的allocation中。
如果自己的window已经创建了,那么要改变自己所拥有的window的大小,使之符合所分配的大小。
如果widget是一个容器(container),那么对其所有的子widget也要相应地计算它们的大小并重新给它们分配大小。
分配大小可能发生在实例化之前,也可能在实例化后因为所属容器的大小、位置发生变化而被重新分配,因此widget的window可能已经被创建,也可能 是NULL,需要进行判断。
实例化
这才是真正的实例化阶段。实例化所需要做的事只有一个:用gdk_window_new创建window。创建好window后需要用 GTK_WIDGET_SET_FLAGS来给widget设置GTK_REALIZED标志。设置之后用GTK_REALIZED宏检查widget是 否已经被实例化时会返回TRUE,表示该widget已经被实例化了。
可以用gtk_widget_realize手动实例化一个widget
3、映射(map)
所谓映射,就是将已经创建好的window映射(显示)到屏幕上。需要做的事是用gdk_window_show将window给显示出来。和实例 化时类似,需要用GTK_WIDGET_SET_FLAGS设置GTK_MAPPED标志,表示已经映射好了。
要注意的是map时需要判断widget是否已经实例化(用GTK_REALIZED),如果没有,应该首先实例化widget,这样才能显示 window。
同样可以用gtk_widget_map手动映射一个widget。
用gtk_widget_show来显示一个widget的本质,就是将widget实例化,并将其映射。当然每一步都要判断是否已经做过,重复实 例化和映射会造成资源泄漏(window被多次创建)和其他问题。
以上就是一个widget从创建到显示的过程。当然其中还有其父widget的流程。一个widget当且仅当其父widget被实例化后才能实例 化,映射亦然(放心,这个流程是GTK+自动判断的)
接下来就是销毁一个widget时要做的事了。
4、反映射(unmap)
当隐藏一个widget时,其实就是取消这个widget的映射。具体做法是用gtk_window_hide来隐藏window,并用 GTK_WIDGET_UNSET_FLAGS来取消(GTK_MAPPED)。
5、反实例化(unrealize)
销毁一个widget之前会自动要求将其反实例化。反实例化就是将window给销毁(记得把window指针设回NULL),并取消 (GTK_REALIZED)标志。
有时可能会需要用gtk_widget_unrealize来手动反实例化一个widget。
6、销毁(destroy)
和new对应,把剩下的资源释放,最后用gobject的相应函数释放整个widget
下面是取自GtkEntry中的典型代码:
创建:
GtkWidget*
gtk_entry_new
(
void
)
{
/* 返回类型为GTK_TYPE_ENTRY的对象(Gobject的工作) */
return g_object_new
( GTK_TYPE_ENTRY,
NULL
) ;
}
/* 初始化函数,在g_object_new时自动调用 */
static
void
gtk_entry_init
( GtkEntry *entry
)
{
GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE
( entry
) ;
/* 设置widget标识 */
GTK_WIDGET_SET_FLAGS
( entry, GTK_CAN_FOCUS
) ;
/* 初始化各字段 */
entry->text_size = MIN_SIZE;
entry->text = g_malloc
( entry->text_size
) ;
entry->text
[
0
] =
'/0 ' ;
/* …… */
/* 设置拖放 */
gtk_drag_dest_set
( GTK_WIDGET
( entry
) ,
GTK_DEST_DEFAULT_HIGHLIGHT,
NULL ,
0 ,
GDK_ACTION_COPY | GDK_ACTION_MOVE
) ;
gtk_drag_dest_add_text_targets
( GTK_WIDGET
( entry
)
) ;
/* 输入法context */
entry->im_context = gtk_im_multicontext_new
(
) ;
/* 信号 */
g_signal_connect
( entry->im_context,
"commit" ,
G_CALLBACK
( gtk_entry_commit_cb
) , entry
) ;
/* …… */
}
大小分配:
static
void
gtk_entry_size_allocate
( GtkWidget *widget,
GtkAllocation *allocation
)
{
GtkEntry *entry = GTK_ENTRY
( widget
) ;
/* 保存到allocation中 */
widget->allocation = *allocation;
/* 判断是否实例化 */
if
( GTK_WIDGET_REALIZED
( widget
)
)
{
/* 计算窗口大小…… */
/* 改变窗口大小 */
gdk_window_move_resize
( widget->window, x, y, width, height
) ;
/* …… */
}
}
大小请求:
static
void
gtk_entry_size_request
( GtkWidget *widget,
GtkRequisition *requisition
)
{
/* 计算所需大小…… */
/* 设置所城大小 */
if
( entry->width_chars <
0
)
requisition->width = MIN_ENTRY_WIDTH + xborder *
2 + inner_border.
left + inner_border.
right ;
else
{
/* …… */
requisition->width = char_pixels * entry->width_chars + xborder *
2 + inner_border.
left + inner_border.
right ;
}
requisition->height = PANGO_PIXELS
( entry->ascent + entry->descent
) + yborder *
2 + inner_border.
top + inner_border.
bottom ;
/* …… */
}
实例化:
static
void
gtk_entry_realize
( GtkWidget *widget
)
{
/* …… */
/* 设置标志 */
GTK_WIDGET_SET_FLAGS
( widget, GTK_REALIZED
) ;
/* …… */
/* 创建window */
widget->window = gdk_window_new
( gtk_widget_get_parent_window
( widget
) , &attributes, attributes_mask
) ;
gdk_window_set_user_data
( widget->window, entry
) ;
/* …… */
}
映射:
static
void
gtk_entry_map
( GtkWidget *widget
)
{
/* …… */
/* 判断是否可以且需要显示 */
if
( GTK_WIDGET_REALIZED
( widget
) && !GTK_WIDGET_MAPPED
( widget
)
)
{
/* 调用父类的map函数,也就是GtkWidget的,这样就不用自己设置GTK_MAPPED和显示widget->window了 */
GTK_WIDGET_CLASS
( gtk_entry_parent_class
) ->map
( widget
) ;
/* …… */
/* 显示需要显示的window */
gdk_window_show
( icon_info->window
) ;
/* …… */
}
反映射:
static
void
gtk_entry_unmap
( GtkWidget *widget
)
{
/* …… */
/* 判断是否需要隐藏 */
if
( GTK_WIDGET_MAPPED
( widget
)
)
{
/* …… */
/* 隐藏需要显示的window */
gdk_window_hide
( icon_info->window
) ;
/* …… */
/* 调用父类的unmap函数,也就是GtkWidget的,这样就不用自己取消GTK_MAPPED和隐藏widget->window了 */
GTK_WIDGET_CLASS
( gtk_entry_parent_class
) ->unmap
( widget
) ;
}
}
反实例化:
static
void
gtk_entry_unrealize
( GtkWidget *widget
)
{
/* …… */
/* 调用父类的unrealize函数来销毁widget->window和取消GTK_REALIZED标识 */
GTK_WIDGET_CLASS
( gtk_entry_parent_class
) ->unrealize
( widget
) ;
/* …… */
}
销毁:
static
void
gtk_entry_destroy
( GtkObject *object
)
{
/* 销毁为成员分配的空间…… */
/* 用父类的object销毁函数自动调用gobject来销毁 */
GTK_OBJECT_CLASS
( gtk_entry_parent_class
) ->destroy
( object
) ;
}