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创建出来的过程。这其中包括几个阶段:
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 voidgtk_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 voidgtk_entry_destroy (GtkObject *object) { /* 销毁为成员分配的空间…… */ /* 用父类的object销毁函数自动调用gobject来销毁 */ GTK_OBJECT_CLASS (gtk_entry_parent_class)->destroy (object); }