[gtk+] Add a drawing example to the tutorial



commit 80e1340e51855f9fee469bc8dac95abdb7c56da4
Author: Matthias Clasen <mclasen redhat com>
Date:   Tue Jan 18 23:57:17 2011 -0500

    Add a drawing example to the tutorial

 docs/reference/gtk/Makefile.am         |    2 +
 docs/reference/gtk/getting_started.xml |   37 ++++++
 docs/reference/gtk/images/drawing.png  |  Bin 0 -> 3686 bytes
 examples/Makefile.am                   |    7 +-
 examples/drawing.c                     |  200 ++++++++++++++++++++++++++++++++
 5 files changed, 245 insertions(+), 1 deletions(-)
---
diff --git a/docs/reference/gtk/Makefile.am b/docs/reference/gtk/Makefile.am
index 74520fc..091f06d 100644
--- a/docs/reference/gtk/Makefile.am
+++ b/docs/reference/gtk/Makefile.am
@@ -328,6 +328,8 @@ HTML_IMAGES = \
 	$(srcdir)/images/layout-tbrl.png				\
 	$(srcdir)/images/window-default.png				\
 	$(srcdir)/images/hello-world.png				\
+	$(srcdir)/images/grid-packing.png				\
+	$(srcdir)/images/drawing.png					\
 	$(srcdir)/images/switch.png					\
 	$(srcdir)/images/linear.png					\
 	$(srcdir)/images/ease.png					\
diff --git a/docs/reference/gtk/getting_started.xml b/docs/reference/gtk/getting_started.xml
index aebc5ee..d47a09f 100644
--- a/docs/reference/gtk/getting_started.xml
+++ b/docs/reference/gtk/getting_started.xml
@@ -147,4 +147,41 @@
     </example>
   </section>
 
+  <section>
+    <title>Drawing</title>
+
+    <para>Many widgets, like buttons, do all their drawing themselves. You
+    just tell them the label you want to see, and they figure out what font
+    to use, draw the button outline and focus rectangle, etc. Sometimes, it
+    is necessary to do some custom drawing. In that case, a #GtkDrawingArea
+    might be the right widget to use. It offers a canvas on which you can
+    draw by connecting to the #GtkWidget::draw signal.
+    </para>
+
+    <para>The contents of a widget often need to be partially or fully redrawn,
+    e.g. when another window is moved and uncovers part of the widget, or
+    when tie window containing it is resized. It is also possible to explicitly
+    cause part or all of the widget to be redrawn, by calling
+    gtk_widget_queue_draw() or its variants. GTK+ takes care of most of the
+    details by providing a ready-to-use cairo context to the ::draw signal
+    handler.</para>
+
+    <para>The following example shows a ::draw signal handler. It is a bit
+    more complicated than the previous examples, since it also demonstrates
+    input event handling by means of ::button-press and ::motion-notify
+    handlers.</para>
+
+    <para>
+      <inlinegraphic fileref="drawing.png" format="PNG"></inlinegraphic>
+    </para>
+
+    <example id="gtk-getting-started-drawing">
+      <title>Drawing in response to input</title>
+      <programlisting>
+        <xi:include href="../../../../examples/drawing.c" parse="text">
+          <xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback>
+        </xi:include>
+      </programlisting>
+    </example>
+  </section>
 </chapter>
diff --git a/docs/reference/gtk/images/drawing.png b/docs/reference/gtk/images/drawing.png
new file mode 100644
index 0000000..a510500
Binary files /dev/null and b/docs/reference/gtk/images/drawing.png differ
diff --git a/examples/Makefile.am b/examples/Makefile.am
index f4d06bf..948d58a 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -48,4 +48,9 @@ LDADD = \
 	$(top_builddir)/gtk/libgtk-3.0.la	\
 	$(GTK_DEP_LIBS)
 
-noinst_PROGRAMS = hello-world window-default bloatpad grid-packing
+noinst_PROGRAMS = \
+	hello-world				\
+	window-default				\
+	bloatpad				\
+	grid-packing				\
+	drawing
diff --git a/examples/drawing.c b/examples/drawing.c
new file mode 100644
index 0000000..28f291b
--- /dev/null
+++ b/examples/drawing.c
@@ -0,0 +1,200 @@
+#include <gtk/gtk.h>
+
+/* Surface to store current scribbles */
+static cairo_surface_t *surface = NULL;
+
+static void
+clear_surface (void)
+{
+  cairo_t *cr;
+
+  cr = cairo_create (surface);
+
+  cairo_set_source_rgb (cr, 1, 1, 1);
+  cairo_paint (cr);
+
+  cairo_destroy (cr);
+}
+
+/* Create a new surface of the appropriate size to store our scribbles */
+static gboolean
+configure_event_cb (GtkWidget         *widget,
+                    GdkEventConfigure *event,
+                    gpointer           data)
+{
+  if (surface)
+    cairo_surface_destroy (surface);
+
+  surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget),
+                                               CAIRO_CONTENT_COLOR,
+                                               gtk_widget_get_allocated_width (widget),
+                                               gtk_widget_get_allocated_height (widget));
+
+  /* Initialize the surface to white */
+  clear_surface ();
+
+  /* We've handled the configure event, no need for further processing. */
+  return TRUE;
+}
+
+/* Redraw the screen from the surface. Note that the ::draw
+ * signal receives a ready-to-be-used cairo_t that is already
+ * clipped to only draw the exposed areas of the widget
+ */
+static gboolean
+draw_cb (GtkWidget *widget,
+         cairo_t   *cr,
+         gpointer   data)
+{
+  cairo_set_source_surface (cr, surface, 0, 0);
+  cairo_paint (cr);
+
+  return FALSE;
+}
+
+/* Draw a rectangle on the surface at the given position */
+static void
+draw_brush (GtkWidget *widget,
+            gdouble    x,
+            gdouble    y)
+{
+  cairo_t *cr;
+
+  /* Paint to the surface, where we store our state */
+  cr = cairo_create (surface);
+
+  cairo_rectangle (cr, x - 3, y - 3, 6, 6);
+  cairo_fill (cr);
+
+  cairo_destroy (cr);
+
+  /* Now invalidate the affected region of the drawing area. */
+  gtk_widget_queue_draw_area (widget, x - 3, y - 3, 6, 6);
+}
+
+/* Handle button press events by either drawing a rectangle
+ * or clearing the surface, depending on which button was pressed.
+ * The ::button-press signal handler receives a GdkEventButton
+ * struct which contains this information.
+ */
+static gboolean
+button_press_event_cb (GtkWidget      *widget,
+                       GdkEventButton *event,
+                       gpointer        data)
+{
+  /* paranoia check, in case we haven't gotten a configure event */
+  if (surface == NULL)
+    return FALSE;
+
+  if (event->button == 1)
+    {
+      draw_brush (widget, event->x, event->y);
+    }
+  else if (event->button == 3)
+    {
+      clear_surface ();
+      gtk_widget_queue_draw (widget);
+    }
+
+  /* We've handled the event, stop processing */
+  return TRUE;
+}
+
+/* Handle motion events by continuing to draw if button 1 is
+ * still held down. The ::motion-notify signal handler receives
+ * a GdkEventMotion struct which contains this information.
+ */
+static gboolean
+motion_notify_event_cb (GtkWidget      *widget,
+                        GdkEventMotion *event,
+                        gpointer        data)
+{
+  int x, y;
+  GdkModifierType state;
+
+  /* paranoia check, in case we haven't gotten a configure event */
+  if (surface == NULL)
+    return FALSE;
+
+  /* This call is very important; it requests the next motion event.
+   * If you don't call gdk_window_get_pointer() you'll only get
+   * a single motion event. The reason is that we specified
+   * GDK_POINTER_MOTION_HINT_MASK to gtk_widget_set_events().
+   * If we hadn't specified that, we could just use event->x, event->y
+   * as the pointer location. But we'd also get deluged in events.
+   * By requesting the next event as we handle the current one,
+   * we avoid getting a huge number of events faster than we
+   * can cope.
+   */
+  gdk_window_get_pointer (event->window, &x, &y, &state);
+
+  if (state & GDK_BUTTON1_MASK)
+    draw_brush (widget, x, y);
+
+  /* We've handled it, stop processing */
+  return TRUE;
+}
+
+static void
+close_window (void)
+{
+  if (surface)
+    cairo_surface_destroy (surface);
+
+  gtk_main_quit ();
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  GtkWidget *window;
+  GtkWidget *frame;
+  GtkWidget *da;
+
+  gtk_init (&argc, &argv);
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_title (GTK_WINDOW (window), "Drawing Area");
+
+  g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);
+
+  gtk_container_set_border_width (GTK_CONTAINER (window), 8);
+
+  frame = gtk_frame_new (NULL);
+  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+  gtk_container_add (GTK_CONTAINER (window), frame);
+
+  da = gtk_drawing_area_new ();
+  /* set a minimum size */
+  gtk_widget_set_size_request (da, 100, 100);
+
+  gtk_container_add (GTK_CONTAINER (frame), da);
+
+  /* Signals used to handle the backing surface */
+  g_signal_connect (da, "draw",
+                    G_CALLBACK (draw_cb), NULL);
+  g_signal_connect (da,"configure-event",
+                    G_CALLBACK (configure_event_cb), NULL);
+
+  /* Event signals */
+  g_signal_connect (da, "motion-notify-event",
+                    G_CALLBACK (motion_notify_event_cb), NULL);
+  g_signal_connect (da, "button-press-event",
+                    G_CALLBACK (button_press_event_cb), NULL);
+
+  /* Ask to receive events the drawing area doesn't normally
+   * subscribe to. In particular, we need to ask for the
+   * button press and motion notify events that want to handle.
+   */
+  gtk_widget_set_events (da, gtk_widget_get_events (da)
+                             | GDK_BUTTON_PRESS_MASK
+                             | GDK_POINTER_MOTION_MASK
+                             | GDK_POINTER_MOTION_HINT_MASK);
+
+  gtk_widget_show_all (window);
+
+  gtk_main ();
+
+  return 0;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]