From: developer.gnome.org/doc/GGAD/z57.html
Fully understanding GTK+ requires some minimal understanding of the X Window System. This book assumes you have a user-level understanding---you know what an X server is, that X is network transparent, what a window manager does, and so on. A few more details are needed to write programs, however.
One detail is particularly important: the X Window System maintains a tree of windows. "Window" in this sense refers to an X window, not a GtkWindow---GtkWindow is a GTK+-specific concept, a widget that corresponds to an application's toplevel X window. An X window is not the user-visible concept "window" represented by GtkWindow; rather, it's an abstraction used by the X server to partition the screen. The "background" displayed by your X server is the root window; the root window has no parent. Application windows are typically near-children of the root window; most window managers create a child of the root window to hold the window's titlebar and other decorations, and place the application window inside. Window managers have total control over application windows---they can reposition them, reparent them, and iconify them at will. Application windows can in turn contain subwindows, which are controlled by the application. Note that GTK+ uses the GDK library, rather than using X directly; in GDK, there is a thin X window wrapper called GdkWindow. Don't confuse GdkWindow and GtkWindow.
An X window, or a GdkWindow, gives the X server hints about the structure of the graphics being displayed. Since X is network transparent, this helps reduce network traffic. The X server knows how to show windows on the screen; hide them; move them around (keeping children in position relative to their parents); capture events such as mouse movements on a per-window basis; and so on. A GdkWindow is also the fundamental unit for drawing graphics---you can't draw to "the screen" as a whole, you must draw on a GdkWindow.
Most GTK+ widgets have a corresponding GdkWindow. There are exceptions, such as GtkLabel; these are referred to as "no window widgets," and are relatively lightweight. Widgets with no associated GdkWindow draw into their parent's GdkWindow. Some operations, such as capturing events, require a GdkWindow; thus they are impossible on no-window widgets.
Widgets pass through a number of states related to their GdkWindow:
-
A widget is said to be realized if its corresponding GdkWindow has been created. Widgets are realized via gtk_widget_realize(), and unrealized via gtk_widget_unrealize(). Since an X window must have a parent, if a widget is realized its parent must also be.
-
A widget is mapped if gdk_window_show() has been called on its GdkWindow. This means the server has been asked to display the window on the screen; obviously the GdkWindow must exist, implying that the widget is realized.
-
A widget is visible if it will automatically be mapped when its parent is mapped. This means that gtk_widget_show() has been called on the widget. A widget can be rendered invisible by calling gtk_widget_hide(); this will either unschedule the pending map, or unmap the widget (hide its GdkWindow). Since toplevel widgets have no parent, they are mapped as soon as they are shown.
In typical user code, you only need to call gtk_widget_show(); this implies realizing and mapping the widget as soon as its parent is realized and mapped. It's important to understand that gtk_widget_show() has no immediate effect, it merely schedules the widget to be shown. This means you don't have to worry about showing widgets in any particular order; it also means that you can't immediately access the GdkWindow of a widget. Sometimes you need to access the GdkWindow; in those cases you'll want to manually call gtk_widget_realize() to create it. gtk_widget_realize() will also realize a widget's parents, if appropriate. It's uncommon to need gtk_widget_realize(); if you find that you do, perhaps you are approaching the problem incorrectly.
Destroying a widget automatically reverses the entire sequence of events, recursively unrealizing the widget's children and the widget itself.
Figure 23 summarizes the functions discussed in this section.
Figure 24 summarizes macros for querying the states discussed in this section.
在屏幕上显示需要几个相关步骤。在调用 WIDGETNAME_new() 创建构件之后,如下几个函数需要用到:
你可能注意到后面的两个函数十分相似,都是负责在屏幕上绘制构件。实际上许多构件并不真正关心它们之间的不同。构件类里的默认 draw() 函数只是简单的为重绘区域产生一个暴露事件。然而,一些构件通过区分这两个函数可以减少操作。例如,如果一个构件有多个 X 窗口,因为暴露事件标识了暴露的窗口,它可以只重绘受影响的窗口,调用 draw() 是不可能这样的。
容器构件,即使它们自身并不关心这个差别,也不能简单的使用默认 draw() 函数,因为它的子构件可能需要注意这个差别。然而,在两个函数里重复绘制代码是一种浪费的。按惯例,构件有一个名为 WIDGETNAME_paint() 的函数做实际的绘制构件的工作,draw() 和 expose() 函数再调用它。