gtk drawing model

来源:互联网 发布:獭祭 存米 知乎 编辑:程序博客网 时间:2024/06/06 21:41

GTK+ Drawing Model


Table of Contents

I. Part Title
1. The GTK+ Drawing Model
Overview of the drawing model
Window and no-window widgets
Hierarchical drawing
Double buffering
Automatic double buffering
App-paintable widgets
TODO

List of Figures

1.1. Windowed label vs. no-window label
1.2. Hierarchical drawing order

List of Examples

1.1. Disabling automatic double buffering

Part Title

Table of Contents

1. The GTK+ Drawing Model
Overview of the drawing model
Window and no-window widgets
Hierarchical drawing
Double buffering
Automatic double buffering
App-paintable widgets
TODO

Chapter�1.�The GTK+ Drawing Model

Table of Contents

Overview of the drawing model
Window and no-window widgets
Hierarchical drawing
Double buffering
Automatic double buffering
App-paintable widgets
TODO

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 GdkWindowrepresents a window from the underlying windowing system on which GTK+is running. For example, on X11 it corresponds to aWindow; on Win32, it corresponds to a HANDLE.The windowing system generates events for these windows. The GDKinterface to the windowing system translates such native events intoGdkEventstructures and sends them on to the GTK layer. In turn, the GTK layerfinds the widget that corresponds to a particularGdkWindow and emits the corresponding eventsignals on that widget.

When the program needs to redraw a region of aGdkWindow, GDK generates an event oftype GDK_EVENT_EXPOSEfor that window. FIXME: is the link to GDK_EVENT_EXPOSEcorrect? The GTK+ widget layer in turn finds the widget thatcorresponds to that window, and emits the expose-event signalfor that widget.

In principle, each widget could have aGdkWindow of its own. With such ascheme, the drawing cycle would be trivial: when GDK notifiesthe GTK layer about an exposure event for aGdkWindow, the GTK layer would simplyemit the expose-eventsignal for that widget. The widget's expose eventhandler would subsequently repaint the widget. No furtherwork would be necessary; the windowing system would generateexposure events for each window that needs it, and then eachcorresponding widget would draw itself in turn.

However, in practice it is convenient to have widgets which donot have a GdkWindow of their own, butrather share the one from their parent widget. Such widgetshave the GTK_NO_WINDOW widget flag turned on; thiscan be tested easily with the GTK_WIDGET_NO_WINDOW()macro. As such, these are called no-windowwidgets.

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. Figure�1.1, “Windowed label vs. no-window label” shows this undesirable effect.

    Figure�1.1.�Windowed label vs. no-window label

    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 ownwindows. Also, widgets which want to receive events resulting fromuser interaction may find it convenient to use windows of their own aswell.

Hierarchical drawing

When the GTK layer receives an exposure event from GDK, it finds thewidget that corresponds to the window which received the event. Bydefinition, this corresponds to a widget that isnot a GTK_NO_WINDOW widget.First this widget paints its background, and then, if it is a containerwidget, it tells each of its GTK_NO_WINDOWchildren to paint themselves. This process is applied recursively forall the GTK_NO_WINDOW descendants of the originalwidget.

Note that this process does not get propagated to widgets which havewindows of their own, that is, to widgets which donot have the GTK_NO_WINDOWflag turned on. If such widgets require redrawing, then the windowing systemwill already have sent exposure events to their corresponding windows.As such, there is no need to propagate theexposure to them on the GTK+ side.

Figure�1.2, “Hierarchical drawing order” shows how a simple toplevel window wouldpaint itself when it contains only GTK_NO_WINDOW descendants:

  1. 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.

  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.

  3. 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.

  4. The frame label has no children, so it just draws its text: "Frame�Label".

  5. The internal label has no children, so it just draws its text: "This is some text inside the frame!".

  6. 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.

  7. 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.

  8. This is a text label which has no children, so it just draws its own text: "Cancel".

  9. 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.

  10. Similar to number 8, this is a text label which has no children, so it just draws its own text: "OK".

 

Figure�1.2.�Hierarchical drawing order

Hierarchical drawing order

To avoid the flickering that would result from each widget drawingitself in turn, GTK+ uses a double-buffering mechanism. The followingsections describe this mechanism in detail.

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 doublebuffering 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 callgdk_window_begin_paint_region() andgdk_window_end_paint() at the beginningand end of their expose-event handlers.

To make this easier, most GTK+ widgets have theGTK_DOUBLE_BUFFERED widget flag turned on bydefault. When GTK+ encounters such a widget, it automaticallycalls gdk_window_begin_paint_region()before emitting the expose-event signal for the widget, andthen it calls gdk_window_end_paint()after the signal has been emitted. This is convenient formost widgets, as they do not need to worry about creatingtheir own temporary drawing buffers or about calling thosefunctions.

However, some widgets may prefer to disable this kind ofautomatic double buffering and do things on their own. To dothis, turn off the GTK_DOUBLE_BUFFEREDflag in your widget's constructor.

Example�1.1.�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 waythat the different drawing operations do not overlap eachother. For example, this may be the case for a simple imageviewer: it can just draw the image in a single operation.This would not be the case with a wordprocessor, since it will need to draw and over-draw the page'sbackground, then the background for highlighted text, and thenthe text itself.

Even if you turn off theGTK_DOUBLE_BUFFERED flag on a widget, youcan still callgdk_window_begin_paint_region() andgdk_window_end_paint() by hand to usetemporary 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 on certain widgets like toplevel windows. When this is the case, GTK+ needs to be told not to overwrite what you drew with the default contents for that window.

Two widgets in GTK+ pay attention to the GTK_APP_PAINTABLE widget flag: GtkWindow and GtkEventBox.

TODO

  • Change the glossary's "no-window" entry to point to the "window-no-window-widgets" section.

  • APP_PAINTABLE: gtk_window_expose(), tooltips, etc.

  • DOUBLE_BUFFERED: gtkmain.c:gtk_main_do_event().

  • How do you figure out the allocation of a no-window widget? Your offsets are not (0, 0).

  • How do you change the background of a label?

 
原创粉丝点击