GTK Widget中內(nèi)部結(jié)構(gòu)與工作流程
GTK Widget中內(nèi)部結(jié)構(gòu)與工作流程是本文要介紹的內(nèi)容,主要是來了解并學習GTK Widget的流程,具體內(nèi)容的實現(xiàn)來看本文詳解。
Gtk Widget的基本結(jié)構(gòu)是這樣的:
- typedef struct {
- GtkStyle *GSEAL (style);
- GtkRequisition GSEAL (requisition);
- GtkAllocation GSEAL (allocation);
- GdkWindow *GSEAL (window);
- GtkWidget *GSEAL (parent);
- } GtkWidget
;
其中最重要的是它的window屬性,每個GtkWidget都必須有一個window。Widget是圍繞著window轉(zhuǎn)的,只有有了window,Widget的存在才有意義。
要注意這里的window是一個GdkWindow,而不是GtkWindow。GdkWindow是對X的window的封裝,大致上是屏幕上的一塊矩形區(qū)域,可以在上面畫畫,可以接收事件。
一個Widget從創(chuàng)建、顯示到銷毀,大致要經(jīng)過這么幾個過程:
1、創(chuàng)建(new)
這是調(diào)用gtk_xxx_new時所觸發(fā)的。它干的活很簡單,用gobject的對象系統(tǒng)創(chuàng)建一個相應(yīng)widget的實例。
當創(chuàng)建實例時,gobject會自動調(diào)用指定的初始化(init)函數(shù)(在get_type時指定),init函數(shù)負責把widget的各字段都初始化(把標題文字什么的設(shè)為NULL之類的)。
注意此時window并沒有被創(chuàng)建,其實只是有了個widget的架子而已。
創(chuàng)建之后就可以對widget進行各種屬性的設(shè)置了。
2、實例化(realize)
實例化的過程,就是將window創(chuàng)建出來的過程。這其中包括幾個階段:
詢問大小請求(size_request)
GTK在實例化一個widget之前,會詢問這個widget希望的大小是多大。widget可以根據(jù)自己的情況(例如屬性什么的),計算出自己所需要的大小,也可以返回一個默認值,反正就是widget自己定啦:)。
分配大小(size_allocate):
GTK獲得大小請求后就會給widget分配一個大小。要注意的是分配的大小不一定和請求的大小相同。一般來說,在分配大小時widget需要做幾件事。
將分配的大小記錄在自己的allocation中。
如果自己的window已經(jīng)創(chuàng)建了,那么要改變自己所擁有的window的大小,使之符合所分配的大小。
如果widget是一個容器(container),那么對其所有的子widget也要相應(yīng)地計算它們的大小并重新給它們分配大小。
分配大小可能發(fā)生在實例化之前,也可能在實例化后因為所屬容器的大小、位置發(fā)生變化而被重新分配,因此widget的window可能已經(jīng)被創(chuàng)建,也可能是NULL,需要進行判斷。
實例化
這才是真正的實例化階段。實例化所需要做的事只有一個:用gdk_window_new創(chuàng)建window。創(chuàng)建好window后需要用 GTK_WIDGET_SET_FLAGS來給widget設(shè)置GTK_REALIZED標志。設(shè)置之后用GTK_REALIZED宏檢查widget是 否已經(jīng)被實例化時會返回TRUE,表示該widget已經(jīng)被實例化了。
可以用gtk_widget_realize手動實例化一個widget
3、映射(map)
所謂映射,就是將已經(jīng)創(chuàng)建好的window映射(顯示)到屏幕上。需要做的事是用gdk_window_show將window給顯示出來。和實例化時類似,需要用GTK_WIDGET_SET_FLAGS設(shè)置GTK_MAPPED標志,表示已經(jīng)映射好了。
要注意的是map時需要判斷widget是否已經(jīng)實例化(用GTK_REALIZED),如果沒有,應(yīng)該首先實例化widget,這樣才能顯示window。
同樣可以用gtk_widget_map手動映射一個widget。
用gtk_widget_show來顯示一個widget的本質(zhì),就是將widget實例化,并將其映射。當然每一步都要判斷是否已經(jīng)做過,重復(fù)實例化和映射會造成資源泄漏(window被多次創(chuàng)建)和其他問題。
以上就是一個widget從創(chuàng)建到顯示的過程。當然其中還有其父widget的流程。一個widget當且僅當其父widget被實例化后才能實例化,映射亦然(放心,這個流程是GTK+自動判斷的)
接下來就是銷毀一個widget時要做的事了。
4、反映射(unmap)
當隱藏一個widget時,其實就是取消這個widget的映射。具體做法是用gtk_window_hide來隱藏window,并用GTK_WIDGET_UNSET_FLAGS來取消(GTK_MAPPED)。
5、反實例化(unrealize)
銷毀一個widget之前會自動要求將其反實例化。反實例化就是將window給銷毀(記得把window指針設(shè)回NULL),并取消(GTK_REALIZED)標志。
有時可能會需要用gtk_widget_unrealize來手動反實例化一個widget。
6、銷毀(destroy)
和new對應(yīng),把剩下的資源釋放,最后用gobject的相應(yīng)函數(shù)釋放整個widget
下面是取自GtkEntry中的典型代碼:
創(chuàng)建:
- GtkWidget*
- gtk_entry_new (void)
- {
- /* 返回類型為GTK_TYPE_ENTRY的對象(Gobject的工作) */
- return g_object_new (GTK_TYPE_ENTRY, NULL);
- }
- /* 初始化函數(shù),在g_object_new時自動調(diào)用 */
- static void
- gtk_entry_init (GtkEntry *entry)
- {
- GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry);
- /* 設(shè)置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';
- /* …… */
- /* 設(shè)置拖放 */
- 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)
- {
- /* 計算所需大小…… */
- /* 設(shè)置所城大小 */
- 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)
- {
- /* …… */
- /* 設(shè)置標志 */
- GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
- /* …… */
- /* 創(chuàng)建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))
- {
- /* 調(diào)用父類的map函數(shù),也就是GtkWidget的,這樣就不用自己設(shè)置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);
- /* …… */
- /* 調(diào)用父類的unmap函數(shù),也就是GtkWidget的,這樣就不用自己取消GTK_MAPPED和隱藏widget->window了 */
- GTK_WIDGET_CLASS (gtk_entry_parent_class)->unmap (widget);
- }
- }
反實例化:
- static void
- gtk_entry_unrealize (GtkWidget *widget)
- {
- /* …… */
- /* 調(diào)用父類的unrealize函數(shù)來銷毀widget->window和取消GTK_REALIZED標識 */
- GTK_WIDGET_CLASS (gtk_entry_parent_class)->unrealize (widget);
- /* …… */
- }
銷毀:
- static void
- gtk_entry_destroy (GtkObject *object)
- {
- /* 銷毀為成員分配的空間…… */
- /* 用父類的object銷毀函數(shù)自動調(diào)用gobject來銷毀 */
- GTK_OBJECT_CLASS (gtk_entry_parent_class)->destroy (object);
- }
小結(jié):GTK Widget中內(nèi)部結(jié)構(gòu)與工作流程的內(nèi)容介紹完了,希望通過GTK Widget內(nèi)容的學習能對你有所幫助!