diff --git a/docs/reference/ChangeLog b/docs/reference/ChangeLog index 312324c1d6..ae48ef8f8f 100644 --- a/docs/reference/ChangeLog +++ b/docs/reference/ChangeLog @@ -1,3 +1,16 @@ +2008-10-29 Federico Mena Quintero + + * gtk/drawing-model.xml: New chapter for "The GTK+ Drawing Model". + + * gtk/Makefile.am (content_files): Add drawing-model.xml. + (HTML_IMAGES): Add a the images for that new chapter. + + * gtk/tmpl/gtkwidget.sgml: Link to the drawing model docs from the + descriptions of GTK_APP_PAINTABLE and GTK_DOUBLE_BUFFERED. + + * gtk/glossary.xml: Link to the drawing model docs on no-window + widgets. + 2008-10-27 Matthias Clasen * gtk/gtk-sections.txt: diff --git a/docs/reference/gtk/Makefile.am b/docs/reference/gtk/Makefile.am index 16155f4dcd..3669707a85 100644 --- a/docs/reference/gtk/Makefile.am +++ b/docs/reference/gtk/Makefile.am @@ -128,6 +128,7 @@ content_files = \ changes-2.0.sgml \ compiling.sgml \ directfb.sgml \ + drawing-model.xml \ glossary.xml \ migrating-checklist.sgml \ migrating-GtkAction.sgml \ @@ -301,6 +302,8 @@ HTML_IMAGES = \ $(srcdir)/images/combo-box.png \ $(srcdir)/images/combo-box-entry.png \ $(srcdir)/images/entry.png \ + $(srcdir)/images/figure-hierarchical-drawing.png \ + $(srcdir)/images/figure-windowed-label.png \ $(srcdir)/images/file-button.png \ $(srcdir)/images/filechooser.png \ $(srcdir)/images/font-button.png \ diff --git a/docs/reference/gtk/drawing-model.xml b/docs/reference/gtk/drawing-model.xml new file mode 100644 index 0000000000..1da700480c --- /dev/null +++ b/docs/reference/gtk/drawing-model.xml @@ -0,0 +1,587 @@ + + The GTK+ Drawing Model + + + This chapter describes the GTK+ drawing model in detail. If you + are interested in the procedure which GTK+ follows to draw its + widgets and windows, you should read this chapter; this will be + useful to know if you decide to implement your own widgets. This + chapter will also clarify the reasons behind the ways certain + things are done in GTK+; for example, why you cannot change the + background color of all widgets with the same method. + + +
+ Overview of the drawing model + + + Programs that run in a windowing system generally create + rectangular regions in the screen called + windows. Traditional windowing systems + do not automatically save the graphical content of windows, and + instead ask client programs to repaint those windows whenever it + is needed. For example, if a window that is stacked below other + windows gets raised to the top, then a client program has to + repaint the area that was previously obscured. When the + windowing system asks a client program to redraw part of a + window, it sends an exposure event to the + program for that window. + + + + Here, "windows" means "rectangular regions with automatic + clipping", instead of "toplevel application windows". Most + windowing systems support nested windows, where the contents of + child windows get clipped by the boundaries of their parents. + Although GTK+ and GDK in particular may run on a windowing + system with no such notion of nested windows, GDK presents the + illusion of being under such a system. A toplevel window may + contain many subwindows and sub-subwindows, for example, one for + the menu bar, one for the document area, one for each scrollbar, + and one for the status bar. In addition, controls that receive + user input, such as clickable buttons, are likely to have their + own subwindows as well. + + + + Generally, the drawing cycle begins when GTK+ receives an + exposure event from the underlying windowing system: if the + user drags a window over another one, the windowing system will + tell the underlying window that it needs to repaint itself. The + drawing cycle can also be initiated when a widget itself decides + that it needs to update its display. For example, when the user + types a character in a GtkEntry + widget, the entry asks GTK+ to queue a redraw operation for + itself. + + + + The following sections describe how GTK+ decides which widgets + need to be repainted, and how widgets work internally in terms + of the resources they use from the windowing system. + + +
+ Window and no-window widgets + + + A GdkWindow + represents a window from the underlying windowing system on which GTK+ + is running. For example, on X11 it corresponds to a + Window; on Win32, it corresponds to a HANDLE. + The windowing system generates events for these windows. The GDK + interface to the windowing system translates such native events into + GdkEvent + structures and sends them on to the GTK layer. In turn, the GTK layer + finds the widget that corresponds to a particular + GdkWindow and emits the corresponding event + signals on that widget. + + + + When the program needs to redraw a region of a + GdkWindow, GDK generates an event of + type GDK_EVENT_EXPOSE + for that window. The GTK+ widget layer in turn finds the + widget that corresponds to that window, and emits the expose-event signal + for that widget. + + + + In principle, each widget could have a + GdkWindow of its own. With such a + scheme, the drawing cycle would be trivial: when GDK notifies + the GTK layer about an exposure event for a + GdkWindow, the GTK layer would simply + emit the expose-event + signal for that widget. The widget's expose event + handler would subsequently repaint the widget. No further + work would be necessary; the windowing system would generate + exposure events for each window that needs it, and then each + corresponding widget would draw itself in turn. + + + + However, in practice it is convenient to have widgets which do + not have a GdkWindow of their own, but + rather share the one from their parent widget. Such widgets + have the GTK_NO_WINDOW widget flag turned on; this + can be tested easily with the GTK_WIDGET_NO_WINDOW() + macro. As such, these are called no-window + widgets. + + + + No-window widgets are useful for various reasons: + + + + + + Some widgets may want the parent's background to show through, even + when they draw on parts of it. For example, consider a theme that + uses textured backgrounds, such as gradients or repeating + patterns. If each widget had its own window, and in turn its own + gradient background, labels would look bad because there would be a + visible break with respect to their surroundings. shows this undesirable effect. + + +
+ Windowed label vs. no-window label + + +
+
+ + + + Reducing the number of windows creates less traffic between GTK+ and + the underlying windowing system, especially when getting events. + + +
+ + + On the other hand, widgets that would benefit from having a "hard" + clipping region may find it more convenient to create their own + windows. Also, widgets which want to receive events resulting from + user interaction may find it convenient to use windows of their own as + well. Widgets may have more than one window if they want to + define different regions for capturing events. + +
+ +
+ Hierarchical drawing + + + When the GTK layer receives an exposure event from GDK, it + finds the widget that corresponds to the window which received + the event. By definition, this corresponds to a widget that + has the GTK_NO_WINDOW flag turned + off (otherwise, the widget wouldn't own + the window!). First this widget paints its background, and + then, if it is a container widget, it tells each of its + GTK_NO_WINDOW children to paint + themselves. This process is applied recursively for all the + GTK_NO_WINDOW descendants of the original + widget. + + + + Note that this process does not get propagated to widgets + which have windows of their own, that is, to widgets which + have the GTK_NO_WINDOW flag turned off. + If such widgets require redrawing, then the windowing system + will already have sent exposure events to their corresponding + windows. As such, there is no need to + propagate the exposure to them on the + GTK+ side. + + + + shows how a simple toplevel window would + paint itself when it contains only GTK_NO_WINDOW descendants: + + + + + The outermost, thick rectangle is a toplevel GtkWindow, + which is not a GTK_NO_WINDOW widget — + as such, it does receive its exposure event as it comes from GDK. + First the GtkWindow would paint its own + background. Then, it would ask its only child to paint itself, + numbered 2. + + + + + The dotted rectangle represents a GtkVBox, which + has been made the sole child of the + GtkWindow. Boxes are just layout + containers that do not paint anything by themselves, so this + GtkVBox would draw nothing, but rather ask + its children to draw themselves. The children are numbered 3 and + 6. + + + + + The thin rectangle is a GtkFrame, + which has two children: a label for the frame, numbered 4, and + another label inside, numbered 5. First the frame would draw its + own beveled box, then ask the frame label and its internal child to + draw themselves. + + + + + The frame label has no children, so it just draws its text: "Frame Label". + + + + + The internal label has no children, so it just draws its text: "This + is some text inside the frame!". + + + + + The dotted rectangle represents a GtkHBox. Again, + this does not draw anything by itself, but rather asks its children + to draw themselves. The children are numbered 7 and 9. + + + + + The thin rectangle is a GtkButton with + a single child, numbered 8. First the button would draw its + beveled box, and then it would ask its child to draw itself. + + + + + This is a text label which has no children, so it just draws its + own text: "Cancel". + + + + + Similar to number 7, this is a button with a single child, numbered + 10. First the button would draw its beveled box, and then it would + ask its child to draw itself. + + + + + Similar to number 8, this is a text label which has no children, + so it just draws its own text: "OK". + + + + + +
+ Hierarchical drawing order + + +
+ + + To avoid the flickering that would result from each widget drawing + itself in turn, GTK+ uses a double-buffering mechanism. The following + sections describe this mechanism in detail. + +
+ +
+ Notes on drawing no-window widgets + + + Remember that the coordinates in a GdkEventExpose are relative to + the GdkWindow that received the event, + not to the widget whose expose-event + handler is being called. If your widget owns the window, then + these coordinates are probably what you expect. However, if + you have a GTK_NO_WINDOW widget that + shares its parent's window, then the event's coordinates will + be offset by your widget's allocation: remember that the + allocation is always relative to the parent + window of the widget, not to the parent + widget itself. + + + + For example, if you have a no-window widget whose allocation + is { x=5, y=6, + widthheight }, + then your drawing origin should be at (5, 6), not at + (0, 0). + +
+ +
+ Drawing over child windows + + + When you draw on a GdkWindow, your + drawing gets clipped by any child windows that it may + intersect. Sometimes you need to draw over your child windows + as well; for example, when drawing a drag-handle to resize + something. In this case, turn on the GDK_INCLUDE_INFERIORS + subwindow mode for the GdkGC which you use for + drawing. + +
+
+ +
+ Double buffering + + + When the GTK layer receives an exposure event from GDK, it first finds + the !GTK_NO_WINDOW widget that + corresponds to the event's window. Then, it emits the expose-event signal for that + widget. As described above, that widget will first draw its background, + and then ask each of its GTK_NO_WINDOW children to + draw themselves. + + + + If each of the drawing calls made by each subwidget's + expose-event handler were sent directly to the + windowing system, flicker could result. This is because areas may get + redrawn repeatedly: the background, then decorative frames, then text + labels, etc. To avoid flicker, GTK+ employs a double + buffering system at the GDK level. Widgets normally don't + know that they are drawing to an off-screen buffer; they just issue their + normal drawing commands, and the buffer gets sent to the windowing system + when all drawing operations are done. + + + + + + Two basic functions in GDK form the core of the double-buffering + mechanism: gdk_window_begin_paint_region() + and gdk_window_end_paint(). + The first function tells a GdkWindow to + create a temporary off-screen buffer for drawing. All + subsequent drawing operations to this window get automatically + redirected to that buffer. The second function actually paints + the buffer onto the on-screen window, and frees the buffer. + + +
+ Automatic double buffering + + + It would be inconvenient for all widgets to call + gdk_window_begin_paint_region() and + gdk_window_end_paint() at the beginning + and end of their expose-event handlers. + + + + To make this easier, most GTK+ widgets have the + GTK_DOUBLE_BUFFERED widget flag turned on by + default. When GTK+ encounters such a widget, it automatically + calls gdk_window_begin_paint_region() + before emitting the expose-event signal for the widget, and + then it calls gdk_window_end_paint() + after the signal has been emitted. This is convenient for + most widgets, as they do not need to worry about creating + their own temporary drawing buffers or about calling those + functions. + + + + However, some widgets may prefer to disable this kind of + automatic double buffering and do things on their own. To do + this, turn off the GTK_DOUBLE_BUFFERED + flag in your widget's constructor. + + + + Disabling automatic double buffering + + +static void +my_widget_init (MyWidget *widget) +{ + ... + + GTK_WIDGET_UNSET_FLAGS (widget, GTK_DOUBLE_BUFFERED); + + ... +} + + + + + When is it convenient to disable double buffering? Generally, + this is the case only if your widget gets drawn in such a way + that the different drawing operations do not overlap each + other. For example, this may be the case for a simple image + viewer: it can just draw the image in a single operation. + This would not be the case with a word + processor, since it will need to draw and over-draw the page's + background, then the background for highlighted text, and then + the text itself. + + + + Even if you turn off the + GTK_DOUBLE_BUFFERED flag on a widget, you + can still call + gdk_window_begin_paint_region() and + gdk_window_end_paint() by hand to use + temporary drawing buffers. + +
+
+ +
+ App-paintable widgets + + + Generally, applications use the pre-defined widgets in GTK+ and + they do not draw extra things on top of them (the exception + being GtkDrawingArea). However, + applications may sometimes find it convenient to draw directly + on certain widgets like toplevel windows or event boxes. When + this is the case, GTK+ needs to be told not to overwrite your + drawing afterwards, when the window gets to drawing its default + contents. + + + + GtkWindow and + GtkEventBox are the only two widgets + which will draw their default contents unless you turn on the + GTK_APP_PAINTABLE widget flag. If you turn on + this flag, then they will not draw their contents and let you do + it instead. + + + + The expose-event handler for GtkWindow is + implemented effectively like this: + + + +static gint +gtk_window_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + if (!GTK_WIDGET_APP_PAINTABLE (widget)) + gtk_paint_flat_box (widget->style, widget->window, GTK_STATE_NORMAL, + GTK_SHADOW_NONE, event->area, widget, "base", 0, 0, -1, -1); + + if (GTK_WIDGET_CLASS (gtk_window_parent_class)->expose_event) + return GTK_WIDGET_CLASS (gtk_window_parent_class)->expose_event (widget, event); + + return FALSE; +} + + + + The expose-event handler for GtkEventBox + is implemented in a similar fashion. + + + + Since the expose-event + signal runs user-connected handlers + before the widget's default handler, what + happens is this: + + + + + + Your own expose-event handler gets run. It paints something + on the window or the event box. + + + + + + The widget's default expose-event handler gets run. If + GTK_APP_PAINTABLE is turned off (this + is the default), your drawing will be + overwritten. If that flag is turned on, the + widget will not draw its default contents and preserve your + drawing instead. + + + + + + The expose-event handler for the parent class gets run. + Since both GtkWindow and + GtkEventBox are descendants of + GtkContainer, their no-window + children will be asked to draw themselves recursively, as + described in . + + + + + + Summary of app-paintable widgets + + + Turn on the GTK_APP_PAINTABLE flag if you + intend to draw your own content directly on a + GtkWindow and + GtkEventBox. You seldom need to draw + on top of other widgets, and + GtkDrawingArea ignores this flag, as it + is intended to be drawn on. + + +
+ +
+ TODO + + + + + APP_PAINTABLE: link the flag's documentation to this document. + + + + + DOUBLE_BUFFERED: link the flag's documentation to this document. + + + +
+
+ + diff --git a/docs/reference/gtk/glossary.xml b/docs/reference/gtk/glossary.xml index c402b3d660..1dce9c3dfc 100644 --- a/docs/reference/gtk/glossary.xml +++ b/docs/reference/gtk/glossary.xml @@ -197,7 +197,9 @@ draw its contents, but rather shares its parent's. Such a widget has the %GTK_NO_WINDOW flag set, and - can be tested with the GTK_WIDGET_NO_WINDOW() macro. + can be tested with the GTK_WIDGET_NO_WINDOW() macro. See + for a detailed + description of this flag. diff --git a/docs/reference/gtk/gtk-docs.sgml b/docs/reference/gtk/gtk-docs.sgml index de30afe550..e6aa20819c 100644 --- a/docs/reference/gtk/gtk-docs.sgml +++ b/docs/reference/gtk/gtk-docs.sgml @@ -96,6 +96,7 @@ that is, GUI components such as #GtkButton or #GtkTextView. + diff --git a/docs/reference/gtk/images/figure-hierarchical-drawing.png b/docs/reference/gtk/images/figure-hierarchical-drawing.png new file mode 100644 index 0000000000..a2acbc53d8 Binary files /dev/null and b/docs/reference/gtk/images/figure-hierarchical-drawing.png differ diff --git a/docs/reference/gtk/images/figure-windowed-label.png b/docs/reference/gtk/images/figure-windowed-label.png new file mode 100644 index 0000000000..74f398cb88 Binary files /dev/null and b/docs/reference/gtk/images/figure-windowed-label.png differ diff --git a/docs/reference/gtk/tmpl/gtkwidget.sgml b/docs/reference/gtk/tmpl/gtkwidget.sgml index df417d863c..3a7eb60898 100644 --- a/docs/reference/gtk/tmpl/gtkwidget.sgml +++ b/docs/reference/gtk/tmpl/gtkwidget.sgml @@ -939,13 +939,18 @@ Tells about certain properties of the widget. @GTK_APP_PAINTABLE: Set and unset by gtk_widget_set_app_paintable(). Must be set on widgets whose window the application directly draws on, - in order to keep GTK+ from overwriting the drawn stuff. + in order to keep GTK+ from overwriting the drawn stuff. See + for a detailed + description of this flag. @GTK_RECEIVES_DEFAULT: The widget when focused will receive the default action and have %GTK_HAS_DEFAULT set even if there is a different widget set as default. @GTK_DOUBLE_BUFFERED: Set and unset by gtk_widget_set_double_buffered(). - Indicates that exposes done on the widget should be double-buffered. + Indicates that exposes done on the widget should be + double-buffered. See for a + detailed discussion of how double-buffering works in GTK+ and + why you may want to disable it for special cases. @GTK_NO_SHOW_ALL: