GtkWidget的基本结构是这样的:
- typedef struct {
- GtkStyle *GSEAL (style);
- GtkRequisition GSEAL (requisition);
- GtkAllocation GSEAL (allocation);
- GdkWindow *GSEAL (window);
- GtkWidget *GSEAL (parent);
- } GtkWidget;
|
一个Widget从创建、显示到销毁,大致要经过这么几个过程:
1、创建(new)
2、实例化(realize)
实例化的过程,就是将window创建出来的过程。这其中包括几个阶段:
- 询问大小请求(size_request):
GTK在实例化一个widget之前,会询问这个widget希望的大小是多大。widget可以根据自己的情况(例如属性什么的),计算出自己所需要的大小,也可以返回一个默认值,反正就是widget自己定啦:)。 - 分配大小(size_allocate):
GTK获得大小请求后就会给widget分配一个大小。要注意的是分配的大小不一定和请求的大小相同。一般来说,在分配大小时widget需要做几件事。
1)将分配的大小记录在自己的allocation中。
2)如果自己的window已经创建了,那么要改变自己所拥有的window的大小,使之符合所分配的大小。
3)如果widget是一个容器(container),那么对其所有的子widget也要相应地计算它们的大小并重新给它们分配大小。
4)分配大小可能发生在实例化之前,也可能在实例化后因为所属容器的大小、位置发生变化而被重新分配,因此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)
4、反映射(unmap)
5、反实例化(unrealize)
6、销毁(destroy)
下面是取自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);
- }