1 | #include <gtk/gtk.h> |
2 | |
3 | /* Surface to store current scribbles */ |
4 | static cairo_surface_t *surface = NULL; |
5 | |
6 | static void |
7 | clear_surface (void) |
8 | { |
9 | cairo_t *cr; |
10 | |
11 | cr = cairo_create (target: surface); |
12 | |
13 | cairo_set_source_rgb (cr, red: 1, green: 1, blue: 1); |
14 | cairo_paint (cr); |
15 | |
16 | cairo_destroy (cr); |
17 | } |
18 | |
19 | /* Create a new surface of the appropriate size to store our scribbles */ |
20 | static void |
21 | resize_cb (GtkWidget *widget, |
22 | int width, |
23 | int height, |
24 | gpointer data) |
25 | { |
26 | if (surface) |
27 | { |
28 | cairo_surface_destroy (surface); |
29 | surface = NULL; |
30 | } |
31 | |
32 | if (gtk_native_get_surface (self: gtk_widget_get_native (widget))) |
33 | { |
34 | surface = gdk_surface_create_similar_surface (surface: gtk_native_get_surface (self: gtk_widget_get_native (widget)), |
35 | content: CAIRO_CONTENT_COLOR, |
36 | width: gtk_widget_get_width (widget), |
37 | height: gtk_widget_get_height (widget)); |
38 | |
39 | /* Initialize the surface to white */ |
40 | clear_surface (); |
41 | } |
42 | } |
43 | |
44 | /* Redraw the screen from the surface. Note that the draw |
45 | * callback receives a ready-to-be-used cairo_t that is already |
46 | * clipped to only draw the exposed areas of the widget |
47 | */ |
48 | static void |
49 | draw_cb (GtkDrawingArea *drawing_area, |
50 | cairo_t *cr, |
51 | int width, |
52 | int height, |
53 | gpointer data) |
54 | { |
55 | cairo_set_source_surface (cr, surface, x: 0, y: 0); |
56 | cairo_paint (cr); |
57 | } |
58 | |
59 | /* Draw a rectangle on the surface at the given position */ |
60 | static void |
61 | draw_brush (GtkWidget *widget, |
62 | double x, |
63 | double y) |
64 | { |
65 | cairo_t *cr; |
66 | |
67 | /* Paint to the surface, where we store our state */ |
68 | cr = cairo_create (target: surface); |
69 | |
70 | cairo_rectangle (cr, x: x - 3, y: y - 3, width: 6, height: 6); |
71 | cairo_fill (cr); |
72 | |
73 | cairo_destroy (cr); |
74 | |
75 | /* Now invalidate the drawing area. */ |
76 | gtk_widget_queue_draw (widget); |
77 | } |
78 | |
79 | static double start_x; |
80 | static double start_y; |
81 | |
82 | static void |
83 | drag_begin (GtkGestureDrag *gesture, |
84 | double x, |
85 | double y, |
86 | GtkWidget *area) |
87 | { |
88 | start_x = x; |
89 | start_y = y; |
90 | |
91 | draw_brush (widget: area, x, y); |
92 | } |
93 | |
94 | static void |
95 | drag_update (GtkGestureDrag *gesture, |
96 | double x, |
97 | double y, |
98 | GtkWidget *area) |
99 | { |
100 | draw_brush (widget: area, x: start_x + x, y: start_y + y); |
101 | } |
102 | |
103 | static void |
104 | drag_end (GtkGestureDrag *gesture, |
105 | double x, |
106 | double y, |
107 | GtkWidget *area) |
108 | { |
109 | draw_brush (widget: area, x: start_x + x, y: start_y + y); |
110 | } |
111 | |
112 | static void |
113 | pressed (GtkGestureClick *gesture, |
114 | int n_press, |
115 | double x, |
116 | double y, |
117 | GtkWidget *area) |
118 | { |
119 | clear_surface (); |
120 | gtk_widget_queue_draw (widget: area); |
121 | } |
122 | |
123 | static void |
124 | close_window (void) |
125 | { |
126 | if (surface) |
127 | cairo_surface_destroy (surface); |
128 | } |
129 | |
130 | static void |
131 | activate (GtkApplication *app, |
132 | gpointer user_data) |
133 | { |
134 | GtkWidget *window; |
135 | GtkWidget *drawing_area; |
136 | GtkGesture *drag; |
137 | GtkGesture *press; |
138 | |
139 | window = gtk_application_window_new (application: app); |
140 | gtk_window_set_title (GTK_WINDOW (window), title: "Drawing Area" ); |
141 | |
142 | g_signal_connect (window, "destroy" , G_CALLBACK (close_window), NULL); |
143 | |
144 | drawing_area = gtk_drawing_area_new (); |
145 | /* set a minimum size */ |
146 | gtk_widget_set_size_request (widget: drawing_area, width: 100, height: 100); |
147 | |
148 | gtk_window_set_child (GTK_WINDOW (window), child: drawing_area); |
149 | |
150 | gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (drawing_area), draw_func: draw_cb, NULL, NULL); |
151 | |
152 | g_signal_connect_after (drawing_area, "resize" , G_CALLBACK (resize_cb), NULL); |
153 | |
154 | drag = gtk_gesture_drag_new (); |
155 | gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (drag), GDK_BUTTON_PRIMARY); |
156 | gtk_widget_add_controller (widget: drawing_area, GTK_EVENT_CONTROLLER (drag)); |
157 | g_signal_connect (drag, "drag-begin" , G_CALLBACK (drag_begin), drawing_area); |
158 | g_signal_connect (drag, "drag-update" , G_CALLBACK (drag_update), drawing_area); |
159 | g_signal_connect (drag, "drag-end" , G_CALLBACK (drag_end), drawing_area); |
160 | |
161 | press = gtk_gesture_click_new (); |
162 | gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (press), GDK_BUTTON_SECONDARY); |
163 | gtk_widget_add_controller (widget: drawing_area, GTK_EVENT_CONTROLLER (press)); |
164 | |
165 | g_signal_connect (press, "pressed" , G_CALLBACK (pressed), drawing_area); |
166 | |
167 | gtk_widget_show (widget: window); |
168 | } |
169 | |
170 | int |
171 | main (int argc, |
172 | char **argv) |
173 | { |
174 | GtkApplication *app; |
175 | int status; |
176 | |
177 | app = gtk_application_new (application_id: "org.gtk.example" , flags: G_APPLICATION_FLAGS_NONE); |
178 | g_signal_connect (app, "activate" , G_CALLBACK (activate), NULL); |
179 | status = g_application_run (G_APPLICATION (app), argc, argv); |
180 | g_object_unref (object: app); |
181 | |
182 | return status; |
183 | } |
184 | |