drawing.c 4.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#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 */
20 21 22 23 24 25
static void
size_allocate_cb (GtkWidget     *widget,
                  GtkAllocation *alloc,
                  int            baseline,
                  GdkRectangle  *clip,
                  gpointer       data)
26 27
{
  if (surface)
28 29 30 31
    {
      cairo_surface_destroy (surface);
      surface = NULL;
    }
32

33 34 35 36 37 38
  if (gtk_widget_get_window (widget))
    {
      surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget),
                                                   CAIRO_CONTENT_COLOR,
                                                   gtk_widget_get_width (widget),
                                                   gtk_widget_get_height (widget));
39

40 41 42
      /* Initialize the surface to white */
      clear_surface ();
    }
43 44
}

45 46
/* Redraw the screen from the surface. Note that the draw
 * callback receives a ready-to-be-used cairo_t that is already
47 48
 * clipped to only draw the exposed areas of the widget
 */
49 50 51 52 53 54
static void
draw_cb (GtkDrawingArea *drawing_area,
         cairo_t        *cr,
         int             width,
         int             height,
         gpointer        data)
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
{
  cairo_set_source_surface (cr, surface, 0, 0);
  cairo_paint (cr);
}

/* 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);
}

80 81
static double start_x;
static double start_y;
82

83 84 85 86 87 88 89 90
static void
drag_begin (GtkGestureDrag *gesture,
            double          x,
            double          y,
            GtkWidget      *area)
{
  start_x = x;
  start_y = y;
91

92
  draw_brush (area, x, y);
93 94
}

95 96 97 98 99
static void
drag_update (GtkGestureDrag *gesture,
             double          x,
             double          y,
             GtkWidget      *area)
100
{
101 102
  draw_brush (area, start_x + x, start_y + y);
}
Matthias Clasen's avatar
Matthias Clasen committed
103

104 105 106 107 108 109 110 111
static void
drag_end (GtkGestureDrag *gesture,
          double          x,
          double          y,
          GtkWidget      *area)
{
  draw_brush (area, start_x + x, start_y + y);
}
112

113 114 115 116 117 118 119 120 121
static void
pressed (GtkGestureMultiPress *gesture,
         int                   n_press,
         double                x,
         double                y,
         GtkWidget            *area)
{
  clear_surface ();
  gtk_widget_queue_draw (area);
122 123 124 125 126 127 128 129 130
}

static void
close_window (void)
{
  if (surface)
    cairo_surface_destroy (surface);
}

131 132 133
static void
activate (GtkApplication *app,
          gpointer        user_data)
134 135 136
{
  GtkWidget *window;
  GtkWidget *frame;
137
  GtkWidget *drawing_area;
138 139
  GtkGesture *drag;
  GtkGesture *press;
140

141
  window = gtk_application_window_new (app);
142 143 144 145 146 147 148 149
  gtk_window_set_title (GTK_WINDOW (window), "Drawing Area");

  g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);

  frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  gtk_container_add (GTK_CONTAINER (window), frame);

150
  drawing_area = gtk_drawing_area_new ();
151
  /* set a minimum size */
152
  gtk_widget_set_size_request (drawing_area, 100, 100);
153

154
  gtk_container_add (GTK_CONTAINER (frame), drawing_area);
155

156 157 158 159
  gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (drawing_area), draw_cb, NULL, NULL);

  g_signal_connect_after (drawing_area, "size-allocate",
                          G_CALLBACK (size_allocate_cb), NULL);
160

161 162 163 164 165 166
  drag = gtk_gesture_drag_new (drawing_area);
  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (drag), GDK_BUTTON_PRIMARY);
  g_object_set_data_full (G_OBJECT (drawing_area), "drag", drag, g_object_unref);

  g_signal_connect (drag, "drag-begin", G_CALLBACK (drag_begin), drawing_area);
  g_signal_connect (drag, "drag-update", G_CALLBACK (drag_update), drawing_area);
167
  g_signal_connect (drag, "drag-end", G_CALLBACK (drag_end), drawing_area);
168 169 170 171 172 173

  press = gtk_gesture_multi_press_new (drawing_area);
  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (press), GDK_BUTTON_SECONDARY);
  g_object_set_data_full (G_OBJECT (drawing_area), "press", press, g_object_unref);

  g_signal_connect (press, "pressed", G_CALLBACK (pressed), drawing_area);
174

Timm Bäder's avatar
Timm Bäder committed
175
  gtk_widget_show (window);
176 177 178 179 180 181 182 183
}

int
main (int    argc,
      char **argv)
{
  GtkApplication *app;
  int status;
184

185 186 187 188
  app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
  g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
  status = g_application_run (G_APPLICATION (app), argc, argv);
  g_object_unref (app);
189

190
  return status;
191
}