drawingarea.inc 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. (* Drawing Area
  2. *
  3. * GtkDrawingArea is a blank area where you can draw custom displays
  4. * of various kinds.
  5. *
  6. * This demo has two drawing areas. The checkerboard area shows
  7. * how you can just draw something; all you have to do is write
  8. * a signal handler for expose_event, as shown here.
  9. *
  10. * The "scribble" area is a bit more advanced, and shows how to handle
  11. * events such as button presses and mouse motion. Click the mouse
  12. * and drag in the scribble area to draw squiggles. Resize the window
  13. * to clear the area.
  14. *)
  15. var
  16. da_window : PGtkWidget;
  17. (* Pixmap for scribble area, to store current scribbles *)
  18. da_pixmap : PGdkPixmap;
  19. (* Create a new pixmap of the appropriate size to store our scribbles *)
  20. function scribble_configure_event (widget : PGtkWidget;
  21. event : PGdkEventConfigure;
  22. data : gpointer): gboolean; cdecl;
  23. begin
  24. if da_pixmap <> NULL then
  25. g_object_unref (G_OBJECT (da_pixmap));
  26. da_pixmap := gdk_pixmap_new (widget^.window,
  27. widget^.allocation.width,
  28. widget^.allocation.height,
  29. -1);
  30. (* Initialize the pixmap to white *)
  31. gdk_draw_rectangle (da_pixmap,
  32. widget^.style^.white_gc,
  33. gTRUE,
  34. 0, 0,
  35. widget^.allocation.width,
  36. widget^.allocation.height);
  37. (* We've handled the configure event, no need for further processing. *)
  38. scribble_configure_event := TRUE;
  39. end;
  40. (* Redraw the screen from the pixmap *)
  41. function scribble_expose_event (widget : PGtkWidget;
  42. event : PGdkEventExpose;
  43. data : gpointer): gboolean; cdecl;
  44. begin
  45. (* We use the "foreground GC" for the widget since it already exists,
  46. * but honestly any GC would work. The only thing to worry about
  47. * is whether the GC has an inappropriate clip region set.
  48. *)
  49. gdk_draw_drawable (widget^.window,
  50. widget^.style^.fg_gc[GTK_WIDGET_STATE (widget)],
  51. da_pixmap,
  52. (* Only copy the area that was exposed. *)
  53. event^.area.x, event^.area.y,
  54. event^.area.x, event^.area.y,
  55. event^.area.width, event^.area.height);
  56. scribble_expose_event := FALSE;
  57. end;
  58. (* Draw a rectangle on the screen *)
  59. procedure draw_brush (widget : PGtkWidget;
  60. x, y : gdouble);
  61. var
  62. update_rect : TGdkRectangle;
  63. begin
  64. update_rect.x := round (x - 3);
  65. update_rect.y := round (y - 3);
  66. update_rect.width := 6;
  67. update_rect.height := 6;
  68. (* Paint to the pixmap, where we store our state *)
  69. gdk_draw_rectangle (da_pixmap,
  70. widget^.style^.black_gc,
  71. gTRUE,
  72. update_rect.x, update_rect.y,
  73. update_rect.width, update_rect.height);
  74. (* Now invalidate the affected region of the drawing area. *)
  75. gdk_window_invalidate_rect (widget^.window,
  76. @update_rect,
  77. FALSE);
  78. end;
  79. function scribble_button_press_event (widget : PGtkWidget;
  80. event : PGdkEventButton;
  81. data : gpointer): gboolean; cdecl;
  82. begin
  83. if da_pixmap = NULL then
  84. exit (FALSE); (* paranoia check, in case we haven't gotten a configure event *)
  85. if event^.button = 1 then
  86. draw_brush (widget, event^.x, event^.y);
  87. (* We've handled the event, stop processing *)
  88. exit (TRUE);
  89. end;
  90. function scribble_motion_notify_event (widget : PGtkWidget;
  91. event : PGdkEventButton;
  92. data : gpointer): gboolean; cdecl;
  93. var
  94. x, y : gint;
  95. state : TGdkModifierType;
  96. begin
  97. if da_pixmap = NULL then
  98. exit (FALSE); (* paranoia check, in case we haven't gotten a configure event *)
  99. (* This call is very important; it requests the next motion event.
  100. * If you don't call gdk_window_get_pointer() you'll only get
  101. * a single motion event. The reason is that we specified
  102. * GDK_POINTER_MOTION_HINT_MASK to gtk_widget_set_events().
  103. * If we hadn't specified that, we could just use event->x, event->y
  104. * as the pointer location. But we'd also get deluged in events.
  105. * By requesting the next event as we handle the current one,
  106. * we avoid getting a huge number of events faster than we
  107. * can cope.
  108. *)
  109. gdk_window_get_pointer (event^.window, @x, @y, @state);
  110. if (state and GDK_BUTTON1_MASK) <> 0 then
  111. draw_brush (widget, x, y);
  112. (* We've handled it, stop processing *)
  113. exit (TRUE);
  114. end;
  115. const
  116. CHECK_SIZE = 10;
  117. SPACING = 2;
  118. function checkerboard_expose (da : PGtkWidget;
  119. event : PGdkEventButton;
  120. data : gpointer): gboolean; cdecl;
  121. var
  122. i, j,
  123. xcount, ycount : gint;
  124. gc1, gc2, gc : PGdkGc;
  125. color : TGdkColor;
  126. begin
  127. (* At the start of an expose handler, a clip region of event->area
  128. * is set on the window, and event->area has been cleared to the
  129. * widget's background color. The docs for
  130. * gdk_window_begin_paint_region() give more details on how this
  131. * works.
  132. *)
  133. (* It would be a bit more efficient to keep these
  134. * GC's around instead of recreating on each expose, but
  135. * this is the lazy/slow way.
  136. *)
  137. gc1 := gdk_gc_new (da^.window);
  138. color.red := $7530;
  139. color.green := $0;
  140. color.blue := $7530;
  141. gdk_gc_set_rgb_fg_color (gc1, @color);
  142. gc2 := gdk_gc_new (da^.window);
  143. color.red := $ffff;
  144. color.green := $ffff;
  145. color.blue := $ffff;
  146. gdk_gc_set_rgb_fg_color (gc2, @color);
  147. xcount := 0;
  148. i := SPACING;
  149. while i < da^.allocation.width do
  150. begin
  151. j := SPACING;
  152. ycount := xcount mod 2; (* start with even/odd depending on row *)
  153. while j < da^.allocation.height do
  154. begin
  155. if (ycount mod 2) <> 0 then
  156. gc := gc1
  157. else
  158. gc := gc2;
  159. (* If we're outside event->area, this will do nothing.
  160. * It might be mildly more efficient if we handled
  161. * the clipping ourselves, but again we're feeling lazy.
  162. *)
  163. gdk_draw_rectangle (da^.window,
  164. gc,
  165. gTRUE,
  166. i, j,
  167. CHECK_SIZE,
  168. CHECK_SIZE);
  169. j := j + CHECK_SIZE + SPACING;
  170. inc (ycount);
  171. end;
  172. i := i + CHECK_SIZE + SPACING;
  173. inc (xcount);
  174. end;
  175. g_object_unref (G_OBJECT (gc1));
  176. g_object_unref (G_OBJECT (gc2));
  177. (* return TRUE because we've handled this event, so no
  178. * further processing is required.
  179. *)
  180. checkerboard_expose := TRUE;
  181. end;
  182. function do_drawingarea : PGtkWidget;
  183. var
  184. frame,
  185. vbox,
  186. da,
  187. thelabel : PGtkWidget;
  188. begin
  189. if da_window = NULL then
  190. begin
  191. da_window := gtk_window_new (GTK_WINDOW_TOPLEVEL);
  192. gtk_window_set_title (GTK_WINDOW (da_window), 'Drawing Area');
  193. g_signal_connect (da_window, 'destroy', G_CALLBACK (@gtk_widget_destroyed), @da_window);
  194. gtk_container_set_border_width (GTK_CONTAINER (da_window), 8);
  195. vbox := gtk_vbox_new (FALSE, 8);
  196. gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
  197. gtk_container_add (GTK_CONTAINER (da_window), vbox);
  198. (*
  199. * Create the checkerboard area
  200. *)
  201. thelabel := gtk_label_new (NULL);
  202. gtk_label_set_markup (GTK_LABEL (thelabel),
  203. '<u>Checkerboard pattern</u>');
  204. gtk_box_pack_start (GTK_BOX (vbox), thelabel, FALSE, FALSE, 0);
  205. frame := gtk_frame_new (NULL);
  206. gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  207. gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
  208. da := gtk_drawing_area_new ();
  209. (* set a minimum size *)
  210. gtk_widget_set_size_request (da, 100, 100);
  211. gtk_container_add (GTK_CONTAINER (frame), da);
  212. g_signal_connect (da, 'expose_event',
  213. G_CALLBACK (@checkerboard_expose), NULL);
  214. (*
  215. * Create the scribble area
  216. *)
  217. thelabel := gtk_label_new (NULL);
  218. gtk_label_set_markup (GTK_LABEL (thelabel),
  219. '<u>Scribble area</u>');
  220. gtk_box_pack_start (GTK_BOX (vbox), thelabel, FALSE, FALSE, 0);
  221. frame := gtk_frame_new (NULL);
  222. gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  223. gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
  224. da := gtk_drawing_area_new ();
  225. (* set a minimum size *)
  226. gtk_widget_set_size_request (da, 100, 100);
  227. gtk_container_add (GTK_CONTAINER (frame), da);
  228. (* Signals used to handle backing pixmap *)
  229. g_signal_connect (da, 'expose_event',
  230. G_CALLBACK (@scribble_expose_event), NULL);
  231. g_signal_connect (da,'configure_event',
  232. G_CALLBACK (@scribble_configure_event), NULL);
  233. (* Event signals *)
  234. g_signal_connect (da, 'motion_notify_event',
  235. G_CALLBACK (@scribble_motion_notify_event), NULL);
  236. g_signal_connect (da, 'button_press_event',
  237. G_CALLBACK (@scribble_button_press_event), NULL);
  238. (* Ask to receive events the drawing area doesn't normally
  239. * subscribe to
  240. *)
  241. gtk_widget_set_events (da, gtk_widget_get_events (da)
  242. or GDK_LEAVE_NOTIFY_MASK
  243. or GDK_BUTTON_PRESS_MASK
  244. or GDK_POINTER_MOTION_MASK
  245. or GDK_POINTER_MOTION_HINT_MASK);
  246. end;
  247. if not GTK_WIDGET_VISIBLE (da_window) then
  248. gtk_widget_show_all (da_window)
  249. else begin
  250. gtk_widget_destroy (da_window);
  251. da_window := NULL;
  252. end;
  253. do_drawingarea := da_window;
  254. end;