diff options
author | Matthias Clasen <mclasen@redhat.com> | 2011-01-18 23:57:17 -0500 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2011-01-18 23:59:17 -0500 |
commit | 80e1340e51855f9fee469bc8dac95abdb7c56da4 (patch) | |
tree | 0d8b8ced51ca154b4feba176b8121e1d5faae4c6 /examples/drawing.c | |
parent | ceeaf183a11f2abfc487d6381718a2707e65f41d (diff) | |
download | gtk+-80e1340e51855f9fee469bc8dac95abdb7c56da4.tar.gz |
Add a drawing example to the tutorial
Diffstat (limited to 'examples/drawing.c')
-rw-r--r-- | examples/drawing.c | 200 |
1 files changed, 200 insertions, 0 deletions
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 <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; +} |