forked from AuroraMiddleware/gtk
541 lines
20 KiB
XML
541 lines
20 KiB
XML
<?xml version="1.0"?>
|
|
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
|
|
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
|
|
]>
|
|
<refentry id="chap-drawing-model">
|
|
<refmeta>
|
|
<refentrytitle>The GTK+ Drawing Model</refentrytitle>
|
|
<manvolnum>3</manvolnum>
|
|
<refmiscinfo>GTK Library</refmiscinfo>
|
|
</refmeta>
|
|
|
|
<refnamediv>
|
|
<refname>The GTK+ Drawing Model</refname>
|
|
<refpurpose>
|
|
The GTK+ drawing model in detail
|
|
</refpurpose>
|
|
</refnamediv>
|
|
|
|
|
|
<refsect1 id="drawing-overview">
|
|
<title>Overview of the drawing model</title>
|
|
|
|
<para>
|
|
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.
|
|
</para>
|
|
|
|
<para>
|
|
Programs that run in a windowing system generally create
|
|
rectangular regions in the screen called
|
|
<firstterm>windows</firstterm>. 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 <firstterm>exposure event</firstterm> to the
|
|
program for that window.
|
|
</para>
|
|
|
|
<para>
|
|
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.
|
|
</para>
|
|
|
|
<para>
|
|
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 <link
|
|
linkend="GtkEntry"><classname>GtkEntry</classname></link>
|
|
widget, the entry asks GTK+ to queue a redraw operation for
|
|
itself.
|
|
</para>
|
|
|
|
<para>
|
|
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.
|
|
</para>
|
|
|
|
<para>
|
|
A <link linkend="GdkWindow"><classname>GdkWindow</classname></link>
|
|
represents a window from the underlying windowing system on which GTK+
|
|
is running. For example, on X11 it corresponds to a
|
|
<type>Window</type>; on Win32, it corresponds to a <type>HANDLE</type>.
|
|
The windowing system generates events for these windows. The GDK
|
|
interface to the windowing system translates such native events into
|
|
<link linkend="GdkEvent"><structname>GdkEvent</structname></link>
|
|
structures and sends them on to the GTK layer. In turn, the GTK layer
|
|
finds the widget that corresponds to a particular
|
|
<classname>GdkWindow</classname> and emits the corresponding event
|
|
signals on that widget.
|
|
</para>
|
|
|
|
<refsect2 id="emission of the draw event">
|
|
<title>Emission of the draw event</title>
|
|
|
|
<para>
|
|
When the program needs to redraw a region of a
|
|
<classname>GdkWindow</classname>, generates an event of
|
|
type <link
|
|
linkend="GDK_EVENT_EXPOSE"><constant>GDK_EVENT_EXPOSE</constant></link>
|
|
for that window, specifying the region to redraw in the process.
|
|
</para>
|
|
|
|
<para>
|
|
When generating the event, GDK also sets up double buffering to
|
|
avoid the flickering that would result from each widget drawing
|
|
itself in turn. <xref linkend="double-buffering"/> describes
|
|
the double buffering mechanism in detail.
|
|
</para>
|
|
|
|
<para>
|
|
When the GTK+ widget layer receives the event, it finds the widget that
|
|
corresponds to the window, and causes it to render itself using the
|
|
widget's #GtkWidget::draw signal. For this purpose it creates a
|
|
<link linkend="#cairo_t">cairo context</link>. It then clips the context
|
|
to the area that needs to be drawn. This makes sure that the minimal
|
|
amount of work is done if only a small part of the widget needs to be
|
|
repainted. After translating the context so that its (0, 0) coordinate
|
|
corresponds to the top left corner of the widget, it effectively calls
|
|
the widget's <function>gtk_widget_draw</function> function.
|
|
</para>
|
|
|
|
<para>
|
|
<function>gtk_widget_draw</function> takes care of drawing the widget
|
|
to the cairo context. It first checks that the widget actually needs to
|
|
be drawn. Widgets might for example be empty or outside of the cairo
|
|
context's clipped area, which would make drawing them not do anything.
|
|
Usually they will need to be drawn. In this case, the context will be
|
|
clipped to the widget's allocated size and the
|
|
<link linkend="GtkWidget::draw">draw signal</link> will be emitted on
|
|
the widget which will finally draw the widget.
|
|
</para>
|
|
</refsect2>
|
|
|
|
<refsect2 id="window-no-window-widgets">
|
|
<title>Window and no-window widgets</title>
|
|
|
|
<para>
|
|
In principle, each widget could have a
|
|
<classname>GdkWindow</classname> 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
|
|
<classname>GdkWindow</classname>, the GTK layer would simply
|
|
emit the #GtkWidget::draw signal for that widget. The signal
|
|
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.
|
|
</para>
|
|
|
|
<para>
|
|
However, in practice it is convenient to have widgets which do
|
|
not have a <classname>GdkWindow</classname> of their own, but
|
|
rather share the one from their parent widget. Such widgets
|
|
have called <function>gtk_widget_set_has_window</function> to
|
|
disable it; this can be tested easily with the <link
|
|
linkend="gtk-widget-get-has-window"><function>gtk_widget_get_has_window()</function></link>
|
|
function. As such, these are called <firstterm>no-window
|
|
widgets</firstterm>.
|
|
</para>
|
|
|
|
<para>
|
|
No-window widgets are useful for various reasons:
|
|
</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
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. <xref
|
|
linkend="figure-windowed-label"/> shows this undesirable effect.
|
|
</para>
|
|
|
|
<figure id="figure-windowed-label">
|
|
<title>Windowed label vs. no-window label</title>
|
|
|
|
<graphic fileref="figure-windowed-label.png" format="png"/>
|
|
</figure>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
Reducing the number of windows creates less traffic between GTK+ and
|
|
the underlying windowing system, especially when getting events.
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>
|
|
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.
|
|
</para>
|
|
</refsect2>
|
|
|
|
<refsect2 id="hierarchical-drawing">
|
|
<title>Hierarchical drawing</title>
|
|
|
|
<para>
|
|
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 <constant>GTK_NO_WINDOW</constant> flag turned
|
|
<emphasis>off</emphasis> (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
|
|
<constant>GTK_NO_WINDOW</constant> children to paint
|
|
themselves. This process is applied recursively for all the
|
|
<constant>GTK_NO_WINDOW</constant> descendants of the original
|
|
widget.
|
|
</para>
|
|
|
|
<para>
|
|
Note that this process does not get propagated to widgets
|
|
which have windows of their own, that is, to widgets which
|
|
have the <constant>GTK_NO_WINDOW</constant> 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
|
|
<firstterm>propagate</firstterm> the exposure to them on the
|
|
GTK+ side.
|
|
</para>
|
|
|
|
<para>
|
|
<xref
|
|
linkend="figure-hierarchical-drawing"/> shows how a simple toplevel window would
|
|
paint itself when it contains only <constant>GTK_NO_WINDOW</constant> descendants:
|
|
|
|
<orderedlist>
|
|
<listitem>
|
|
<para>
|
|
The outermost, thick rectangle is a toplevel <link
|
|
linkend="GtkWindow"><classname>GtkWindow</classname></link>,
|
|
which is not a <constant>GTK_NO_WINDOW</constant> widget —
|
|
as such, it does receive its exposure event as it comes from GDK.
|
|
First the <classname>GtkWindow</classname> would paint its own
|
|
background. Then, it would ask its only child to paint itself,
|
|
numbered 2.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
The dotted rectangle represents a <link
|
|
linkend="GtkVBox"><classname>GtkVBox</classname></link>, which
|
|
has been made the sole child of the
|
|
<classname>GtkWindow</classname>. Boxes are just layout
|
|
containers that do not paint anything by themselves, so this
|
|
<classname>GtkVBox</classname> would draw nothing, but rather ask
|
|
its children to draw themselves. The children are numbered 3 and
|
|
6.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
The thin rectangle is a <link
|
|
linkend="GtkFrame"><classname>GtkFrame</classname></link>,
|
|
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.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
The frame label has no children, so it just draws its text: "Frame Label".
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
The internal label has no children, so it just draws its text: "This
|
|
is some text inside the frame!".
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
The dotted rectangle represents a <link
|
|
linkend="GtkHBox"><classname>GtkHBox</classname></link>. Again,
|
|
this does not draw anything by itself, but rather asks its children
|
|
to draw themselves. The children are numbered 7 and 9.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
The thin rectangle is a <link
|
|
linkend="GtkButton"><classname>GtkButton</classname></link> with
|
|
a single child, numbered 8. First the button would draw its
|
|
beveled box, and then it would ask its child to draw itself.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
This is a text label which has no children, so it just draws its
|
|
own text: "Cancel".
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
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.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Similar to number 8, this is a text label which has no children,
|
|
so it just draws its own text: "OK".
|
|
</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
</para>
|
|
|
|
<figure id="figure-hierarchical-drawing">
|
|
<title>Hierarchical drawing order</title>
|
|
|
|
<graphic fileref="figure-hierarchical-drawing.png" format="png"/>
|
|
</figure>
|
|
</refsect2>
|
|
|
|
</refsect1>
|
|
|
|
<refsect1 id="double-buffering">
|
|
<title>Double buffering</title>
|
|
|
|
<para>
|
|
When the GTK layer receives an exposure event from GDK, it first finds
|
|
the <literal>!<constant>GTK_NO_WINDOW</constant></literal> widget that
|
|
corresponds to the event's window. Then, it emits the
|
|
#GtkWidget::draw signal for that
|
|
widget. As described above, that widget will first draw its background,
|
|
and then ask each of its <constant>GTK_NO_WINDOW</constant> children to
|
|
draw themselves.
|
|
</para>
|
|
|
|
<para>
|
|
If each of the drawing calls made by each subwidget's
|
|
<literal>draw</literal> 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 <firstterm>double
|
|
buffering</firstterm> 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.
|
|
</para>
|
|
|
|
<!-- FIXME: figure with a timeline of non-double-buffered and
|
|
double-buffered paints:
|
|
|
|
onscreen:
|
|
[garbage]
|
|
[background]
|
|
[button-frame]
|
|
[icon]
|
|
[label]
|
|
|
|
|
|
onscreen: offscreen:
|
|
[garbage]
|
|
[background]
|
|
[button-frame]
|
|
[icon]
|
|
[label]
|
|
[final result]
|
|
-->
|
|
|
|
<para>
|
|
Two basic functions in GDK form the core of the double-buffering
|
|
mechanism: <link
|
|
linkend="gdk_window_begin_paint_region"><function>gdk_window_begin_paint_region()</function></link>
|
|
and <link
|
|
linkend="gdk_window_end_paint"><function>gdk_window_end_paint()</function></link>.
|
|
The first function tells a <classname>GdkWindow</classname> 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.
|
|
</para>
|
|
|
|
<refsect2 id="automatic-double-buffering">
|
|
<title>Automatic double buffering</title>
|
|
|
|
<para>
|
|
It would be inconvenient for all widgets to call
|
|
<function>gdk_window_begin_paint_region()</function> and
|
|
<function>gdk_window_end_paint()</function> at the beginning
|
|
and end of their draw handlers.
|
|
</para>
|
|
|
|
<para>
|
|
To make this easier, most GTK+ widgets have the
|
|
<constant>GTK_DOUBLE_BUFFERED</constant> <link
|
|
linkend="GtkWidgetFlags">widget flag</link> turned on by
|
|
default. When GTK+ encounters such a widget, it automatically
|
|
calls <function>gdk_window_begin_paint_region()</function>
|
|
before emitting the #GtkWidget::draw signal for the widget, and
|
|
then it calls <function>gdk_window_end_paint()</function>
|
|
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.
|
|
</para>
|
|
|
|
<para>
|
|
However, some widgets may prefer to disable this kind of
|
|
automatic double buffering and do things on their own. To do
|
|
this, call the
|
|
<function>gtk_widget_set_double_buffered()</function> function
|
|
in your widget's constructor.
|
|
</para>
|
|
|
|
<example id="disabling-double-buffering">
|
|
<title>Disabling automatic double buffering</title>
|
|
|
|
<programlisting>
|
|
static void
|
|
my_widget_init (MyWidget *widget)
|
|
{
|
|
...
|
|
|
|
gtk_widget_set_double_buffered (widget, FALSE);
|
|
|
|
...
|
|
}
|
|
</programlisting>
|
|
</example>
|
|
|
|
<para>
|
|
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 <emphasis>not</emphasis> 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.
|
|
</para>
|
|
|
|
<para>
|
|
Even if you turn off double buffering on a widget, you
|
|
can still call
|
|
<function>gdk_window_begin_paint_region()</function> and
|
|
<function>gdk_window_end_paint()</function> by hand to use
|
|
temporary drawing buffers.
|
|
</para>
|
|
</refsect2>
|
|
</refsect1>
|
|
|
|
<refsect1 id="app-paintable-widgets">
|
|
<title>App-paintable widgets</title>
|
|
|
|
<para>
|
|
Generally, applications use the pre-defined widgets in GTK+ and
|
|
they do not draw extra things on top of them (the exception
|
|
being <classname>GtkDrawingArea</classname>). 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.
|
|
</para>
|
|
|
|
<para>
|
|
<classname>GtkWindow</classname> and
|
|
<classname>GtkEventBox</classname> are the two widgets that allow
|
|
turning off drawing of default contents by calling
|
|
<function>gtk_widget_set_app_paintable()</function>. If you call
|
|
this function, they will not draw their contents and let you do
|
|
it instead.
|
|
</para>
|
|
|
|
<para>
|
|
Since the #GtkWidget::draw signal runs user-connected handlers
|
|
<emphasis>before</emphasis> the widget's default handler, what
|
|
usually happens is this:
|
|
</para>
|
|
|
|
<orderedlist>
|
|
<listitem>
|
|
<para>
|
|
Your own draw handler gets run. It paints something
|
|
on the window or the event box.
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
The widget's default draw handler gets run. If
|
|
<function>gtk_widget_set_app_paintable()</function> has not
|
|
been called to turn off widget drawing (this
|
|
is the default), <emphasis>your drawing will be
|
|
overwritten</emphasis>. An app paintable widget will not
|
|
draw its default contents however and preserve your drawing
|
|
instead.
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
The draw handler for the parent class gets run.
|
|
Since both <classname>GtkWindow</classname> and
|
|
<classname>GtkEventBox</classname> are descendants of
|
|
<classname>GtkContainer</classname>, their no-window
|
|
children will be asked to draw themselves recursively, as
|
|
described in <xref linkend="hierarchical-drawing"/>.
|
|
</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
|
|
<formalpara>
|
|
<title>Summary of app-paintable widgets</title>
|
|
|
|
<para>
|
|
Call <function>gtk_widget_set_app_paintable()</function> if you
|
|
intend to draw your own content directly on a
|
|
<classname>GtkWindow</classname> and
|
|
<classname>GtkEventBox</classname>. You seldom need to draw
|
|
on top of other widgets, and
|
|
<classname>GtkDrawingArea</classname> ignores this flag, as it
|
|
<emphasis>is</emphasis> intended to be drawn on.
|
|
</para>
|
|
</formalpara>
|
|
</refsect1>
|
|
</refentry>
|
|
|
|
<!--
|
|
Local variables:
|
|
mode: xml
|
|
sgml-parent-document: ("gtk-docs.sgml" "book" "part" "refentry")
|
|
End:
|
|
-->
|