Drawing in GTK+
Total Page:16
File Type:pdf, Size:1020Kb
CSci493.70 Graphical User Interface Programming Prof. Stewart Weiss Drawing in GTK+ Drawing in GTK+ Background In order to understand how to draw in GTK, you rst have to understand something about how GTK draws widgets, because how GTK draws widgets has an important role in how you design your drawing application. An understanding of how GTK draws widgets is also required if you ever plan to build your own custom widgets. Windows and Clipping Most windowing systems are designed around the idea that an application's visual display lies within a rectangular region on the screen called its window. The windowing system, e.g. Gnome or KDE or Explorer, does not automatically save the graphical content of an application's windows; instead it asks the application itself to repaint 1 its 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 that contains that window. An exposure event is simply an event sent from the underlying windowing system to a widget to notify it that it must redraw itself. In this context, a "window" means "a rectangular region with automatic clipping", not a top-level application window. Clipping is the act of removing portions of a window that do not need to be redrawn, or looked at the other way, it is determining which are the only regions of a window that must be redrawn. In Figure 1, window A is below window B, which is below window C. If the user does something that brings A forward on the screen, then the only region of window A that must be redrawn is the portion of A in the set A \ (B [ C), which is shaded in the gure. Figure 1: Window clipping. Most windowing systems support nested windows, which are called child windows. A top-level window may contain many child windows, which may contain child windows in turn, and so on. For example, a top-level window will typically contain a window for the menu bar, one for the document area, one for each scrollbar, 1We will use the terms draw and paint interchangeably. Technically, the operation we are describing is painting, because it is the application of colors and brush strokes to a canvas. Drawing is just a special kind of painting. All GUI APIs refer to the process of visually rendering their windows as painting, or repainting, even if they describe the process occasionally as drawing. We will keep with this tradition of equating the two terms. 1 CSci493.70 Graphical User Interface Programming Prof. Stewart Weiss Drawing in GTK+ and one for the status bar. In addition, controls that receive user input, such as clickable buttons, typically have their own subwindows. It is possible for GTK+ to run on a windowing system with no notion of nested windows. This is not a problem, because GDK presents the illusion of being under such a system, and therefore, in GTK+, nested windows are always available. The Drawing Cycle Generally, the drawing cycle begins when GTK+ receives an exposure event from the underlying windowing system, such as when the user drags a window over another one. In this case 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. In other words, a widget can call a function that requests that the windowing system itself send it an event to redraw itself! This simplies the program, as you will soon see. A GdkWindow represents a window from the underlying windowing system on which GTK+ is running. On X11 it corresponds to a (X) Window; on Win32, it corresponds to a HANDLE. It is the windowing system that generates events for these windows. These events are passed to the GDK interface, which translates these native events into GdkEvent structures and sends them on to the GTK layer. In turn, the GTK widget layer nds the widget that corresponds to a particular GdkWindow and emits the corresponding event signals on that widget. Widgets With and Without GdkWindows If every single widget had its own GdkWindow, the drawing cycle would be trivial: the underlying windowing system would notify GDK about a window that needed redrawing, GDK would pass that exposure event for the specic GdkWindow to the GTK layer, and the GTK layer would emit the expose-event signal for that widget. The widget's expose event handler would then repaint the widget. Nothing else would have to be done; the windowing system would generate exposure events for each window that needed it, and then each corresponding widget would draw itself in turn. However, in practice, there are reasons for which it is more convenient and ecient to allow widgets not to have a GdkWindow of their own, but to instead share the one from their parent widget. Every widget has a GTK_NO_WINDOW ag that indicates whether or not the widget has its own window. If a widget does not have its own window, it must inform GTK that it does not, when it is created, by setting this ag to true. (In GTK+-2.18 and later, the widget should call gtk_widget_set_has_window(), passing a false value in its second argument.) This is not something you would have to do as a programmer. It is the job of the widget's implementer to do this in the widget's constructor (its init method.) You, as a programmer, can test whether a particular widget has a GdkWindow of its own by inspecting the value of the GTK_NO_WINDOW ag or by calling the gtk_widget_get_has_window() method in GTK+-2.18 or later, which will return true if it has its own window2. Widgets that do not have their own GDK windows are called no-window widgets or GTK_NO_WINDOW widgets. Why would you want a widget to be window-less? There are two primary reasons. • Some widgets may want the parent's background to show through, even when they draw on parts of it. Such is the case, for instance, when a label is placed on a button with a themed texture. If each widget had its own window, and therefore its own background, labels would look bad because there would be a visible break with respect to their surroundings. • Reducing the total number of GDK windows reduces the volume of trac between GDK and GTK, so if a widget can be implemented without a window, it improves performance. 2You cannot just test whether the window member variable of the GdkWindow structure is NULL, because it may be sharing its parent's GdkWindow. 2 CSci493.70 Graphical User Interface Programming Prof. Stewart Weiss Drawing in GTK+ Hierarchical Drawing With this understanding, we can direct our attention to describing the sequence of steps that take place when GTK receives the exposure event. The very rst step is that GTK nds the widget that corresponds to the window that received the event. This widget cannot be a no-window widget, otherwise the widget wouldn't own the window that received the event. This widget begins by painting its background. Then, if it is a container widget, it tells each of its no-window children to paint themselves. This process is applied recursively for all the no-window descendants of the original widget. This process does not get propagated to widgets that have windows of their own (those for which GTK_NO_WINDOW is true). This is because if any of those widgets require redrawing, the windowing system will have already sent exposure events to their corresponding GDK windows. Thus, there is no need to propagate the exposure to them on the GTK+ side. An Example (from the GTK+ API Documentation) The following example is from the online Gnome/GTK+ documentation, The GTK+ Drawing Model, at http://library.gnome.org/devel/gtk/unstable/chap-drawing-model.html. It demonstrates how a top-level window would repaint itself when it has only no-window widget children. Figure 2: Hierarchical redrawing of a window 1. The outermost, thick rectangle is a top-level GtkWindow, which is not a GTK_NO_WINDOW widget therefore, 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, which is the dotted-rectangle numbered 2. 2. The dotted rectangle represents a GtkVBox, which has been made the sole child of the GtkWindow. GtkBoxes, which are no-window widgets, 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, also a no-window widget, 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.