From 80e1340e51855f9fee469bc8dac95abdb7c56da4 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 18 Jan 2011 23:57:17 -0500 Subject: [PATCH] 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 deletion(-) create mode 100644 docs/reference/gtk/images/drawing.png create mode 100644 examples/drawing.c diff --git a/docs/reference/gtk/Makefile.am b/docs/reference/gtk/Makefile.am index 74520fc2bf..091f06d7b7 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 aebc5ee42f..d47a09f1fd 100644 --- a/docs/reference/gtk/getting_started.xml +++ b/docs/reference/gtk/getting_started.xml @@ -147,4 +147,41 @@ +
+ Drawing + + 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. + + + 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. + + 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. + + + + + + + Drawing in response to input + + + FIXME: MISSING XINCLUDE CONTENT + + + +
diff --git a/docs/reference/gtk/images/drawing.png b/docs/reference/gtk/images/drawing.png new file mode 100644 index 0000000000000000000000000000000000000000..a5105002ef29cec87fb501edfb68e19c36a7425c GIT binary patch literal 3686 zcmb7HXHb(}uzo{m(gY!tPz<39NRcKrbm{pN0V$DAKtW3A0-+bF(iNmgN1D=m2oR7e zAXPfZhoDpmNb&Ohx_|DUduGnrv$H$T?(FO{vm1*v)TX6^QUL%!tMfqP;kCqG-!w|{ z>(|1cTkBeoda3CcQ(m8!l=d;#IvYmQ4D;xPBgWUp(*baDd*SLJ>SgEY;Na$keu3Eo zcPU?ko$6?)8vEt^%=LF?ddx-WQc8`;9@c-I{f&U&<>r8I!Na=4gy=O)Le-d)?=Uxs zm{L8f9B*hmPP)V|idxp42ok&J-@C+SY{tpza#Pwv(lwbv;Rqxzf>Ot^heM}CVDg+W ze2n4u!VoR@ikC}ubsSM4%R9vLlfBE<(n&lwxt^?waBo1asw~CTB?rr;`McxH%_L

Yi&fe2mfzUiwvmC@T3Si# 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 MrlCf)nswO!0N{+Y8~^|S literal 0 HcmV?d00001 diff --git a/examples/Makefile.am b/examples/Makefile.am index f4d06bfd41..948d58a213 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 0000000000..28f291b8b8 --- /dev/null +++ b/examples/drawing.c @@ -0,0 +1,200 @@ +#include + +/* 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; +}